Merge and output

When the parts are built, the script enters the Merge phase, chooses a tolerance under which coincident nodes become one, chooses a file format, and writes. This chapter covers the four tolerance commands and what they report, how nodes and elements are numbered, the write command and the naming of its files, and the layout of the two formats James writes: its own neutral text file and Exodus II.

Entering the Merge phase

merge ends the open part, if there is one, and puts the session in the Merge phase. The commands that choose a tolerance, choose a format, name a file and write belong there, and the tolerance, format and name commands are also legal earlier. The one move out of the Merge phase is a new part: a block or cylinder there starts a part and returns the session to the Part phase. A second merge is an error.

Entering the phase merges nothing and writes nothing. A tolerance command only records its tolerance. The merge happens at a write, over the parts that the write covers, and the file is made then. Only write makes a file.

The tolerance commands

Four commands set the tolerance. They differ in which nodes they consider and in whether James reports what it merged.

Command Nodes considered Report
t every node of the covered parts none
tp every node of the covered parts yes
st surface nodes only none
stp surface nodes only yes

Each takes one real number, in the length units of the mesh. They are legal in the Control, Part and Merge phases.

What the merge does

James applies these rules at every write.

  • Two nodes merge when the distance between them is less than the tolerance. A distance exactly equal to the tolerance does not merge. A tolerance of zero or below merges nothing, which is how a script restores a mesh that an earlier tolerance merged.
  • Only the last tolerance command read before a write governs that write. Earlier ones, of any of the four commands, have no effect on it. A t followed by an stp merges surface nodes under the stp alone.
  • The merge always starts from the nodes as the parts built them. It is never chained onto the result of a previous merge.
  • James visits the nodes in the order of their numbers before merging. Each node merges into the closest node that has already survived and lies within the tolerance. Of two survivors at the same distance, it takes the one with the lower number. A node with no such survivor survives.
  • The survivor of a group is therefore its first-defined node, the one with the lowest number before merging. A survivor keeps its own position. The nodes that merge into it do not move it.
  • The rule is not transitive. A chain of three nodes, each 0.9 of the tolerance from the next, leaves two nodes: the second merges into the first, and the third is too far from the first and stays.
  • A surface node lies on a face of an active element that has no active element across it in the same part. That covers the six outer faces of a part and every face that de or dei exposed. Two parts that touch each have the touching face on their own side, so both count. Both nodes of a pair must be surface nodes. A node on the inner side of an element whose outer face is exposed is not one.
  • A node that no active element uses never merges, under any of the four commands, whether James writes it or not.
  • A write that no tolerance command precedes merges nothing.

Put the tolerance well above the gap you want to close and well below the size of an element. James compares against strictly less, and a tolerance near an element’s size collapses it.

The merge on two blocks

The script examples/two-blocks.tg, which make test runs, joins two blocks that share a face:

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

Each block has 3 by 3 by 3 nodes, 27 each, and they share the face x = 1. The comment lines give the command line, which names the format with --format because the script has none. The nine nodes of the shared face merge, so 54 nodes become 45. James prints the report that examples/two-blocks.diag.txt holds:

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

The report is described below.

What a chain of close nodes does

The script tests/golden/011-merge-chain/input.tg sets three cubes in a row, each 0.009 along x from the one before, under a tolerance of 0.01:

c Three unit cubes, each 0.9 of the tolerance along x from the one before.
c The second merges into the first; the third is 1.8 tolerances from the
c first and the second did not survive, so it stays: 24 nodes become 16,
c where a rule that merged through a chain would leave 8.
block 1 2;1 2;1 2;0 1;0 1;0 1;
endpart
block 1 2;1 2;1 2;.009 1.009;0 1;0 1;
endpart
block 1 2;1 2;1 2;.018 1.018;0 1;0 1;
endpart
merge
tp .01
neutral
write

Each of the second cube’s eight nodes is 0.009 from the matching node of the first cube, which is less than 0.01, so all eight merge into the first cube’s. Each of the third cube’s nodes is 0.018 from the matching node of the first cube, which is more than 0.01, and the second cube’s nodes did not survive, so the third cube’s nodes stay. The 24 nodes become 16. A rule that followed the chain would leave 8. James prints the report that tests/golden/011-merge-chain/input.diag.txt holds:

