Tutorial: Cubit+Abaqus#
This tutorial implements the same workflow introduced in Tutorial 04: Simulation, but uses Cubit [62] for the geometry, partition, and meshing tasks. It is included as an example for how to use Cubit with a build system and include best practices, such as an API and CLI.
Note
Depending on your Cubit installation and operating system, it may not be
possible to import Cubit into arbitrary Python 3 environments. Instead, it
may be necessary to execute Cubit Python scripts using the Python
interpreter bundled with Cubit. An alternate Cubit tutorial using
waves.scons_extensions.add_cubit_python()
is provided as
tutorial_cubit_alternate
$ pwd
/home/roppenheimer/waves-tutorials
$ waves fetch --destination tutorial_cubit_alternate tutorials/tutorial_cubit_alternate
References#
SCons Appendix D:
AddPostAction
[32]GNU sed and conda-forge sed [63]
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
PS > conda create --name waves-tutorial-env --channel conda-forge waves scons matplotlib pandas pyyaml xarray seaborn numpy salib pytest
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#
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
Create a new
tutorial_cubit
directory with thewaves fetch
command below
$ pwd
/home/roppenheimer/waves-tutorials
$ waves fetch --destination tutorial_cubit tutorials/tutorial_cubit
$ ls tutorial_cubit
modsim_package/ abaqus.scons cubit.scons SConstruct sierra.scons fierro.scons
Make the new
tutorial_cubit
directory the current working directory
$ pwd
/home/roppenheimer/waves-tutorials
$ cd tutorial_cubit
$ pwd
/home/roppenheimer/waves-tutorials/tutorial_cubit
$ ls
modsim_package/ abaqus.scons cubit.scons SConstruct sierra.scons fierro.scons
SConscript#
Note that the tutorial_cubit
directory has three SConscript files: cubit.scons
, abaqus.scons
,
sierra.scons
. The first two are relevant to the current tutorial. The sierra.scons
workflow is described in the
complementary Tutorial: Cubit+Sierra.
Review the
cubit.scons
andabaqus.scons
tutorials and compare them against the Tutorial 04: Simulation files.
The structure has changed enough that a diff view is not as useful. Instead the contents of the new SConscript files are duplicated below.
waves-tutorials/tutorial_cubit/cubit.scons
1#! /usr/bin/env python
2"""Rectangle compression workflow: geometry, partition, mesh.
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 * ``cubit`` - String path for the Cubit executable
10
11* ``element_type`` - The Cubit 4 node quadrilateral element type
12* ``solver`` - The target solver to use when writing a mesh file
13"""
14
15import pathlib
16
17# Inherit the parent construction environment
18Import("env", "element_type", "solver")
19
20# Simulation variables
21build_directory = pathlib.Path(Dir(".").abspath)
22workflow_name = build_directory.name
23
24# Collect the target nodes to build a concise alias for all targets
25workflow = []
26
27# Rectangle 2D
28# Geometry
29workflow.extend(
30 env.PythonScript(
31 target=["rectangle_geometry.cub"],
32 source=["#/modsim_package/cubit/rectangle_geometry.py"],
33 subcommand_options="",
34 )
35)
36
37# Partition
38workflow.extend(
39 env.PythonScript(
40 target=["rectangle_partition.cub"],
41 source=["#/modsim_package/cubit/rectangle_partition.py", "rectangle_geometry.cub"],
42 subcommand_options="",
43 )
44)
45
46# Mesh
47if solver.lower() == "abaqus":
48 mesh_extension = "inp"
49elif solver.lower() in ["sierra", "adagio"]:
50 mesh_extension = "g"
51else:
52 raise RuntimeError(f"Unknown solver '{solver}'")
53workflow.extend(
54 env.PythonScript(
55 target=[f"rectangle_mesh.{mesh_extension}", "rectangle_mesh.cub"],
56 source=["#/modsim_package/cubit/rectangle_mesh.py", "rectangle_partition.cub"],
57 subcommand_options="--element-type ${element_type} --solver ${solver}",
58 element_type=element_type,
59 solver=solver,
60 )
61)
62
63
64# Cube 3D
65# Geometry
66workflow.extend(
67 env.PythonScript(
68 target=["cube_geometry.cub"],
69 source=["#/modsim_package/cubit/cube_geometry.py"],
70 subcommand_options="",
71 )
72)
73
74# Partition
75workflow.extend(
76 env.PythonScript(
77 target=["cube_partition.cub"],
78 source=["#/modsim_package/cubit/cube_partition.py", "cube_geometry.cub"],
79 subcommand_options="",
80 )
81)
82
83# Mesh
84if solver.lower() == "abaqus":
85 mesh_extension = "inp"
86elif solver.lower() in ["sierra", "adagio"]:
87 mesh_extension = "g"
88else:
89 raise RuntimeError(f"Unknown solver '{solver}'")
90workflow.extend(
91 env.PythonScript(
92 target=[f"cube_mesh.{mesh_extension}", "cube_mesh.cub"],
93 source=["#/modsim_package/cubit/cube_mesh.py", "cube_partition.cub"],
94 subcommand_options="--element-type ${element_type} --solver ${solver}",
95 element_type=element_type,
96 solver=solver,
97 )
98)
99
100# Collector alias based on parent directory name
101env.Alias(f"{workflow_name}_cubit", workflow)
102
103if not env["unconditional_build"] and not env["CUBIT_PROGRAM"]:
104 print(f"Program 'cubit' was not found in construction environment. Ignoring '{workflow_name}' target(s)")
105 Ignore([".", workflow_name], workflow)
waves-tutorials/tutorial_cubit/abaqus.scons
1#! /usr/bin/env python
2"""Rectangle compression workflow: Abaqus solve.
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 * ``cubit`` - String path for the Cubit executable
11"""
12
13import pathlib
14
15# Inherit the parent construction environment
16Import("env")
17
18# Simulation variables
19build_directory = pathlib.Path(Dir(".").abspath)
20workflow_name = build_directory.name
21
22# Collect the target nodes to build a concise alias for all targets
23workflow = []
24datacheck = []
25
26element_type = "QUAD"
27solver = "abaqus"
28SConscript("cubit.scons", exports={"env": env, "element_type": element_type, "solver": solver}, duplicate=False)
29
30# Modify mesh target(s) element type.
31# Linux style sed command options must be modified for macOS. Command must be modified if on Windows.
32env.AddPostAction("rectangle_mesh.inp", "sed -i 's/CPE4/CPS4/g' ${TARGET.abspath}")
33
34# SolverPrep
35copy_source_list = [
36 "#/modsim_package/abaqus/rectangle_compression.inp",
37 "#/modsim_package/abaqus/assembly.inp",
38 "#/modsim_package/abaqus/boundary.inp",
39 "#/modsim_package/abaqus/field_output.inp",
40 "#/modsim_package/abaqus/materials.inp",
41 "#/modsim_package/abaqus/parts.inp",
42 "#/modsim_package/abaqus/history_output.inp",
43]
44workflow.extend(
45 env.CopySubstfile(
46 copy_source_list,
47 )
48)
49
50# Abaqus Solve
51solve_source_list = [
52 "rectangle_compression.inp",
53 "assembly.inp",
54 "boundary.inp",
55 "field_output.inp",
56 "materials.inp",
57 "parts.inp",
58 "history_output.inp",
59 "rectangle_mesh.inp",
60]
61
62datacheck.extend(
63 env.AbaqusSolver(
64 target=[
65 "rectangle_compression_DATACHECK.odb",
66 "rectangle_compression_DATACHECK.dat",
67 "rectangle_compression_DATACHECK.msg",
68 "rectangle_compression_DATACHECK.com",
69 "rectangle_compression_DATACHECK.prt",
70 "rectangle_compression_DATACHECK.023",
71 "rectangle_compression_DATACHECK.mdl",
72 "rectangle_compression_DATACHECK.sim",
73 "rectangle_compression_DATACHECK.stt",
74 ],
75 source=solve_source_list,
76 job="rectangle_compression_DATACHECK",
77 program_options="-double both -datacheck",
78 )
79)
80
81workflow.extend(
82 env.AbaqusSolver(
83 target=[
84 "rectangle_compression.odb",
85 "rectangle_compression.dat",
86 "rectangle_compression.msg",
87 "rectangle_compression.com",
88 "rectangle_compression.prt",
89 "rectangle_compression.sta",
90 ],
91 source=solve_source_list,
92 job="rectangle_compression",
93 program_options="-double both",
94 )
95)
96
97# Collector alias based on parent directory name
98env.Alias(workflow_name, workflow)
99env.Alias(f"{workflow_name}_datacheck", datacheck)
100
101if not env["unconditional_build"] and not env["ABAQUS_PROGRAM"]:
102 print(f"Program 'abaqus' was not found in construction environment. Ignoring '{workflow_name}' target(s)")
103 Ignore([".", workflow_name], workflow)
104 Ignore([".", f"{workflow_name}_datacheck"], datacheck)
Note that Cubit does not support the Abaqus plane stress element CPS4
, so we must add a post-action to the orphan
mesh target to change the element type. A post-action is used to avoid generating intermediate target files, which would
be required if we created a separate task for the file modification. This post-action is written to apply to a list, so
if additional orphan mesh files needed the same modification, the post-action would be added to each targets’ build
signature definition with a single AddPostAction
definition.
The sed
command is not available on all systems, but a Conda packaged version, conda-forge sed, of the GNU
sed program can be used to provide system-to-system consistency with Conda environment management. See the Conda
documentation for more information about virtual environment management with Conda.
Cubit Journal Files#
Review the following journal files in the
waves-tutorials/modsim_package/cubit
directory.
The Cubit journal files include the same CLI introduced in Tutorial 02: Partition and Mesh for the Abaqus journal files. Besides the differences in Abaqus and Cubit commands, the major difference between the Abaqus and Cubit journal files is the opportunity to use Python 3 with Cubit, where Abaqus journal files must use the Abaqus controlled installation of Python 2. The API and CLI built from the Cubit journal files’ docstrings may be found in the WAVES-TUTORIAL API for cubit and the WAVES-TUTORIAL CLI for cubit, respectively.
waves-tutorials/tutorial_cubit/modsim_package/cubit/rectangle_geometry.py
1"""Create a simple rectangle geometry."""
2
3import argparse
4import pathlib
5import sys
6
7import cubit
8
9
10def main(output_file: pathlib.Path, width: float, height: float) -> None:
11 """Create a simple rectangle geometry.
12
13 This script creates a simple Cubit model with a single rectangle part.
14
15 :param str output_file: The output file for the Cubit model. Will be stripped of the extension and ``.cub`` will be
16 used.
17 :param float width: The rectangle width
18 :param float height: The rectangle height
19
20 :returns: writes ``output_file``.cub
21 """
22 output_file = output_file.with_suffix(".cub")
23
24 cubit.init(["cubit", "-noecho", "-nojournal", "-nographics", "-batch"])
25 cubit.cmd("new")
26 cubit.cmd("reset")
27
28 cubit.cmd("create vertex 0 0 0")
29 cubit.cmd(f"create vertex {width} 0 0")
30 cubit.cmd(f"create vertex {width} {height} 0")
31 cubit.cmd(f"create vertex 0 {height} 0")
32 cubit.cmd("create surface vertex 1,2,3,4")
33
34 cubit.cmd(f"save as '{output_file}' overwrite")
35
36
37def get_parser() -> argparse.ArgumentParser:
38 """Return the command-line interface parser."""
39 script_name = pathlib.Path(__file__)
40 # Set default parameter values
41 default_output_file = script_name.with_suffix(".cub").name
42 default_width = 1.0
43 default_height = 1.0
44
45 prog = f"python {script_name.name} "
46 cli_description = "Create a simple rectangle geometry and write an ``output_file``.cub Cubit model file."
47 parser = argparse.ArgumentParser(description=cli_description, prog=prog)
48 parser.add_argument(
49 "--output-file",
50 type=pathlib.Path,
51 default=default_output_file,
52 help=(
53 "The output file for the Cubit model. "
54 "Will be stripped of the extension and ``.cub`` will be used, e.g. ``output_file``.cub "
55 "(default: %(default)s"
56 ),
57 )
58 parser.add_argument(
59 "--width",
60 type=float,
61 default=default_width,
62 help="The rectangle width",
63 )
64 parser.add_argument(
65 "--height",
66 type=float,
67 default=default_height,
68 help="The rectangle height",
69 )
70 return parser
71
72
73if __name__ == "__main__":
74 parser = get_parser()
75 args = parser.parse_args()
76 sys.exit(
77 main(
78 output_file=args.output_file,
79 width=args.width,
80 height=args.height,
81 )
82 )
waves-tutorials/tutorial_cubit/modsim_package/cubit/rectangle_partition.py
1"""Partition the simple rectangle geometry created by ``rectangle_geometry.py``."""
2
3import argparse
4import pathlib
5import shutil
6import sys
7
8import cubit
9
10
11def main(input_file: pathlib.Path, output_file: pathlib.Path, width: float, height: float) -> None:
12 """Partition the simple rectangle geometry created by ``rectangle_geometry.py``.
13
14 This script partitions a simple Cubit model with a single rectangle part.
15
16 **Feature labels:**
17
18 * ``bottom_left`` - bottom left vertex
19 * ``bottom_right`` - bottom right vertex
20 * ``top_right`` - top right vertex
21 * ``top_left`` - top left vertex
22 * ``left`` - left edge nodes
23 * ``top`` - top edge nodes
24 * ``right`` - right edge nodes
25 * ``bottom`` - bottom edge nodes
26 * ``elset_left`` - left edge elements
27 * ``elset_top`` - top edge elements
28 * ``elset_right`` - right edge elements
29 * ``elset_bottom`` - bottom edge elements
30
31 :param str input_file: The Cubit model file created by ``rectangle_geometry.py``. Will be stripped of the extension
32 and ``.cub`` will be used.
33 :param str output_file: The output file for the Cubit model. Will be stripped of the extension and ``.cub`` will be
34 used.
35 :param float width: The rectangle width
36 :param float height: The rectangle height
37
38 :returns: writes ``output_file``.cub
39 """
40 input_file = input_file.with_suffix(".cub")
41 output_file = output_file.with_suffix(".cub")
42
43 # Avoid modifying the contents or timestamp on the input file.
44 # Required to get conditional re-builds with a build system such as GNU Make, CMake, or SCons
45 if input_file != output_file:
46 shutil.copyfile(input_file, output_file)
47
48 cubit.init(["cubit", "-noecho", "-nojournal", "-nographics", "-batch"])
49 cubit.cmd("new")
50 cubit.cmd("reset")
51
52 cubit.cmd(f"open '{output_file}'")
53
54 cubit.cmd("nodeset 1 add vertex 1")
55 cubit.cmd("nodeset 1 name 'bottom_left'")
56 cubit.cmd("nodeset 2 add vertex 2")
57 cubit.cmd("nodeset 2 name 'bottom_right'")
58 cubit.cmd("nodeset 3 add vertex 3")
59 cubit.cmd("nodeset 3 name 'top_right'")
60 cubit.cmd("nodeset 4 add vertex 4")
61 cubit.cmd("nodeset 4 name 'top_left'")
62
63 cubit.cmd("nodeset 5 add curve 4")
64 cubit.cmd("nodeset 5 name 'left'")
65 cubit.cmd("nodeset 6 add curve 3")
66 cubit.cmd("nodeset 6 name 'top'")
67 cubit.cmd("nodeset 7 add curve 2")
68 cubit.cmd("nodeset 7 name 'right'")
69 cubit.cmd("nodeset 8 add curve 1")
70 cubit.cmd("nodeset 8 name 'bottom'")
71
72 cubit.cmd("sideset 1 add curve 4")
73 cubit.cmd("sideset 1 name 'elset_left'")
74 cubit.cmd("sideset 2 add curve 3")
75 cubit.cmd("sideset 2 name 'elset_top'")
76 cubit.cmd("sideset 3 add curve 2")
77 cubit.cmd("sideset 3 name 'elset_right'")
78 cubit.cmd("sideset 4 add curve 1")
79 cubit.cmd("sideset 4 name 'elset_bottom'")
80
81 cubit.cmd(f"save as '{output_file}' overwrite")
82
83
84def get_parser() -> argparse.ArgumentParser:
85 """Return the command-line interface parser."""
86 script_name = pathlib.Path(__file__)
87 # Set default parameter values
88 default_input_file = script_name.with_suffix(".cub").name.replace("_partition", "_geometry")
89 default_output_file = script_name.with_suffix(".cub").name
90 default_width = 1.0
91 default_height = 1.0
92
93 prog = f"python {script_name.name} "
94 cli_description = (
95 "Partition the simple rectangle geometry created by ``rectangle_geometry.py`` "
96 "and write an ``output_file``.cub Cubit model file."
97 )
98 parser = argparse.ArgumentParser(description=cli_description, prog=prog)
99 parser.add_argument(
100 "--input-file",
101 type=pathlib.Path,
102 default=default_input_file,
103 help=(
104 "The Cubit model file created by ``rectangle_geometry.py``. "
105 "Will be stripped of the extension and ``.cub`` will be used, e.g. ``input_file``.cub "
106 "(default: %(default)s"
107 ),
108 )
109 parser.add_argument(
110 "--output-file",
111 type=pathlib.Path,
112 default=default_output_file,
113 help=(
114 "The output file for the Cubit model. "
115 "Will be stripped of the extension and ``.cub`` will be used, e.g. ``output_file``.cub "
116 "(default: %(default)s"
117 ),
118 )
119 parser.add_argument(
120 "--width",
121 type=float,
122 default=default_width,
123 help="The rectangle width",
124 )
125 parser.add_argument(
126 "--height",
127 type=float,
128 default=default_height,
129 help="The rectangle height",
130 )
131 return parser
132
133
134if __name__ == "__main__":
135 parser = get_parser()
136 args = parser.parse_args()
137 sys.exit(
138 main(
139 input_file=args.input_file,
140 output_file=args.output_file,
141 width=args.width,
142 height=args.height,
143 )
144 )
waves-tutorials/tutorial_cubit/modsim_package/cubit/rectangle_mesh.py
1"""Mesh the simple rectangle geometry partitioned by ``rectangle_partition.py``."""
2
3import argparse
4import pathlib
5import shutil
6import sys
7
8import cubit
9
10
11def main(
12 input_file: pathlib.Path,
13 output_file: pathlib.Path,
14 global_seed: float,
15 element_type: str = "QUAD",
16 solver: str = "abaqus",
17) -> None:
18 """Mesh the simple rectangle geometry partitioned by ``rectangle_partition.py``.
19
20 This script meshes a simple Cubit model with a single rectangle part.
21
22 **Feature labels:**
23
24 * ``NODES`` - all part nodes
25 * ``ELEMENTS`` - all part elements
26
27 :param str input_file: The Cubit model file created by ``rectangle_partition.py``. Will be stripped of the extension
28 and ``.cub`` will be used.
29 :param str output_file: The output file for the Cubit model. Will be stripped of the extension and ``.cub`` and
30 ``.inp`` will be used for the model and orphan mesh output files, respectively.
31 :param float global_seed: The global mesh seed size
32 :param str element_type: The model element type. Must be a supported Cubit 4 node element type.
33 :param str solver: The solver type to use when exporting the mesh
34
35 :returns: writes ``output_file``.cub and ``output_file``.inp
36
37 :raises RuntimeError: If the solver is not supported
38 """
39 input_file = pathlib.Path(input_file).with_suffix(".cub")
40 output_file = pathlib.Path(output_file).with_suffix(".cub")
41 abaqus_mesh_file = output_file.with_suffix(".inp")
42 sierra_mesh_file = output_file.with_suffix(".g")
43
44 # Avoid modifying the contents or timestamp on the input file.
45 # Required to get conditional re-builds with a build system such as GNU Make, CMake, or SCons
46 if input_file != output_file:
47 shutil.copyfile(input_file, output_file)
48
49 cubit.init(["cubit", "-noecho", "-nojournal", "-nographics", "-batch"])
50 cubit.cmd("new")
51 cubit.cmd("reset")
52
53 cubit.cmd(f"open '{output_file}'")
54
55 cubit.cmd(f"surface 1 size {global_seed}")
56 cubit.cmd("mesh surface 1")
57 cubit.cmd("set duplicate block elements off")
58
59 cubit.cmd("nodeset 9 add surface 1")
60 cubit.cmd("nodeset 9 name 'NODES'")
61
62 cubit.cmd("block 1 add surface 1")
63 cubit.cmd(f"block 1 name 'ELEMENTS' Element type {element_type}")
64
65 cubit.cmd(f"save as '{output_file}' overwrite")
66
67 if solver.lower() == "abaqus":
68 # Export Abaqus orphan mesh for Abaqus workflow
69 cubit.cmd(f"export abaqus '{abaqus_mesh_file}' partial dimension 2 block 1 overwrite everything")
70 elif solver.lower() in ["sierra", "adagio"]:
71 # Export Genesis file for Sierra workflow
72 cubit.cmd(f"export mesh '{sierra_mesh_file}' overwrite")
73 else:
74 raise RuntimeError(f"Uknown solver '{solver}'")
75
76
77def get_parser() -> argparse.ArgumentParser:
78 """Return the command-line interface parser."""
79 script_name = pathlib.Path(__file__)
80 # Set default parameter values
81 default_input_file = script_name.with_suffix(".cub").name.replace("_mesh", "_partition")
82 default_output_file = script_name.with_suffix(".cub").name
83 default_global_seed = 1.0
84 default_element_type = "QUAD"
85 default_solver = "abaqus"
86
87 prog = f"python {script_name.name} "
88 cli_description = (
89 "Mesh the simple rectangle geometry partitioned by ``rectangle_partition.py`` "
90 "and write an ``output_file``.cub Cubit model file and ``output_file``.inp orphan mesh file."
91 )
92 parser = argparse.ArgumentParser(description=cli_description, prog=prog)
93 parser.add_argument(
94 "--input-file",
95 type=pathlib.Path,
96 default=default_input_file,
97 help=(
98 "The Cubit model file created by ``rectangle_partition.py``. "
99 "Will be stripped of the extension and ``.cub`` will be used, e.g. ``input_file``.cub "
100 "(default: %(default)s"
101 ),
102 )
103 parser.add_argument(
104 "--output-file",
105 type=pathlib.Path,
106 default=default_output_file,
107 help=(
108 "The output file for the Cubit model. "
109 "Will be stripped of the extension and ``.cub`` will be used, e.g. ``output_file``.cub"
110 ),
111 )
112 parser.add_argument(
113 "--global-seed",
114 type=float,
115 default=default_global_seed,
116 help="The global mesh seed size (default: %(default)s)",
117 )
118 parser.add_argument(
119 "--element-type",
120 type=str,
121 default=default_element_type,
122 help="The model element type. Must be a supported Cubit 4 node element type. (default: %(default)s)",
123 )
124 parser.add_argument(
125 "--solver",
126 type=str,
127 default=default_solver,
128 choices=["abaqus", "sierra", "adagio"],
129 help="The target solver for the mesh file. (default: %(default)s)",
130 )
131
132 return parser
133
134
135if __name__ == "__main__":
136 parser = get_parser()
137 args = parser.parse_args()
138 sys.exit(
139 main(
140 input_file=args.input_file,
141 output_file=args.output_file,
142 global_seed=args.global_seed,
143 element_type=args.element_type,
144 solver=args.solver,
145 )
146 )
SConstruct#
A diff
against the SConstruct
file from Tutorial 04: Simulation is included below to help identify the
changes made in this tutorial.
waves-tutorials/tutorial_cubit/SConstruct
--- /home/runner/work/waves/waves/build/docs/tutorials_tutorial_04_simulation_SConstruct
+++ /home/runner/work/waves/waves/build/docs/tutorial_cubit_SConstruct
@@ -1,8 +1,11 @@
#! /usr/bin/env python
-"""Configure the WAVES simulation tutorial."""
+"""Configure the WAVES Cubit+Abaqus/Fierro/Sierra tutorial."""
import os
import pathlib
+import subprocess
+import sys
+import typing
import waves
@@ -49,6 +52,20 @@
metavar="COMMAND",
help=f"Override for the Abaqus command. Repeat to specify more than one (default: {default_abaqus_commands})",
)
+default_cubit_commands = [
+ "/apps/Cubit-16.12/cubit",
+ "/usr/projects/ea/Cubit/Cubit-16.12/cubit",
+ "cubit",
+]
+AddOption(
+ "--cubit-command",
+ dest="cubit_command",
+ nargs=1,
+ type="string",
+ action="append",
+ metavar="COMMAND",
+ help=f"Override for the Cubit command. Repeat to specify more than one (default: {default_cubit_commands})",
+)
# Comments used in tutorial code snippets: marker-2
@@ -59,6 +76,7 @@
unconditional_build=GetOption("unconditional_build"),
print_build_failures=GetOption("print_build_failures"),
abaqus_commands=GetOption("abaqus_command"),
+ cubit_commands=GetOption("cubit_command"),
)
# Conditionally print failed task *.stdout files
@@ -70,6 +88,30 @@
env["ABAQUS_PROGRAM"] = env.AddProgram(
env["abaqus_commands"] if env["abaqus_commands"] is not None else default_abaqus_commands
)
+env["CUBIT_PROGRAM"] = env.AddCubit(
+ env["cubit_commands"] if env["cubit_commands"] is not None else default_cubit_commands
+)
+env["FIERRO_IMPLICIT_PROGRAM"] = env.AddProgram(["fierro-parallel-implicit"])
+
+
+# Sierra requires a separate construction environment
+def return_modulepath(modules: typing.Iterable[str]) -> pathlib.Path:
+ """Return parent path of first found module or the current working directory."""
+ return next((pathlib.Path(path).parent for path in modules if pathlib.Path(path).exists()), pathlib.Path())
+
+
+sierra_modules = [
+ "/projects/aea_compute/modulefiles/sierra/5.21.6",
+]
+sierra_modulefile_parent = return_modulepath(sierra_modules)
+try:
+ env_sierra = waves.scons_extensions.shell_environment(
+ f"module use {sierra_modulefile_parent} && module load sierra"
+ )
+except subprocess.CalledProcessError:
+ print("Failed to initialize the Sierra environment", file=sys.stderr)
+ env_sierra = Environment()
+env_sierra["sierra"] = waves.scons_extensions.add_program(env_sierra, ["sierra"])
# Comments used in tutorial code snippets: marker-4
@@ -85,26 +127,33 @@
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
env.Append(BUILDERS={})
+env_sierra.Append(
+ BUILDERS={
+ "Sierra": waves.scons_extensions.sierra_builder_factory(program=env_sierra["sierra"]),
+ }
+)
+
# Comments used in tutorial code snippets: marker-6
# Add simulation targets
workflow_configurations = [
- "tutorial_01_geometry.scons",
- "tutorial_02_partition_mesh.scons",
- "tutorial_03_solverprep.scons",
- "tutorial_04_simulation.scons",
+ "abaqus.scons",
+ "sierra.scons",
+ "fierro.scons",
]
for workflow in workflow_configurations:
build_dir = env["variant_dir_base"] / pathlib.Path(workflow).stem
- SConscript(workflow, variant_dir=build_dir, exports={"env": env}, duplicate=False)
+ SConscript(
+ workflow,
+ variant_dir=build_dir,
+ exports={"env": env, "env_sierra": env_sierra},
+ duplicate=False,
+ )
# Comments used in tutorial code snippets: marker-7
Note that the Cubit Python files don’t perform any imports from the current modsim project package, so the
PYTHONPATH
modification is no longer required. This tutorial is created in a new, stand-alone subdirectory, so the
previous tutorial workflow configurations are no longer available. Only the sierra
and abaqus
workflow
configurations will be found by SCons at execution time. Finally, note that the cubit
SConscript file is not called
by the SConstruct
file. Instead, the cubit
configuration is reused by the sierra
and abaqus
workflows,
so the Cubit tasks only need to be defined once. To handle this task reuse some additional variable import and export
statements are required by the cubit
configuration file.
Build Targets#
Build the new targets
$ pwd
/home/roppenheimer/waves-tutorials/tutorial_cubit
$ scons abaqus
scons: Reading SConscript files ...
Checking whether /apps/abaqus/Commands/abq2024 program exists.../apps/abaqus/Commands/abq2024
Checking whether abq2024 program exists...no
Checking whether /apps/Cubit-16.12/cubit program exists.../apps/Cubit-16.12/cubit
Checking whether cubit program exists...no
Checking whether sierra program exists...no
scons: done reading SConscript files.
scons: Building targets ...
Copy("build/abaqus/rectangle_compression.inp", "modsim_package/abaqus/rectangle_compression.inp")
Copy("build/abaqus/assembly.inp", "modsim_package/abaqus/assembly.inp")
Copy("build/abaqus/boundary.inp", "modsim_package/abaqus/boundary.inp")
Copy("build/abaqus/field_output.inp", "modsim_package/abaqus/field_output.inp")
Copy("build/abaqus/materials.inp", "modsim_package/abaqus/materials.inp")
Copy("build/abaqus/parts.inp", "modsim_package/abaqus/parts.inp")
Copy("build/abaqus/history_output.inp", "modsim_package/abaqus/history_output.inp")
cd /home/roppenheimer/waves-tutorials/tutorial_cubit/build/abaqus && python /home/roppenheimer/waves-tutorials/tutorial_cubit/modsim_package/cubit/rectangle_geometry.py > rectangle_geometry.stdout 2>&1
cd /home/roppenheimer/waves-tutorials/tutorial_cubit/build/abaqus && python /home/roppenheimer/waves-tutorials/tutorial_cubit/modsim_package/cubit/rectangle_partition.py > rectangle_partition.stdout 2>&1
cd /home/roppenheimer/waves-tutorials/tutorial_cubit/build/abaqus && python /home/roppenheimer/waves-tutorials/tutorial_cubit/modsim_package/cubit/rectangle_mesh.py > rectangle_mesh.stdout 2>&1
cd /home/roppenheimer/waves-tutorials/tutorial_cubit/build/abaqus && /apps/abaqus/Commands/abq2024 -job rectangle_compression -input rectangle_compression -double both -interactive -ask_delete no > rectangle_compression.stdout 2>&1
scons: done building targets.
Output Files#
Explore the contents of the build
directory using the tree
command against the build/abaqus
directory, as shown
below.
$ pwd
/home/roppenheimer/waves-tutorials/tutorial_cubit
$ tree build/abaqus
build/abaqus
|-- 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_compression.stdout
|-- rectangle_geometry.cub
|-- rectangle_geometry.stdout
|-- rectangle_mesh.cub
|-- rectangle_mesh.inp
|-- rectangle_mesh.stdout
|-- rectangle_partition.cub
`-- rectangle_partition.stdout
0 directories, 21 files