Getting started
This chapter builds James from its sources, runs a first script, and lists every command-line flag and exit code. James is one executable, james. It reads one script and writes one or more mesh files per run. It needs no library, except the netCDF Fortran library if you want the Exodus II writer.
Build
James needs GNU GCC 16 (gcc-16 and gfortran-16) and GNU Make 3.81 or newer. On macOS, install them with Homebrew. James rejects /usr/bin/gcc on purpose, because on macOS it is Apple clang.
The Exodus II writer is optional. It needs the netCDF Fortran library (netcdf-fortran, which brings nf-config) and the netCDF library (netcdf, which brings nc-config and ncdump). When nf-config is on the path, the build uses it. Otherwise James is built without Exodus II and refuses an Exodus write with built without Exodus support. The command james -v says which build you have. It prints one line with the front end’s version, the engine’s version and whether the Exodus II writer is present, with the netCDF version it uses.
These are the make lines you need:
make # debug build with sanitizers -> build/debug/bin/james
make release # optimized build -> build/release/bin/james
make test # build and run every test (debug)
make BUILD=release test # same for the release build
make WITH_EXODUS=no test # the build without Exodus II, into build/debug-stub, and its tests
make test-docs # documentation lint and the lint's own tests
make check-toolchain # print compiler and make versions
make help # list targets and variables
WITH_EXODUS=auto|yes|no chooses the writer. The default, auto, takes it when nf-config is on the path.
To build with other compilers, run make CC=gcc-15 FC=gfortran-15. Both compilers must be GNU. The netCDF Fortran library must have been built by the same gfortran release. make checks that before it compiles the writer and says so when it cannot.
Run
The usage line is:
james input.tg [-o out] [-keepcl] [--dump-prep path] [--dump-ir path] [--format neutral|exodus] [--write-unref-nodes] [--engine-lines n] [-v] [-h]
james -h prints it with one line for each flag.
| Flag | What it does |
|---|---|
-o out | Writes the mesh to out. Without it, James names the file trugrdo. A script’s own mof command can still rename it (mof). |
i=file, o=file | The same as the bare input name and as -o out. |
-keepcl | Keeps each cleaned buffer next to its input as name.clean.tg. |
--dump-prep path | Writes the expanded script to path. |
--dump-ir path | Writes the intermediate representation to path. |
--format f | Chooses the output writer when the script names none. f is neutral or exodus. |
--write-unref-nodes | Writes every node of every part, not only the nodes an element uses. |
--engine-lines n | Keeps up to n of the engine’s warning and note lines. The default is 4096. |
-v, --version | Prints the version and exits. |
-h, --help | Prints the usage and exits. |
A script that names no output format takes --format. A script that names one, with neutral or exodusii, ignores --format. If neither names a format when write runs, James refuses the write (E1101). The flags -h and -v win over everything else, so james -h needs no input file.
James exits with one of four codes:
| Code | Meaning |
|---|---|
| 0 | The run succeeded. |
| 1 | The script has errors. With errmod 2 or 3, James stops and exits at the first error. With errmod 0 or 1, the default, it reports every error in the file and then exits (errmod). An input file that cannot be read also exits 1. |
| 2 | The engine refused the input or could not write a file, for example because the output path cannot be opened. |
| 3 | The command line is wrong: an unknown flag, a missing value or no input file. |
James writes the IR dump before the engine runs, whether the front end found errors or not. The dump always shows what James built, whatever the engine later does with it.
A first mesh
Build the optimized executable, then write the first example. Plain make builds the debug executable, which carries the sanitizers the tests run under.
make release
cd examples
../build/release/bin/james lens.tg --format exodus -o lens.exo
ncdump -h lens.exo
The script is examples/lens.tg. It names no format, so the command line chooses one.
c A lens: one block part, its top and bottom faces projected onto two
c spheres and its four sides onto a cylinder, so the square block becomes a
c round biconvex lens 0.6 thick at the rim and 1.67 at the centre.
c 245 nodes, 144 elements.
c
c james lens.tg --format exodus -o lens.exo
sd 1 cyli 0 0 0 0 0 1 2
sd 2 sphe 0 0 3.164 4
sd 3 sphe 0 0 -3.164 4
block 1 7;1 7;1 5;-1.4 1.4;-1.4 1.4;-0.6 0.6;
sfi -1 -2;;;sd 1
sfi ;-1 -2;;sd 1
sfi ;;-1;sd 2
sfi ;;-2;sd 3
endpart
merge
write
The script builds one block with 6 by 6 by 4 elements. Three surfaces shape it. Surface 1 is a cylinder of radius 2 about the z axis. Surfaces 2 and 3 are two spheres of radius 4, one centred above the block and one below. Every face of the block is projected: the four sides onto the cylinder, the bottom onto the sphere above and the top onto the sphere below. The block starts as a square slab 2.8 across and 1.2 thick. It ends as a round lens 0.6 thick at the rim and 1.67 thick at the centre. James writes 245 nodes and 144 elements. The concepts chapter explains why each face bulges the way it does.
The ncdump -h output starts with netcdf lens {, and its dimensions read num_nodes = 245, num_elem = 144 and num_el_blk = 1. A part is one element block.
To see the mesh as text, write James’s own neutral file.
../build/release/bin/james lens.tg --format neutral -o lens.neu
The neutral file is plain text and lists every node and element. The tests compare it, byte for byte, with a stored file. The merge and output chapter describes both formats.
What James prints
A run that succeeds prints nothing, as the lens does. A run that has something to say prints it as one line for each message. The second example, examples/two-blocks.tg, builds two blocks that share a face and merges them with stp .001.
c Two blocks of 3 x 3 x 3 nodes sharing the face x = 1. The stp command
c merges the nine nodes of the shared face and reports what it merged:
c 54 nodes become 45.
c
c james two-blocks.tg --format exodus -o two-blocks.exo
block 1 3;1 3;1 3;0 1;0 1;0 1;
endpart
block 1 3;1 3;1 3;1 2;0 1;0 1;
endpart
merge
stp .001
write
James writes 45 nodes and 16 elements in two element blocks, one for each part. It prints what it merged, as the two note lines of examples/two-blocks.diag.txt:
two-blocks.tg:11:1: note: 9 nodes were deleted by tolerancing
two-blocks.tg:11:1: note: 9 nodes merged between parts 1 and 2
Each line names the file, the line and the column of the command that caused it. The notes name line 11, the stp command that did the merging. James prints them after the write has written its file, so a write that cannot open its file prints no report.
Four commands set the merge tolerance: t and tp for every node, and st and stp for the nodes on a part’s surface. tp and stp also report. The last one before a write governs that write alone. A pair of nodes merges when its distance is less than the tolerance. The first-defined node survives. A value of zero or below merges nothing. An element that a merge leaves with zero volume is written as it is, and James warns about it. The merge and output chapter has the full rules.
Which nodes are written
A node that no element uses is not written. This happens for a node inside a region that de or dei deleted. The flag --write-unref-nodes writes every node of every part. Use it to see the nodes of a part that has no element at all.
James keeps up to 4096 of the engine’s warning and note lines. That is enough for a merge report of about four thousand pairs of parts. When a run raises more lines, the summary says how many James did not record. The flag --engine-lines n raises the limit.
Debugging a script
James turns a script into a mesh in five steps. It cleans and expands the script (clean, prep), parses the commands (cmd), and builds the intermediate representation (the IR). If the front end has raised no error, it hands the IR to the engine. The engine meshes the parts, merges their nodes and writes the files.
Two flags let you look inside. The first shows the script after expansion.
james input.tg -keepcl --dump-prep expanded.tg
This cleans and expands input.tg. It writes each cleaned buffer next to its input as input.clean.tg. It writes the expanded script to expanded.tg, which is itself a valid script, with every %name, [expr], include, para, array, def and when, for and while already resolved. Use it to debug a loop or a substitution.
The second flag shows what James built.
james input.tg --dump-ir ir.txt
--dump-ir writes the IR as text before the engine runs. You can read it, or compare two of them, whatever the engine does with it. The writing chapter explains the language those commands belong to.
Versions
Before 1.0.0, the minor number rises with each milestone that changes what a user can do. The patch number rises with a release that only fixes. The front end and the engine always carry the same number, and james -v prints both.
Each release’s manual is the docs/manual/ tree at that release’s tag. To read the manual for the version you run, check out its tag.