input.tg:12:1: note: 8 nodes were deleted by tolerancing
input.tg:12:1: note: 8 nodes merged between parts 1 and 2

The report

Under a governing tp or stp, James reports the merge in note lines. A note is not a warning and does not count in the summary, and errmod does not hide it. The report has two kinds of line.

  • N nodes were deleted by tolerancing: always one line, the number of nodes before the merge less the number after. A merge that finds nothing prints 0 nodes were deleted by tolerancing.
  • N nodes merged between parts A and B: one line for each pair of parts that merged any node. A is the part number of the surviving node and B the part number of the merged one, with A no larger than B. A merge inside one part has A equal to B. The lines come in order of B, then A.

A count of one reads 1 node was deleted and 1 node merged. The part numbers in these lines are the numbers of the script, counted from one. Use the per-pair lines to see that a pair of parts joined: a pair you expected and do not see is a pair whose tolerance was too small.

James prints the report after it has written and closed the file, so a write that fails prints no report, and every line is located at the governing tolerance command. t and st print no report. A write shares the merge and the report of the write just before it when it covers the same parts under the same governing tolerance. Only the previous write’s mesh is reused.

Golden case 012-merge-cylinder-seam merges the two faces of a ring, at 0 and at 360 degrees, inside one part. Its report reads 9 nodes were deleted by tolerancing and 9 nodes merged between parts 1 and 1.

When a merge collapses an element

A merge can leave an element with two or more corners on one node. James writes such an element as it is and does not drop it. If every one of the element’s eight corner triple products vanishes, the element has zero volume, and James warns, once for each part that has any. A hexahedron with a collapsed edge, which is how a wedge is built, keeps its volume and raises nothing.

The script tests/golden/014-merge-zero-volume/input.tg collapses a thin element:

c Two elements stacked in z, the upper one 0.0005 thick, under a tolerance
c of 0.001: the upper element's top face merges into its bottom face. The
c element is written as it is, with its four nodes twice, and warned about.
block 1 2;1 2;1 2 3;0 1;0 1;0 1 1.0005;
endpart
merge
t .001
neutral
write

The upper element is 0.0005 thick, and the tolerance of 0.001 merges its top face into its bottom face. The element is written with its four nodes twice. James prints the warning that tests/golden/014-merge-zero-volume/input.diag.txt holds:

input.tg:7:1: warning: part #0 has 1 element with zero volume after merging
james: 0 errors, 1 warning

The number after part # is the part’s position counted from zero, while the report’s part numbers count from one. A one-part script has part #0 and parts 1 and 1.

Numbering

James numbers nodes and elements by one rule.

  1. Part order: the parts in the order the script made them.
  2. Within a part, k, then j, then i, with i changing fastest.

Node numbers before the merge follow that rule over the nodes James writes. By default those are the nodes that at least one active element references. A node of a block that de or dei deleted, with no active element beside it, is skipped and takes no number. The flag --write-unref-nodes writes every node of every part instead. The flag adds nodes and changes nothing else: an unreferenced node never merges, so the elements, the merges and the survivors are the same in both modes, and only the numbers shift. Golden case 005-butterfly-all-nodes is the part of case 004-butterfly written with the flag, 845 nodes where the default writes 525.

A part that writes no node at all under the default, such as a part with no active element, raises a warning that names the flag. A part that only loses the nodes of its deleted regions raises none, since that is what a deletion means.

After a merge the node numbers are dense. The survivors are numbered 1 upward in the order of their numbers before the merge, and the numbers of the merged nodes leave no gaps. A node that was number 6 before the merge becomes number 5 when the node before it merged away.

Element numbers follow the same rule over each part’s active elements, concatenated in part order. A merge never removes an element, so elements need no compaction. Each element keeps its place and has some of its corners repointed to survivors.

In the file of golden case 006-merge-two-blocks, which merges the two blocks of examples/two-blocks.tg, part 1 holds nodes 1 to 27 and elements 1 to 8. The nine nodes of part 2 on the shared face took the numbers of the part 1 nodes they merged into. Part 2’s remaining 18 nodes are numbered 28 to 45 and its elements 9 to 16. Element 9, the first of part 2, lists the nodes 3, 28, 30, 6, 12, 34, 36 and 15: four of its corners are part 1’s nodes on the shared face.

nerl, which chooses another numbering, is refused. James has this one rule.

Writing the files

write makes one file. It takes no argument. It needs the Merge phase (E1100) and a format (E1101).

The format

Two commands choose the format, and the command line can too.

Chosen by Format
neutral, or --format neutral James’s own plain-text neutral file
exodusii, or --format exodus Exodus II

The format commands are legal in the Control, Part and Merge phases. A format command in the script wins over --format, which is only a fallback for a script that names none. The later of two format commands governs. A second command that names a different format, before any write has used the first, warns (E1104). A format command after a write chooses the next write’s format, so one script can write a mesh in both formats: golden cases 006-merge-two-blocks and 017-lens do. Every other format that other programs use is refused (E0100).

comment and verbatim are accepted and discarded. They have no effect on either file.

The file name

The name of a file comes from mof if the script gave one, then from the -o flag, then it is trugrdo. A mof in the script governs over -o.

The first write under a name uses the name as it stands. The second write under the same name appends .0001, the third .0002, and so on. James counts each name on its own, so a mof to a new name starts a new count, and a mof back to an earlier name continues that name’s count. A write never overwrites a file that an earlier write of the same run made.

The script tests/corpus/141-write-suffix-per-name/input.tg writes four files:

block 1 2;1 2;1 2;0 1;0 1;0 1;
endpart
merge
neutral
write
write
mof other.out
write
mof trugrdo
write

The first write makes trugrdo. The second makes trugrdo.0001. After mof other.out, the third makes other.out. After mof trugrdo, the fourth returns to the first name and makes trugrdo.0002.

If James cannot open a file for writing, it says so and leaves any file already there as it was. If a neutral write fails partway, James deletes the part-written file, so no truncated mesh is left behind.

The neutral format

The neutral format is James’s own. It is a plain dump of the merged mesh and is not the neutral file of any other program. Every field has a fixed width, so a file compares byte for byte. The file has these lines, in this order.

Line Content
1 james neutral 1, the format and its version
2 title and the title of the write, or title alone when the script has no title
3 nodes N elements M parts P with the counts of this file
one per node the node number, then x, y and z
one per element the element number, the part number, then the eight corner nodes
last end

A node line is written with the format (i10, 3(1x, es23.15)): the number right-aligned in ten columns, then each coordinate preceded by one space, in scientific notation with fifteen digits after the point, in 23 columns. An element line is written with (i10, 1x, i10, 8(1x, i10)): the element number, the part number and the eight corner numbers, each in ten columns after one space.

The node lines come in node-number order and the element lines in element-number order, so the numbers run 1 upward with no gaps. The part number on an element line is the part’s number in the script, counted from one. P is the number of parts the write covers. The file has no units line, since a script carries no units.

A coordinate with a three-digit exponent prints without its E, so 1e300 reads 1.000000000000000+300. A reader in C must put the E back before it converts the field.

The first lines and two later lines of the file of golden case 006-merge-two-blocks, tests/golden/006-merge-two-blocks/expected.neutral.txt, are:

james neutral 1
title
nodes 45 elements 16 parts 2
         1   0.000000000000000E+00   0.000000000000000E+00   0.000000000000000E+00

Those are the header and node 1. The element lines start at line 49. The first one is:

         1          1          1          2          5          4         10         11         14         13

The corner order of an element

The eight corners follow one order, the same one the Exodus II HEX8 element uses. The corners 1 to 4 are the k face of the cell, counterclockwise as seen from the k + 1 side. The corners 5 to 8 are the k + 1 face in the same order. For the cell (i, j, k) the corners are n1 = (i, j, k), n2 = (i+1, j, k), n3 = (i+1, j+1, k) and n4 = (i, j+1, k), and n5 to n8 are the same points with k + 1.

              n8 ---------- n7
             /|            /|
            / |           / |        the k + 1 face: n5 n6 n7 n8
          n5 ---------- n6  |
           |  n4 -------|-- n3
           | /          | /          the k face: n1 n2 n3 n4
           |/           |/
          n1 ---------- n2

          i runs to the right, j runs away from you, k runs up

The element on line 49 of golden case 006 lists the corners 1, 2, 5, 4, 10, 11, 14 and 13. Node 2 is one step along i from node 1, node 4 one step along j, and node 10 one step along k, so the list follows the figure. An element whose list gives a negative volume is inverted, and shaping says how to read the warning.

The Exodus II format

The Exodus II writer needs a build with the netCDF Fortran library. james -v says whether the build has it. A build without it refuses an Exodus write with built without Exodus support, and the neutral writer works in both builds.

The layout was fixed from the Exodus II library’s own source and checked with ncdump. It has not been opened with another program’s Exodus reader. Read it beside tests/golden/006-merge-two-blocks/expected.exodus.txt, which is the ncdump text of the second file that golden case writes.

  • File. A netCDF classic file in the 64-bit-offset variant, the library’s large-model form, which stores the coordinates as three variables.
  • Global attributes. api_version and version, both the float 9.06. floating_point_word_size 8, file_size 1, maximum_name_length 32 and int64_status 0. title, the title of the write cut to 80 characters. A longer title raises a warning that it was cut, and a write with no title stores the empty title.
  • Dimensions. len_string 33, len_line 81, four 4, len_name 256, time_step unlimited with no record, num_dim 3, num_nodes, num_elem, num_el_blk and num_qa_rec 1. For block n, num_el_in_blk<n> and num_nod_per_el<n>, which is 8. A dimension whose length would be zero is left out with the variables that use it.
  • Variables. time_whole(time_step). coordx, coordy and coordz, each (num_nodes) of doubles. coor_names(num_dim, len_name), holding x, y and z. eb_status(num_el_blk), all 1. eb_prop1(num_el_blk), with the attribute name equal to ID, holding each block’s part number. eb_names(num_el_blk, len_name), empty. connect<n>(num_el_in_blk<n>, num_nod_per_el<n>) of 32-bit integers, with the attribute elem_type equal to HEX8. qa_records(num_qa_rec, four, len_string).
  • Blocks. One block for each part that has at least one active element, in part order, numbered from 1 in that order. Its elements are in the part’s own order, so an element’s position in the file is its James element number. A part with no active element has no block.
  • No maps. James writes no node map and no element map. A node and an element have the numbers of their positions.
  • QA record. It holds james, the version, an empty date and an empty time. The date is empty so that the same script gives the same bytes every time.

In the golden file, the two blocks of the two-block mesh read num_el_blk = 2, eb_prop1 = 1, 2 and 8 elements each, and qa_records reads "james", "0.3.1", "" and "".

What a write refuses and what it warns about

A write stops the run with an error, exit status 2 for an engine error, in these cases.

  • The parts the write covers hold more than 250,000,000 grid nodes, written or not. James counts them before it allocates anything. A part alone may hold at most 100,000,000.
  • The file cannot be opened, written or closed. The message names the file.
  • The format is Exodus II and the build has no writer.

A write outside the Merge phase (E1100) or with no format (E1101) is an error that James finds while it reads the script, and the run exits with status 1.

James does not refuse a non-finite coordinate in this version. A script that scales a part past the range of a real number, for example with two csca 1e200, can write NaN or Infinity and exit with status 0. Look at the extents of a mesh whose scale you doubt before you use the file. The milestone that edits the writers, M9, is to add the check.

James warns and goes on in these cases.

  • An element has zero volume after a merge: part #P has N elements with zero volume after merging.
  • A part has inverted elements: part #P has N elements with a negative Jacobian. The shaping chapter explains it.
  • A part has no active element, so none of its nodes is written without --write-unref-nodes.
  • A title is longer than 80 characters, and the Exodus II file holds its first 80.

The diagnostics reference lists every message.


James 0.3.1.

This site uses Just the Docs, a documentation theme for Jekyll.