Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 138

File Formats for Unstructured

Computational Meshes
Gambit Meshfile (*.msh) in 2- and 3-D

Laboratory for Product and Process Design


Director: Andreas A. Linninger

LPPD-Project Report

Authors: Argyris Politis,


Brian Sweetman,
and Andreas A. Linninger

Chicago, 4/11/2008

1
Update by Grant Hartung
Addition of
Chapter 1: mm Header Files for Mesh
Chapter 5: Three Dimensional Mesh (Hexahedrons)
Chapter 6: cs31/nwk Files
4/1/2016

Version 2

Update by Guoren Xu
Addition of
Chapter 10: Raw Image Formats
10/10/2016

Version 3

Updated by Grant Hartung


Addition of
Chapter 11: FLUENT Data Formats
2/24/2017

Version 4

Updated by Chang Sub Park, Grant Hartung,


Homa Rashidisabet, Claudia Vesel
10/25/2018

Version 5

2
Table of contents

1 Summary......................................................................................................................7
2 CHAPTER 1: mm Header Files for Mesh Files (.mm)...............................................8
3 CHAPTER 2: Two Dimensional Meshes (Quads)....................................................11
3.1 The Points Section: (0 "Dimension:").....................................................11
3.2 The Faces Section: (0 "Faces:")....................................................................12
3.3 The Cells Section: (0 "Cells:")......................................................................12
3.3.1 Interior with no subdivisions - Default-Interior.............................................12
3.3.2 Interior with several subdivisions..................................................................12
3.4 The Zones Section: (0 "Zones:")....................................................................13
3.4.1 Boundaries no subdivisions – default wall....................................................13
3.4.2 Implementation of Zones:..............................................................................14
3.4.3 Interior with several subdivisions..................................................................14
4 CHAPTER 3: Two Dimensional Mesh (Triangles)...................................................15
4.1 The Points Section: (0 "Dimension:")...................................................15
4.2..................................................................................................................................16
4.3 The Faces Section: (0 "Faces:")...............................................................16
4.4 The Cells Section: (0 "Cells:")...............................................................17
4.5 The Zones Section: (0 "Zones:").............................................................17
5 CHAPTER 4: Three Dimensional Mesh (Tetrahedrons)...........................................19
5.1 The Points Section: (0 "Dimension:").....................................................19
5.2 The Faces Section: (0 "Faces:")...............................................................19
5.3 The Cells Section: (0 "Cells:")................................................................21
5.4 The Zones Section: (0 "Zones:")..............................................................21
6 CHAPTER 5: Three Dimensional Mesh (Hexahedrons)...........................................23
6.1 The Points Section: (0 "Dimension:").....................................................24
6.2 The Faces Section: (0 "Faces:")...............................................................24
6.3 The Cells Section: (0 "Cells:")................................................................28
6.4 The Zones Section: (0 "Zones:")..............................................................28
7 CHAPTER 6: cs31/nwk Files....................................................................................29
7.1 The Case File Section: (.cs31).....................................................................29
7.2 The NWK File Section: (.nwk)......................................................................30
8 CHAPTER 7: – Neutral Files....................................................................................30

3
9 CHAPTER 8: – Nastran Files....................................................................................32
9.1 The NASTRAN File..........................................................................................33
10 CHAPTER 8 – Conversion of msh to Nastran..........................................................35
11 CHAPTER 9 – Extraction of JPEG, TIFF and STL Files.........................................36
12.......................................................................................................................................37
13 CHAPTER 10 – Raw Image Formats........................................................................38
13.1 DICOM..............................................................................................................38
13.2 JPEG..................................................................................................................39
13.3 TIFF...................................................................................................................39
13.4 PNG...................................................................................................................42
13.5 BMP...................................................................................................................42
14 CHAPTER 11 – FLUENT Data Formats..................................................................43
14.1 Mesh File Format...............................................................................................43
14.1.1 Mesh Converter.........................................................................................43
14.1.1.1 Step 1: Modify the Header.................................................................43
14.1.1.2 Step 2: Reading the point coordinate matrix.....................................44
14.1.1.3 Step 3: Reorder the face matrix.........................................................44
14.1.1.4 Step 4: Minimize the Zone Section...................................................44
14.2 Data File Format................................................................................................45
14.2.1 Writing a Walk-In Brain Case File From FLUENT..................................45
Summary..............................................................................................................45
Step 1:...................................................................................................................45
Step 2:...................................................................................................................45
Step 3:...................................................................................................................45
Step 4:...................................................................................................................46
Step 5:...................................................................................................................47
15 References..................................................................................................................48
16 Appendix....................................................................................................................48
16.1 CTRIA3 Format.................................................................................................53
16.2 CQUAD4 Format...............................................................................................54
17 Delphi converter application......................................................................................59
17.1 Summary............................................................................................................59
17.2 Main functions...................................................................................................60
17.2.1 Convert NWK Files to Splines..................................................................60

4
17.2.2 Convert CaseFiles To Splines....................................................................61
17.2.3 Convert Splined to Even2ptmesh..............................................................63
17.2.4 Editing and Movement..............................................................................65
17.2.5 CleanSplinedNW.......................................................................................66
17.3 Additional functions for visualization and statistics..........................................68
17.3.1 DisplayNWK.............................................................................................68
17.3.2 SplineNWKStatistics.................................................................................68
17.3.3 NWKStatistics...........................................................................................69
18 Converting among mesh and stl................................................................................69
18.1 Converter from mesh to stl................................................................................71
18.2 Case Study A: Tetrahedral Tube Mesh..............................................................72
18.3 Converter from stl to msh..................................................................................72
18.4 Walk through of viewer application for visualizing mesh files.........................73
19 Registration of two objects........................................................................................74
19.1 Walk through of viewer application for visualizing mesh files.........................75
20 Documentation of ICEM/GAMBIT mesh converter.................................................78
20.1 Converting ICEM mesh file to GAMBIT mesh file:.........................................79
20.2 Instructions for converting from GAMBIT to ICEM:.......................................81
21 Documentation of Delphi “.cs31” and “.casx” converter..........................................83
21.1 Converting .cs31 file to .casx file:.....................................................................84
21.2 Instructions for converting from .casx to .cs31:................................................87
22 Reading and Visualizing casx files in Matlab...........................................................89

Table of figures
Figure 1. The grid consisted of triangles as described in this report................................19
Figure 2. A tetrahedral volume of the mshFile example given herein..............................24
Figure 3.3: Volume numbering scheme for hexahedral meshes......................................29
Figure 4. ADINA mesh created by a Nastran file. The force is applied at node 5 (in
center). Element numbers are located in the center of each cell (in purple)......................34
Figure 5. A mesh extracted in VTK by applying the Delaunay Triangulation...........38
Figure 6. Converter software GUI.....................................................................................60
Figure 7. Accessing converter from the Viewer application.............................................60
Figure 8. Main tabs in the converter..................................................................................61
Figure 9. Procedure to convert 2-point networks to spline networks................................61
Figure 10. Conversion of 2-point network to spline network............................................62
Figure 11. Procedure to convert 2-point networks to spline networks..............................63
Figure 12. Two sample results of the linear fitting of diameter in each spline. The red plot
is the original spline network diameter and the green plot is the linear fit........................63
Figure 13. Conversion of 2-point case and network to spline case and network. The
original 2-point network is on the left and the spline network on the right.......................64

5
Figure 14. Procedure to convert spline networks to 2-point networks..............................65
Figure 15. Conversion of spline case and network to 2-point case and network. The
original spline network is on the left and the 2-point network is on the right. The face
(edge) indices are also represented in both networks........................................................65
Figure 16. Procedure to convert spline network point properties to 2-point format.........66
Figure 17. Conversion of spline network point property (pressure in this case) to 2-point
format. ...............................................................................................................................66
Figure 18. Procedure to convert 2-point networks to spline networks..............................67
Figure 19. Conversion of spline case and network to 2-point case and network. The
original spline network is on the left and the 2-point network is on the right. The face
(edge) indices are also represented in both networks........................................................67
Figure 20. Procedure to clean spline networks with dangling vessels..............................68
Figure 21. Removal of dangling splines. The original spline network is on the left and the
2-point network is on the right. The face (edge) indices are also represented in both
networks.............................................................................................................................69
Figure 22. Generating a simple mesh in ICEM. Make surface for generating mesh. (d).
Save it as ANSYS Fluent mesh or STL. (E). Simple mesh. (F). Coordinates shown on the
mesh. (G). STL format of same geometry. (H). Gambit format of same geometry. ........71
Figure 23. STL and mesh files comparison. (A). STL file of format for the shown
geometry in Figure 24. (B). Gambit file of same geometry..............................................72
Figure 26. Visualization of converted stl to mesh and original mesh in
ViewerApplication. (A). original sled mouse msh file. (B). Converted to STL and back to
msh. Note that the anatomical grouping is lost during the conversion process.................72
Figure 25. Visualization of converted mesh to stl and original stl in Cerebroview. (B)
Converted Mesh to stl. (C) original stl..............................................................................73
Figure 26. Visualization of converted stl to mesh and original mesh in
ViewerApplication. (A). original sled mouse msh file. (B). Converted msh ...................73
Figure 27. Viewer application form used for visualizing mesh file. (A). Red box shows
the bottom for loading msh file. (B). Visualizng the mesh in viewer application.............75
Figure 28. Registration form. (A). the general view of the registration form. (B). Red box
shows where user needs to insert four points of object one and two, and the bottom for
computing the transformation information. (C). Red boxes show the transformation
matrix, offset vector and eligned second object on object one. (D). visualization of the
eligned points.....................................................................................................................78
Figure 29. Applying transformation matrix on the network in viewer application. Two
misaligned objects, Object 2. The last picture is object two that is aligned with object1. 79
Figure 29. Original ICEM mesh file visualized in ICEM. This is a monkey spine. This
mesh was found amongst the data received from a collaborator. At the time, it was not
able to be loaded by any visualization tools or Delphi programs for processing, a perfect
case to be used within the confines of our converter.........................................................80
Figure 29. The ICEM mesh file has been converted to GAMBIT format and visualized
using ViewerApplication.exe.............................................................................................82
Figure 29. Visualization of the GAMBIT file generated using the converter in Section
22.1. This file is named M1V3.GAMBIT.msh and is located in
S:\__temp\grantConverters................................................................................................82

6
Figure 29. Visualization of the ICEM file generated from the coversion process. This file
is named M1V3.GAMBIT.icem.msh and is located in S:\__temp\grantConverters. Loaded
into ICEM using ImportMeshFromFluent.....................................................................84
Figure 29. Visualization of the original cs31 and nwk file created from grant’s artificial
network generation code (version 1), Project_KFGenAndSolvingApp.exe.......................85
Figure 29. Visualization of the .casx file after being converted from .cs31......................88
Figure 29. Visualization of the .casx file after being read by the casx file reader in Step 2.
...........................................................................................................................................89
Figure 29. Visualization of the converted file (V2) in viewer application........................90
Figure 30: Main workflow of the Matlab code..................................................................90
Figure 31: (A) Arteries and vein fused structure (B) Simple bifurcation..........................91
Figure 32: Matlab compiler used to convert an .m file into a stand-alone executable......91
Figure 33: Running the stand-alone executable from the command prompt.....................92

7
1 Summary

This report presents an overview of the structure of *.msh and Nastran (*.nas) files.
These files are extracted via the well-known mesh generator Gambit. The extracted *.msh
files are subsequently used for Fluent solver to perform CFD simulations. The main
features of the msh files are addressed herein. These are the information given about the
points, faces and volumes that are used for the development of computational domain. As
we will see in the following chapters, the *.msh files follow a specific format in both 2D
and 3D coordinates in order to be readable from Fluent or any other solver.
In Chapter 1, an overview of the mesh structure for two-dimensional grids formed by
quadrilaterals is given. In Chapter 2, an example of two-dimensional grid formed by
triangles is thoroughly discussed. The main features of three-dimensional meshes are
analyzed in Chapter 3. Along with this, we also address the differences between three-
and two dimensional meshes generated by Gambit. In Chapters 4 and 5, two type of files
used in Finite Element Methods, namely Neutral (*.neu) and nastran (*.nas) files are
described. Finally, we describe the use of a converter from *.msh to *.nas files and in
Chapter 7, an overview of how different type of image files (*jpeg, *tiff and *Stl) are
extracted through the VTK is given.
2 CHAPTER 1: mm Header Files for Mesh Files (.mm)

This report describes the format of mm header files *.mm which are supported by the
Gambit mesh format. Mm files are ASCII files that can be used to hold simulation
parameters and data applied to a mesh file. It will include imported simulation parameters
such as constants and boundary conditions. Once a mesh (2D or 3D) is generated in the
Gambit format, a header mm file is used to hold simulation output data correlating to the
mesh.

<model header="Two Dimensional Steady State Diffusion Problem"


author="Andreas A Linninger" date="11/13/2007" ProblemType="Diffusion"
Comments="Add user defined comments here...">
problemtype=Diffusion
.V1\PurePascalMGV78_Ian_PhD\Data\msh\grid2_2D_2096.GAMBIT.v2.msh

<model header= gives a header to display to the user and initiates the section on
simulation parameters.
problemtype= tells the simulation what type of problem to solve, in this case it is a
diffusion problem.

The mesh that is to be simulated on needs to be linked to the simulation. This is done in
the header file in a section entitled “meshfile” where a link to the mesh file is presented.
<meshfile>
meshfile=C:\andi\MG.V1\PurePascalMGV78_Ian_PhD\Data\msh\grid2_2D_2096.GAMBIT.
v2.msh
</meshfile>

<meshfile> opens the section linking the mesh file to the current simulation.
Meshfile= tells the simulation the full path and name of the file containing the mesh to
be simulated.
<meshfile> ends the mesh file section.

The next section gives information about the simulation parameters.


<constants>
D=1.8e-9
</constants>

<BoundaryConditions>
bc0=<SubSection VarId=1 ID=17 type=Dirichlet value=10/>
bc1=<SubSection VarId=1 ID=18 type=Dirichlet value=10/>
bc2=<SubSection VarId=1 ID=19 type=Dirichlet value=10/>
bc3=<SubSection VarId=1 ID=20 type=Dirichlet value=0/>
</BoundaryConditions>

<constants> initiates the section for simulation constants.


D=1.8e-9 tells the simulation that the simulation parameter “D” has a value of 1.8e-9.
</constants> ends the section delineating the simulation constants.
<BoundaryConditions> begins the section delineating the boundary conditions for the
simulation.
bc0= defines the boundary condition named “bc0.
SubSection tells the simulation that “bc0” does not apply to all faces but rather one group
of faces.
st
VarId=1 tells the simulation that “bc0” only applies to the 1 variable in the simulation.
ID=17 tells the simulation which face group which this boundary applies to, in this case it
is face group 17.
type=Dirichlet tells the simulation what type of boundary condition is to be used, in this
case the type is a Derichlet boundary condition.
value=10/> tells the simulation the value of the boundary condition.
</BoundaryConditions> ends the Boundary Conditions section.

The simulation output data is listed in the following section:


(vector=metabolism cell)
(N=324 type=variable)
(12 (0 1 144 0 0)(
2.0905428004
2.2701978308
2.448617957
2.6426070344
2.837910575
3.0007178796
3.2883297019
3.983310066
...
)

(vector=metabolism cell) The data in this section is set by the user to be “metabolism
cell” data.
(N=324 This section has 324 volumes and each volume is of type=variable) type,
meaning that it is not a constant input to the simulation but rather a calculated output of
the simulation.
(12 Volumes
(0 1 144 0 0)( Group “0” of volumes starting at volume “1” ending at volume “144” data
will follow.
2.0905428004 The value corresponding to the cell metabolism of volume 1 from the mesh file.
2.2701978308 The value corresponding to the cell metabolism of volume 2 from the mesh file.
2.448617957 The value corresponding to the cell metabolism of volume 3 from the mesh file.
2.6426070344 The value corresponding to the cell metabolism of volume 4 from the mesh file.
) Closes the vector of data.

After this line, another vector of data can begin (like “Pressure” for instance). If there is
more than one simulation variable, they can be listed after the previous simulation
variable. The second, third (and so forth) variable is listed in the exact same manner as
the first simulation vector:
(vector=pressure)
(N=324 type=variable)
(12 (0 1 144 0 0)(
...
)

(vector=pressure) The data in this section is set by the user to be “pressure” data.
(N=324 This section has 324 volumes and each volume is of type=variable) type,
meaning that it is not a constant input to the simulation but rather a calculated output of
the simulation.
(12 Volumes
(0 1 144 0 0)( Group “0” of volumes starting at volume “1” ending at volume “144” data
will follow.
... The value corresponding to the cell metabolism of the volumes in the mesh file.
) Closes the vector of data.

In the case that there are multiple time steps in a data section, the first vector cannot be a
time-variant variable. The time-variant variable (such as pressure simulated dynamically)
must begin with the second vector and has no limit to how many time points can be
saved. The header for the time data is as follows:
(vector=vorticity|1 cell)
(N=324 type=variable)
(12 (0 1 144 0 0)(
2.0905428004
2.2701978308
2.448617957
2.6426070344
2.837910575
3.0007178796
3.2883297019
3.983310066
...
)

(vector=vorticity|1 cell) The data in this section is set by the user to be “vorticity”
data for timepoint “1” and is designated by each “cell”. In this way, timepoint 2 would
read “(vector=vorticity|2 cell)”.
(N=324 This section has 324 volumes and each volume is of type=variable) type,
meaning that it is not a constant input to the simulation but rather a calculated output of
the simulation.
(12 Volumes
(0 1 144 0 0)( Group “0” of volumes starting at volume “1” ending at volume “144” data
will follow.
2.0905428004 The value corresponding to the cell metabolism of volume 1 from the mesh file.
2.2701978308 The value corresponding to the cell metabolism of volume 2 from the mesh file.
2.448617957 The value corresponding to the cell metabolism of volume 3 from the mesh file.
2.6426070344 The value corresponding to the cell metabolism of volume 4 from the mesh file.
) Closes the vector of data.

3 CHAPTER 2: Two Dimensional Meshes (Quads)

In this chapter, the format of a two-dimensional *.msh file consisted of quadrilaterals is


analytically described.

3.1 The Points Section: (0 "Dimension:")

In this part the numbers of points constitute the computational domain as well as their
coordinates is explicitly given.
(10 (0 1 9 1 2)) “(10 stands for points (0, 1: First index of points, 9: last index of
points, 1: x-coordinate, 2: y-coordinate))”

(10 (0 1 9 1 2)(
1.000000000e+00 1.000000000e+00
2.000000000e+00 0.000000000e+00

))

3.2 The Faces Section: (0 "Faces:")

In this part, the faces that in 2D are consisted of two points as well as information about
the cells that such faces belong are given (see Chapter 2 for more details)

3.3 The Cells Section: (0 "Cells:")

The cell sections specify the indices of cells belonging to interior domains. Each domain
must have contiguously numbered cell indices for this method to work (I guess Gambit
just makes sure that this is the case).

3.3.1 Interior with no subdivisions - Default-Interior


Meshfiles with no interior subdivision of the domain only have one entry:
The default-interior refers a collection of all the faces that do not belong to a boundary
specified by the user. It is the list of all faces that the code generator puts into the
FaceList.

Example:

(12 (0 1 4 0))

Strangely the index ‘0’ is used here, while in the zones section an ‘8’ or sometimes a ‘6’
is used. This requires further research. It appears to be always the last index of a zone or
boundary plus 2.

3.3.2 Interior with several subdivisions


If more subdivisions were defined, more entries with leading (12 (…)) occur.
The following example defines the domain “2” comprised of cells with index 1 until
index 2.
Example: (12 (2 1 2 1 3))

Zeroth integer:
A leading ‘(12’ is a constant specifier for all zones.

First Integer: ‘2’


The first index of the interior domains are labeled by an integer. We will call this the
InteriorDomainIdentifier.

Second and third Integer: ‘1 2’


The second and third integer of the interior domains give the starting and the ending
index of the InteriorDomain. Note again that interior domains must be numbered with
contiguous indices.

Fourth and fifth integer: ‘1 3))’


The last two integers appears to be 1 and 3 for all interior domains closed with ‘))’.

3.4 The Zones Section: (0 "Zones:")

The zones section specifies identifies for different groupings of cell divisions between
zones. Each grouping is associated with a particular integer that is also used in the face
definitions.

3.4.1 Boundaries no subdivisions – default wall


Meshfiles with no interior subdivision of the domain only have one entry:

Example:

(0 "Faces:")
(13(0 1 18 0))
(13(3 1 c 3 0)(
2 5 6 9 0

))

(45 (3 wall wall)())


The integer ‘3’ defines the boundary wall called wall. Note that the boundary identifier
also appears in the face section (both ‘3’ are marked in bold face for clarity).

3.4.2 Implementation of Zones:

In the lower sections of the file, there are two special sections:
The cells section (0 "Cells:") and the zones section (0 "Zones:")

Example from mg_case2.msh

…..
(0 "Cells:")
(12 (0 1 4 0))
(12 (2 1 2 1 3))
(12 (3 3 4 1 3))

(0 "Zones:")
(45 (2 fluid bottom)())
(45 (3 fluid top)())
(45 (4 wall wall3)())
(45 (5 wall wall2)())
(45 (6 wall wall1)())
(45 (8 interior default-interior)())

3.4.3 Interior with several subdivisions


If more subdivisions were defined, more entries with leading (45 (…)) occur.

The following example defines the


zone ‘2’ as a fluid named ‘bottom’ and
zone ‘3’ as a fluid with name ‘top’; and
the groupings for the boundaries with index ‘4’ a wall called ‘wall3’,
the groupings for the boundaries with index ‘5’ a wall called ‘wall2’,
the groupings for the boundaries with index ‘6’ a wall called ‘wall1’, and
the default interior with index ‘8’.

Example:

(0 "Cells:")
(12 (0 1 4 0))
(12 (2 1 2 1 3))
(12 (3 3 4 1 3))
(45 (2 fluid bottom)())
(45 (3 fluid top)())
(45 (4 wall wall3)())
(45 (5 wall wall2)())
(45 (6 wall wall1)())
(45 (8 interior default-interior)())

Zeroth integer:
A leading ‘(45’ is a constant specifier for all cells.

First Integer: ‘2’


The first index of the boundary domains are labeled by an integer. We will call this the
BoundaryGroupingIdentifier

Second and third string: ‘fluid bottom’


The second and third string of the cells section qualify an interior domains by type (e.g.
‘fluid’, ‘solid’) and by name (e.g. ‘bottom’, ‘top’, ‘wall3’, etc).

4 CHAPTER 3: Two Dimensional Mesh (Triangles)

In this chapter, the format of a two-dimensional *.msh file consisted of triangular faces is
analyzed. Such type of files can be exported by the commercial Mesh Generator Gambit.
These files are subsequently used by the well-known Solver, Fluent. We use Courier for
the Grid Structure and Times New Roman for the comments embedded in the file. The
following sections are discussed herein. Firstly, we analyze the Msh File format by
adding comments in order to explain the most important parts of the file. A two-
dimensional, unstructured grid, generated through Gambit, is given as an example for
subsequent analysis (Figure 1). Such grid is consisted of triangles that are formed by
three points joined together in a counterclockwise fashion. Finally we refer to the form of
the file that can be used directly in Fluent in order to instantiate a grid also shown in
Figure 1.

Header: Any changes made in this section, do not influence the reader in Fluent.

(0 "GAMBIT to Fluent File") All brackets starting with “0” do not affect the reader.

(0 "Dimension:") This is Header for “Dimension”


(2 2) Dimensions of the domain, here it is 2D
4.1 The Points Section: (0 "Dimension:")

In this part the numbers of points as well as their coordinates are explicitly given.

(10 (0 1 D 1 2)) “(10 stands for points (0, 1: First index of points, D: last index of
points, 1: x-coordinate, 2: y-coordinate))”

(10 (1 1 D 1 2)(
2.0000000000e+000 0.0000000000e+000
2.0000000000e+000 1.0000000000e+000
2.0000000000e+000 5.0000000000e-001
0.0000000000e+000 0.0000000000e+000
6.6666666667e-001 0.0000000000e+000
1.3333333333e+000 0.0000000000e+000
0.0000000000e+000 1.0000000000e+000
1.3333333333e+000 1.0000000000e+000
6.6666666667e-001 1.0000000000e+000
0.0000000000e+000 5.0000000000e-001
3.9306883226e-001 5.0000000272e-001
1.0000001220e+000 5.0000000017e-001
1.6069312862e+000 5.0000000119e-001
))

4.2

4.3 The Faces Section: (0 "Faces:")

In this part, the faces that in 2D are consisted of two points as well as information about
the cells that such faces belong is given.

(0 "Faces:") This is a Header


(13 (0 1 1a 0)) “(13: stands for faces (0, 1: First index of faces, 1a: last index of
faces, 0))”
(13 (3 1 a 3 0)(
“(13 stands for faces (3: wall-boundary faces, 1: first index of boundary faces, a: last
index of boundary faces, 3, 0))”
2 4 5 2 0
“(2: number of points consist a face, 4: first point, 5: second point, 2: first volume that
face belongs, 0: second volume, “0” stands for boundary)”
the same for other boundary faces shown below.
2 5 6 d 0
2 6 1 8 0
2 1 3 7 0
2 3 2 6 0
2 2 8 5 0
2 8 9 a 0
2 9 7 3 0
2 7 a 4 0
2 a 4 1 0
))

(13 (5 b 1a 2 0)( “(13 stands for faces ( 5: interior faces, b: first index of interior
faces, 1a: last index of interior faces, 2, 0))
2 4 b 1 2
“(2: number of points consist a face, 4: first point, b: second point, 1: first volume, 2:
second volume)
In the same fashion, we read the other interior faces shown below…
2 b a 1 4
2 5 b 2 e
2 7 b 3 4
2 b 9 3 c
2 8 d 5 9
2 d 2 5 6
2 d 3 6 7
2 d 1 7 8
2 d 6 8 b
2 8 c 9 a
2 c d 9 b
2 9 c a c
2 c 6 b d
2 b c c e
2 c 5 d e
))

4.4 The Cells Section: (0 "Cells:")

In this section, information of the total number of cells in the domain is given.
(0 "Cells:") Header
(12 (0 1 e 0)) “(12 stands for cells (0, 1: first index of cells, e: last index of cells, 0))
(12 (2 1 e 1 1))

4.5 The Zones Section: (0 "Zones:")

(0 "Zones:") Header
(45 (2 fluid fluid)()) (45 stands for boundary conditions or interior (2: interior,
fluid:fluid or solid: here is fluid, fluid: the name is fluid))
(45 (3 wall wall)()) “(45 stands for boundary conditions or interior (3: wall, wall: it
is a wall, wall: the name is wall))
(45 (5 interior default-interior)()) (45 stands for zones (5 is interior and
describes the volumes)
By performing the above analysis, we get information about the way that a Gambit msh
File is constructed. The point coordinates are given in the first section. The faces,
consisted of two points, are distinguished into boundary and interior faces. The numbers
of volume cells (in this case are consisted of three faces) are extracted by first and second
volume numbers that are seen in Faces section. It should also be noted that the volume
cells are always constructed following counter-clockwise direction, i.e. the cell 10 (V10)
in Figure 1 is given by the points 8, 9, 12.

F-8 9 F-7 8 F-6


7 2

V3 V10 V5
F-9 F-14 F-23 F-16 F-17 F-5
F-15 F-21 V9
V4 V12 V6
11 12 13
10 F-25 F-22 F-18 3
F-12
V14 V11
V1 F-26 V7
F-13 F-20 F-4
F-10 F-11 F-24 F-19
V2 V13 V8

4
F-1 5 F-2 6 F-3 1
Figure 1. The grid consisted of triangles as described in this report.

Note: The numbers in the Gambit *.msh File is written in hexadecimal notation.
This is because hexadecimal numbers request less space than decimal.
5 CHAPTER 4: Three Dimensional Mesh (Tetrahedrons)

In this chapter, the format of a three-dimensional *.msh file consisted of tetrahedral


volumes is discussed. The format of three-dimensional msh Files is similar with those for
two dimensions analyzed in the previous chapters. However, some distinct differences
are elaborated in this chapter. Such an msh File is constituted of four main parts, namely
“Dimensions:”, “Faces”, “Cells” and “Zones”.
Before these parts of the file, a header is shown, (0 "GAMBIT to Fluent File"),
which informs us that this file was extracted from Gambit to be used as input for Fluent
solver.

5.1 The Points Section: (0 "Dimension:")

In this part, the dimensions of the domain, the total number of points as well as the point
coordinates are given. The “Dimensions” part begins with the header (0
"Dimension:"). In the next line the type of medium used (solid or fluid) as well as the
dimensions of the domain are given, (2 3). The number “2” stands for fluid as
continuum and “3” for the dimensions of the domain (3D). This line is used as an
identifier of the dimensionality of msh File. It is important to note that in three-
dimensions the second digit in this line is 3, while in two-dimensional domains it is given
as 2. The total number of points and the number of coordinates to where these refer is
given in the next line:
(10 (0 1 9 1 3))
The points numbering starts from “1” and ends at “9” and three space coordinates (x, y,
z) shown as “1 3” are subsequently given. The point coordinates are given in following
lines starting from “1” as:

(10 (1 1 9 1 3)(
5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
3.3882497385e-003 -3.2219810665e-006 4.9988601721e-004
))

The first column refers to x-coordinates while the second and third column give the y-
and z-coordinates of the points, respectively.
5.2 The Faces Section: (0 "Faces:")

This section describes the connectivity of faces (i.e. which points are connected to form a
face), the type of faces (i.e. interior or exterior) as well as the volumes in which such
faces are belong to. The section begins with the header (0 "Faces:"). The next line
(13 (0 1 1e 0)) gives information for the total number of faces used in the domain.
In this line, “1” stands for the first index of faces and “1e” for tha last index. Thus, the
total number of faces in this particular domain is “1e”. The header “13” refers to faces. In
the following lines, the faces are subdivided into different parts, each of those references
the various boundary sections created by the user of Gambit.
The first subdivision, in this particular example is as follows:

(13 (3 1 2 3 0)(
3 6 1 8 3 0
3 4 1 6 4 0
))

Where the first line is analyzed as (13 : stands for faces (3: boundary given by the user
1: first index of faces 2: second index of faces 3: wall-boundary type 0)(
It should be noted that the “boundary given by the user” argument is read in conjuction
with the last section of mshFile, namely “Zones”.
In the next line, the number and the indices of points forming a face, as well as the
volume numbers to which these faces are adjusted, can be seen. For example,
3 6 1 8 3 0
3: stands for the number of points forming a face
6: First Point Index
1: Second Point Index
8: Third Point Index
3: First Volume Index
0: Second Volume Index (0 stands for boundary)
In the same fashion, the reader identifies the subsequent subdivisions of boundary faces.

After the boundary subdivisions, the interior faces are specified. The header (13 (a d
1e 2 0)( is analyzed as follows:
13: stands for faces
a : stands for interior (see zones section)
d : first index of interior faces
1e: last index of interior faces
2 : default-interior

In the following lines the same information discussed for the aforementioned boundary
faces are given. For example, the next line is:
3 8 9 7 2 1
where:
3: stands for total number of points
8: First Index of Points
9: Second Index of Points
7: Third Index of Points
2: First Index of Volumes
1: Second Index of Volumes

It should be noted that in two-dimensions the first argument is “2” followed by just two
point indices. This is because in 2D a face is formed from just two points while in 3D a
face (or surface) is consisted from three different points.
Likewise, we read the subsequent lines below.

5.3 The Cells Section: (0 "Cells:")

In this section, the total number of cells of the domain are given. The following lines are
seen:

(0 "Cells:")
(12 (0 1 c 0))
(12 (2 1 c 1 2))

The first line is a header.


In the second line:
12: stands for cells
1: First Index of Volumes
c: Second Index of Volumes

5.4 The Zones Section: (0 "Zones:")

In this section, the type and the name of continuum, the types of boundary as well as the
interior conditions are specified. In this particular example, the last section of the mshFile
is as follows:
(0 "Zones:")
(45 (2 fluid fluid)())
(45 (3 wall w6)())
(45 (4 wall w5)())
(45 (5 wall w4)())
(45 (6 wall w3)())
(45 (7 wall w2)())
(45 (8 wall wall1)())
(45 (10 interior default-interior)())

In the first line, a header is shown. The next lines are referred to the types and names of
boundary conditions created by the user.

(45 (3 wall w6)())


45: stands for zones, 3: section 3 (see faces section) wall: type of BCs w6: name of BC
(45 (4 wall w5)())
(45 (5 wall w4)())
(45 (6 wall w3)())
(45 (7 wall w2)())
(45 (8 wall wall1)())

In the last line the interior conditions are shown:


(45 (10 interior default-interior)())
45: stands for zones
10: stands for interior
Interior: name
Default-interior : interior faces

In Figure 2 the first volume (1) of the domain described above is depicted.
2

8
Figure 2. A tetrahedral volume of the mshFile example given herein.

From the analysis of the mshFile, it is obvious that the first tetrahedron is consisted of the
following four faces:

Face 1: (7 8 2) see line “3 7 8 2 1 0” in boundary face section of mshFile


Face 2: (8 9 7) see line “3 8 9 7 2 1” in interior face section of mshFile
Face 3: (9 8 2) see line “3 9 8 2 9 1” in interior face section of mshFile
Face 4: (7 9 2) see line “3 7 9 2 c 1” in interior face section of mshFile

From the above lines, we also retrieve the information that faces 2, 3 and 4 of Volume 1
are also adjacent to volumes 2, 9 and c, respectively. It means that in a computational
domain consisted of tetrahedrons (four faces in total), there are interior faces (belongs
also to adjacent volumes) and boundary faces. The number of the boundary and interior
faces depends on the position of tetrahedron in the domain.

Note that the numbers in *.msh File are written in hexadecimal notation.

6 CHAPTER 5: Three Dimensional Mesh (Hexahedrons)


In this chapter, the format of a three-dimensional *.msh file consisted of hexahedral
volumes is discussed. The format of hexahedral msh Files is similar for tetrahedrals.
However, some distinct differences are elaborated in this chapter. This type of msh File is
constituted of four main parts, namely “Dimensions:”, “Faces”, “Cells” and
“Zones”.
Before these parts of the file, a header is shown, (0 "GAMBIT to Fluent File"),
which informs us that this file was extracted from Gambit to be used as input for Fluent
solver.

6.1 The Points Section: (0 "Dimension:")

In this part, the dimensions of the domain, the total number of points as well as the point
coordinates are given. The “Dimensions” part begins with the header (0
"Dimension:"). In the next line the type of medium used (solid or fluid) as well as the
dimensions of the domain are given, (2 3). The number “2” stands for fluid as
continuum and “3” for the dimensions of the domain (3D). This line is used as an
identifier of the dimensionality of msh File. It is important to note that in three-
dimensions the second digit in this line is 3, while in two-dimensional domains it is given
as 2. The total number of points and the number of coordinates to where these refer is
given in the next line:
(10 (0 1 9 1 3))
The points numbering starts from “1” and ends at “9” and three space coordinates (x, y,
z) shown as “1 3” are subsequently given. The point coordinates are given in following
lines starting from “1” as:

(2 3)

(10 (1 1 9 1 3)(
5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
3.3882497385e-003 -3.2219810665e-006 4.9988601721e-004
))

The first column refers to x-coordinates while the second and third column give the y-
and z-coordinates of the points, respectively.

6.2 The Faces Section: (0 "Faces:")


This section describes the connectivity of faces (i.e. which points are connected to form a
face), the type of faces (i.e. interior or exterior) as well as the volumes to which said faces
belong. For the correct formatting of a hexahedral mesh structure the face matrix must
follow a specific ordering scheme. A sample of a correct face section can be seen in
below.

(13 (3 1 6 0 0)(
4 0 1 2 3 1 0 //(Face 1)
4 4 5 6 7 1 0 //(Face 2)
4 4 0 1 5 1 0 //(Face 3)
4 4 7 3 0 1 0 //(Face 4)
4 1 2 6 5 1 0 //(Face 5)
4 2 6 7 3 1 0 //(Face 6)
))

Where the first line is analyzed as (13 : stands for faces (3: boundary given by the user
1: first index of faces (index of first face in group) 6: second index of faces (index of last face
in group) 0 0)(. It should be noted that the “boundary given by the user” argument
is read in conjunction with the last section of msh file, namely “Zones”. This number
represents the corresponding zone in the “Zones” section.

The point ordering in a face matrix for a hexahedral mesh are as follows. The first face
from is comprised of points 0, 1, 2 and 3 (Figure 5.1). The points must be listed in a
counter-clockwise fashion. The second face must begin with the point opposing the first
point from the first face (point 4 in this case as it opposes point 0). The second face must
be written in the same direction as the first face (in this case the point order needs to be 4-
5-6-7). The rest of the faces will be listed in the order shown in Figure 5.2.

3 7 s

2
4 s

0 5 5 a

1
Figure 5.1: An example hexahedron to be generated in GAMBIT mesh format. The
numbers correspond to the point numbering scheme in the sample code above.

In the next line, the number and the indices of points forming a face, as well as the
volume numbers to which these faces are adjusted, can be seen. For example,
4 0 1 2 3 1 0
4: stands for the number of points forming a face
0: First Point Index
1: Second Point Index
2: Third Point Index
3: Fourth Point Index
1: First Volume Index
0: Second Volume Index (0 stands for exterior boundary)

2
4

1 5

3
Figure 5.2: An example hexahedron to be generated in GAMBIT mesh format below.
The numbers correspond to the face ordering scheme.

In the same fashion, the reader identifies the subsequent subdivisions of boundary faces.
The face format of a hexahedral structure must follow that the first face is a face of the
user’s choice. The second face needs to be the opposing face.

After the exterior faces section, the interior faces are specified. The header (13 (a d 1e
2 0)( is analyzed as follows:
13: stands for faces
a : stands for interior (see zones section)
d : first index of interior faces
1e: last index of interior faces
2 : default-interior
0)(

In the following lines the same information discussed for the aforementioned interior
faces are given. For example, the hexahedral mesh from Figure 5.3 can be written as:
4 8 9 6 7 2 1

where:
4: stands for total number of points
8: First Index of Points
9: Second Index of Points
6: Third Index of Points
7: Fourth Index of Points
2: First Index of Volumes
1: Second Index of Volumes
8 s Volume 1

2 . 7
Volume 2
1 d
9

33s . 6

0 .
Figure 3.3: Volume numbering scheme for hexahedral meshes.

The correct face matrix for the object in Figure 3 can be seen below.
(0 “Faces:”) //(Faces section)
(13(0 1 B 0)) //(All faces (faces 1-B)
(13(1 1 8 0 0)( //(first face group, Exterior Faces, faces 1-10)
4 0 1 2 3 1 0 //(Face 1)
. //(Face 2)
. //(Face 3)
. //(Face 4)
))
(13(1 1 6 0 0)( //(second face group, Interior Faces, Face 9)
4 6 7 8 9 1 2
))

6.3 The Cells Section: (0 "Cells:")

In this section, the total number of cells of the domain are given. The following lines are
seen:

(0 "Cells:")
(12 (0 1 c 0))
(12 (2 1 c 1 2))

The first line is a header.


In the second line:
12: stands for cells
1: First Index of Volumes
c: Second Index of Volumes

6.4 The Zones Section: (0 "Zones:")


In this section, the type and the name of continuum, the types of boundary as well as the
interior conditions are specified. In this particular example, the last section of the mshFile
is as follows:

(0 "Zones:")
(45 (2 fluid fluid)())
(45 (3 wall w6)())
(45 (4 wall w5)())
(45 (5 wall w4)())
(45 (6 wall w3)())
(45 (7 wall w2)())
(45 (8 wall wall1)())
(45 (10 interior default-interior)())

In the first line, a header is shown. The next lines are referred to the types and names of
boundary conditions created by the user.

(45 (3 wall w6)()) 45: stands for zones, 3: section 3 (see faces section) wall: type of
BCs w6: name of BC
(45 (4 wall w5)())
(45 (5 wall w4)())
(45 (6 wall w3)())
(45 (7 wall w2)())
(45 (8 wall wall1)())

In the last line the interior conditions are shown:


(45 (10 interior default-interior)())
45: stands for zones
10: stands for interior
Interior: name
Default-interior : interior faces

7 CHAPTER 6: cs31/nwk Files

7.1 The Case File Section: (.cs31)


Case files (.cs31) are formatted the same as .mm files. The major difference is that a case
file works with a network (.nwk) file as opposed to a mesh file (.msh). This means that a
case file MUST include a data vector entitled “Dia” which contains the diameter
information corresponding to the network file associated with it. Without this vector, the
associated network file cannot be simulated or visualized.
7.2 The NWK File Section: (.nwk)
NWK files (.nwk) are a specific type of msh file that is 1-dimensional. NWK files are
built as 2-point faces (lines) and diameter information. The diameter information is read
from a .cs31 file and applied to each point which acts like a volume. The structure of a
NWK file is otherwise the exact same as the msh file.

8 CHAPTER 7: – Neutral Files

This report describes the format of neutral files *.neu which are supported by the Gambit
mesh generator. The neutral files are ASCII type files that can be used to import or export
mesh data, including mesh point coordinates, cell-connectivity information and boundary
condition data. Once the mesh (2D or 3D) is generated through Gambit, from the Solver
(Command Tools) the option Generic is selected. Then the mesh is exported through the
option Export > Mesh under the File Bar. The exported file is seen as *.neu at the end
(i.e. Filename.neu).
The following sections describe the format of a two-dimensional Gambit Neutral File.
The first line : [CONTROL INFO 2.2.30] provides information about the version of
Gambit used to generate the *.neu file. It follows the Header for Neutral Files: [**
GAMBIT NEUTRAL FILE]
The following lines are also given information about the Program’s Version, the date and
the dimensions of the grid.

default_id2520
PROGRAM: Gambit VERSION: 2.2.30
Jan 2008
NUMNP NELEM NGRPS NBSETS NDFCD NDFVL
18 22 1 0 2 2

The NUMNP stands for the total number of nodal points in the mesh.
The NELEM stands for the total number of elements.
The NGRPS stands for the number of element groups.
The NBSETS stands for the number of boundary conditions set.
The NDFCD stands for the number of coordinates
The NDFVL stands for the number of velocity components.
The ENDOFSECTION string signifies the end of introductory section.

The next section gives information about the nodal point coordinates in the mesh.
NODAL COORDINATES 2.2.30 Header
1: First Point 0.00000000000e+000: x-coord. 4.00000000000e+000: y-coord.
1 0.00000000000e+000 4.00000000000e+000
2 0.00000000000e+000 0.00000000000e+000
3 0.00000000000e+000 2.66666666667e+000
4 0.00000000000e+000 1.33333333333e+000
5 3.00000000000e+000 0.00000000000e+000
6 1.00000000000e+000 0.00000000000e+000
7 2.00000000000e+000 0.00000000000e+000
8 3.00000000000e+000 4.00000000000e+000
9 3.00000000000e+000 1.33333333333e+000
10 3.00000000000e+000 2.66666666667e+000
11 2.00000000000e+000 4.00000000000e+000
12 1.00000000000e+000 4.00000000000e+000
13 7.66518491352e-001 3.19604214921e+000
14 1.96341923329e+000 1.85175317966e+000
15 1.84167491184e+000 3.11659564726e+000
16 9.76065872390e-001 1.00453448902e+000
17 1.99571175745e+000 7.10540275021e-001
18 9.40906865090e-001 2.17994810933e+000
ENDOFSECTION Header

The cells connectivity of the elements in the mesh is given in the following section:

ELEMENTS/CELLS 2.2.30 Header


1 3 3 12 1 13

1 First Cell 3 Element Type Geometry (3 for triangles) 3 Number of points consist the
first Cell element 12 First Nodal Point 1 Second Nodal Point 13 Third Nodal Point
The same for the subsequent elements/cells below:

1 3 3 12 1 13
2 3 3 13 1 3
3 3 3 8 11 15
4 3 3 8 15 10
5 3 3 4 2 16
6 3 3 16 2 6
7 3 3 16 6 17
8 3 3 17 6 7
9 3 3 5 9 17
10 3 3 5 17 7
11 3 3 15 11 12
12 3 3 4 16 18
13 3 3 4 18 3
14 3 3 17 9 14
15 3 3 14 9 10
16 3 3 3 18 13
17 3 3 12 13 15
18 3 3 10 15 14
19 3 3 16 17 14
20 3 3 16 14 18
21 3 3 18 14 15
22 3 3 15 13 18 Last Element/Cell
ENDOFSECTION Header

The last section described below provides general information of the mesh

GROUP: 1 ELEMENTS: 22 MATERIAL: 2 NFLAGS:


1

GROUP Total number of element groups (1 here)


:
ELEMENTS: Total Number of element/Cells (22 here)
MATERIAL: Type of Material (2 stands for fluid)
NFLAGS : Number of Solver-Dependent Flags

fluid
0
1 2 3 4 5 6 7 8 9
10
11 12 13 14 15 16 17 18 19
20
21 22
ENDOFSECTION Header

9 CHAPTER 8: – Nastran Files

This section analyzes the format of nastran files imported by the commercial FEM (finite
element method) software package, ADINA. We analyze the *.nas file format, noting its
structure and logic.
The *.nas file is a common file imported into finite element solvers. We will analyze a
two-dimensional, structured grid, plane strain model generated in ADINA. The grid
consists of uniformly sized quadrilaterals.
The NASTRAN file format contains mesh information and can be directly imported into
ADINA maintaining the mesh generated from a different program. Parasolid and IGES
file formats contain only geometric information, not mesh information. ADINA does not
import files of type *.msh generated by GAMBIT, which contains geometric and
meshing information.

9.1 The NASTRAN File


(This information is based on:
http://www.mscsoftware.com/support/online_ex/Scenario/Manual/man2.pdf)
Figure 3 shows a mesh generated in ADINA using the nastran file given in Box 1.

Figure 4. ADINA mesh created by a Nastran file. The force is applied at node 5 (in
center). Element numbers are located in the center of each cell (in purple).
SOL 101 (analysis type)
CEND
$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$*
$* CASE CONTROL
$*
$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
SPC=10 (constraint)
LOAD=20 (loading condition; matches Force below)
DISP=ALL (print displacement of all nodes)
$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$*
$* BULK DATA
$*
$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
BEGIN BULK
GRID,1,,0.,0.,0.
GRID: 1-9=node numbers; i, j, k=nodal
GRID,2,,5.,0.,0. coordinates
GRID,3,,10.,0.,0. CQUAD: 4 node quadrilateral elements;
GRID,4,,0.,5.,0. 10=EGrp # 1-4=element numbers;
GRID,5,,5.,5.,0.
GRID,6,,10.,5.,0.
GRID,7,,0.,10.,0.
GRID,8,,5.,10.,0.
PSHELL: 10=group number;
GRID,9,,10.,10.,0.
CQUAD4,1,10,1,2,5,4 30=material number; 0.25=shell
CQUAD4,2,10,2,3,6,5 thickness, 30=material number
CQUAD4,3,10,4,5,8,7 MAT1: homogenous & isotropic;
CQUAD4,4,10,5,6,9,8 30=material number; 3.E7=value of
Young’s modulus; 0.33=Poisson Ratio;
$define element properties (thickness : 0.25)
PSHELL,10,30,0.25,30 2=density; 3=coefThermExp

$define material properties (Ymod=3e7Pa, PR=0.33)


MAT1,30,3.E7,,0.33,2,3
FORCE: 20 (number matches LOAD above); 5=node
$define loads number to which force is applied; -1000=force
FORCE,20,5,,-1000.,0.,0.,1.
magnitude; 0,0,1 (direction of force is in z direction)
SPC1: body is fixed in all directions at these nodes (1-
$define the constraints
SPC1,10,123456,1,2,3,4,6,7,+AA1 4, 6-9). Node 5 is free because this is where the force
+AA1,8,9 is applied.

35
$the end of input file
ENDDATA
Box 1. Sample Nastran file

We note that the first line of text in the Nastran file is SOL. Box 2 shows some available
commands for different types of analyses.

SOL Type of Analysis


101 Linear Statics
106 Nonlinear Statics
129 Nonlinear Transient Response
153 Steady Nonlinear Heat Transfer
159 Transient Heat Transfer
600 Non-Linear Static and Dynamic (implicit)
601 Implicit Non-Linear (Adina)
701 Explicit Non-Linear (Adina)
Box 2. Sample SOL definitions for various types of analysis

More information regarding the construct of the Nastran file can be found in the appendix.

10 CHAPTER 8 – Conversion of msh to Nastran


The conversion of a gambit generated .msh file can be converted to a nastran file using a
compiled Matlab code that was developed in our lab. The procedure for converting a gambit
mech file to a nastran file is as follows:

1. Double click on “adina_conv_quads.exe”


2. Choose the mesh (.msh) file you want to convert. Click open
3. Save as *.nas file

The generated .nas file is ready to be imported into ADINA as follows.

1. Enter the ADINA AUI


2. Click File -> Import nastran

The code is given in the Appendix.

8.1 Conversion of .Msh Fie to Nastran File.

36
In this chapter, the conversion of mesh file from .msh to Nastran file is display. Firstly, it will be
helpful to know that the structure of nastran file is simple. The major categories that a Nastran
file consist are the points and the faces connections. The points are in the format of X-, Y-, and
Z- Coordinates. However, there are key words needed to form the face connection in Nastran
file. For instance, if the face category in the .msh file which is usually denoted by “13” has three
(3) points of connection, to convert the face to Nastran format, it will require the keyword
“CTRIA3”. If it is four (4), it will require the keyword “CQUAD4” as explained below. Because
we are aware that there are usually a lot of point and face grids in a mesh file, we have developed
a MATLAB code to ensure that this conversion is done easily. This section showcase the
MATLAB code for the conversion of .msh to Nastran file and its usage.
8.2 MATLAB code for the Conversion of .msh to Nastran File.
8.2.1 Conversion of .msh points to Nastran Points
Consider the points coordinates below, to convert these coordinates to Nastran points

(2 3)

(10 (1 1 9 1 3)(
5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
3.3882497385e-003 -3.2219810665e-006 4.9988601721e-004
))

coordinates, the format is explained in Chapter 8 of this report. This can be achieved by
following the steps below:
Step 1: Open a new notepad++ file and name it Human_Spine_Points (If you are working on a
Human Spine Mesh). Note: Feel free to give the file any name.
Step 2: Open the .msh file to be converted.
Step 3: Extract the points coordinates from the .msh file to be converted as shown

37
Note: You only need to copy the x-,y- and z- coordinates in the .msh file.
Step 4: Paste the copied coordinates to the Human_Spine_Points notepad++ file and save it by
pressing Ctrl + S
Step 5: Open the MATLAB application and paste the code below in the MATLAB Editor.

38
Note: This code is presented at the appendix.

Step 6: Change the file name at line 2 of the code from ‘Human_New_Vol’ to the file name of
the notepad++ (in this case, ‘Human_Spine_Points.txt’)
Step 7: Name the points coordinates of the Nastran file you are about to write at line 7 by
changing the ‘Human_New_Vol_Points.nas’ to say ‘Human_Spine_Points.nas’
Step 8: Click the ‘Run’ button on the MATLAB.
This will write a new Nastran file for the points which will be in form as shown below:

39
40
8.2.2 Conversion of .msh faces coordinates to Nastran faces coordinates.

Before we proceed with the demonstration of the conversion, it must be noted that:
1. The faces coordinates in .msh file are usually written in hexadecimal. There is a
need to convert it to decimal (This is not an issue though as the code can take
care of the conversion)
2. The format for the faces coordinate in ICEM is different from that of GAMBIT.
Before conversion is done, there is a need to know the source of the file to be
converted.
3. The faces are also in groups

To convert the faces coordinates of .msh to .nas, follow this steps:

Step 1: Search for the number of groups the faces are arranged in the .msh file. This can be done
by pressing Ctrl + F. Then type ‘(13’, then click ‘Find All in Current Document’. This will
display all the group categories in the file as shown below:

Step 2: Count the number of points that make a face. In this case, 3 points make a face
Step 3: Open a new notepad++ and name it ‘Group 7’. Note: You are free to name the group as
desire.
Step 4: Copy all the coordinates under this group and paste it in the new notepad++ file you just
named.

41
Note: Only copy the coordinates as shown above.

Step 5: Open the MATLAB application and paste the code below on the editor

42
Step 6: Change the file name on line 2 to ‘Group 7.txt’. Also change the name of the file to be
written to ‘Group_7.nas’ or as desire.

Step 7: Check the number of columns the file to be converted has. In this case, the file has five
columns. That is why we have matrix a-e in line 9-13. If the number of columns is more than
five, you can uncomment the matrix g and h as required.

Step 8: Check the number of points that make a face. In our case, we have three (3) points that
make a face. That is why we have like 21 of the code active. If we have four (4) points, we
uncomment line 20 and comment out line 21.

Step 9: Change the ‘10’ on line 21 after %d to the group number you are working on. The same
is applicable to line 20 if in use.

Step 10: Change the ‘Start_Connection_Number’ to the first number of the face in each group.
Since we are starting with the first group which is group 7, this variable takes 1. Let’s say the
group 7 stops at the face number 12500, to do for group 8, the variable will take 12501.

Step 11: Click ‘Run’ button on MATLAB to write the .nas file.

43
Step 11: Repeat the steps for other groups on different notepad++

Note: The code is given in the Appendix.

44
Now that we have converted all the points and faces to .nas file, we need to combine the points
together.

8.2.3 Combining Points and Faces to Form a Nastran file

Step 1: Open a notepad++ and name it ‘Human_Spine’. (Again, fell free to name it as you desire.
Step 2: On the file, write BEGIN BULK follow by $ Grid data section on the next line.
Step 3: Copy all the converted points coordinates and paste them below the $Grid data line
Step 4: Copy all the converted faces by groups and paste them on the file starting from the line
after the line where the points coordinates end. Add other converted faces in all the groups to the
new file.
Step 5: After all the points and faces has been added to the ‘Human_Spine’ file, type
‘ENDDATA’ at the end of the file.
Step 6: Save the file by pressing Ctrl + S and change the saving format to .nas.

Now you have your .msh file converted to .nas file.

It must be noted that that there are other categories that are present in Nastran file other than
points and faces (As explained in chapter 8), however these categories are not necessarily needed
to read .nas file.

45
CHAPTER 9 – Extraction of JPEG, TIFF and STL Files

The computational grids developed by Gambit mesh generator are read and subsequently
visualized using a code, written in Python, that was developed in our lab. The computational
meshes visualized in Visualization Tool Kit (VTK) are transformed into images (JPEG or TIFF)
and/or STL files by using the vtkWriter class
(http://www.vtk.org/doc/release/4.2/html/classvtkWriter.html). For the extraction of JPEG
images the following class is used: vtk.vtkJPEGWriter(), for TIFF images, the
vtk.vtkTIFFWriter() and for STL files, the vtk.vtkSTLWriter(). The code for the extraction of
STL files is given below:

w2image = vtk.vtkWindowToImageFilter()
writer = vtk.vtkSTLWriter()
w2image.SetInput(renWin)
w2image.Update()
writer.SetInputConnection(w2image.GetOutputPort())
writer.SetFileName("gam_2.stl") # extract stl file
renWin.Render()
writer.Write()

In the above code, the vtk.vtkWindowToImageFilter() class is used to transform the data
structure into image and subsequently the vtk.vtkSTLWriter() class takes as input the
“WindowToImageFilter” to extract (output) the STL file.

An example of this process is given in the Appendix describes the creation of grid with Delaunay
triangulation. The jpeg image extracted is shown in Figure 4.

46
Figure 5. A mesh extracted in VTK by applying the Delaunay Triangulation

CHAPTER 10 – Raw Image Formats

10.1 DICOM
File extension: .dcm or none

IMPORTANT: A DICOM stack is not only one file, but a collection of files, one for each
image in the stack.

DICOM files are the most common datatype for image stacks in this lab. They are common
because DICOMs are one of the only data types that allows for tags which contain useful
information about the image stack such as the dimensions of each voxel, the name of the patient,
and the data structure used to house the actual color data. DICOM files can be much smaller than
other imaging file formats because it is possible for DICOMs to hold only 8 or 16 bits of data for
each pixel, compared to 24 or 32 which is typical for BMP or PNG images.

Since DICOM stacks contain multiple files, it is possible that the actual names of the file does
not provide any information on how the stack is supposed to be arranged. Do not assume that just
because “1.dcm” is more “on top” of the list means that it is supposed to be positioned on top of
“2.dcm”. The information on how to arrange the stack is given in a tag in the DICOM tag section
of the file. This section is readable with most DICOM readers. An open-source 2D Dicom
reading software entitled “RadiAnt” can be downloaded and installed on your local desktop to
open a DICOM file if you want to manually inspect the tag section. A few critical tags that must
be set properly in order to read a DICOM dataset in Walk-In Brain are listed below:

Tag Code Tag Name Meaning


(0018, 0088) Spacing Between Slices The height of each pixel
(voxel) in 3D space
(0028, 0030) Pixel Spacing The length and width of each
pixel in 3D space
(0028, 0010) Rows The height of the DICOM
image in pixels
(0028,0011) Columns The width of the DICOM
image in pixels
(0028,0004) Photometric Interpretation Gives information about
whether the image is

47
grayscale or color
(0020,0012) Acquisition Number Gives information about
whether the image is
grayscale or color
(0020,0013) Instance Number Same as above

10.2 JPEG
File extension: .jpg or .jpeg

A JPEG image is a very compressed lossy image. It holds a large amount of graphical data in a
very small amount of disk space using lossy compression algorithms. Lossy means that data is
lost when JPEGs are created.

JPEG images vary greatly in quality. Some JPEGs are very compressed but they are very lossy
(this will cause them to look kind of grainy or blurry if you examine the image carefully). Some
are almost lossless but are nearly as large as PNG images.

Generally, JPEG images are great for viewing purposes, especially on mobile devices or for
storing ultra-large images. However, their lossy nature and the way that they are compressed
suggests that this data format is not optimal for analysis or segmentation since it is difficult to
accurately retrieve the original pixel information.

10.3 TIFF
File extension: .tif or .tiff

A TIFF file is a versatile image file type used by a wide variety of industries. It can carry
compressed or uncompressed image data. An image being compressed means that the fewer
colors/shades the image contains, the smaller the image can be. This is likely a reason it is used
in medical/scientific imaging since many of the images in this industry has only grayscale colors
in few shades (usually 256). TIFF images can be lossy or lossless. TIFF files come in two forms.

The first is a single-frame image. These images can be opened with most Windows image
readers and can be displayed like PNGs, BMPs, or JPEGs.

48
The second is a multi-frame image. Basically, it is a single image file which contains the data for
multiple images. For example, a DICOM image stack could be stored as a multi-frame TIFF
image. A few select image readers can open this, including updated versions of Windows image
gallery.

Multi-frame TIFF files are common in scientific visualization and use in general. Multiple data
sets that have been given to our lab were originally multi-frame TIFFs and a few tools such as
Dr. Kleinfeld’s mouse vasculature vectorization software also use multi-frame TIFFs as the
source data type.

It is important to note that unlike DICOM files, TIFF files do not contain tags that provide
information about the image besides from basic information such as time taken, much like PNGs
and JPEGs. David has written a program that can convert PNG and DICOM images into TIFFs.

Converting TIFF to DICOM:


It is frequently convenient for scientists to save their 3D imaging data in TIFF format for easy
transfer and small hard drive space requirements. In order to visualize a TIFF dataset in 3D, we
must convert it to a DICOM dataset. In order to do this, Grant has written a converter that works
within the MatLab environment. A list of required information is listed below that will need to
be gathered before the conversion can take place (note that X and Y directions correspond to the
image in Figure 10.1):

Name of Information Units


Voxel size in X direction mm
Voxel size in Y direction mm
Voxel Depth in Z direction mm
Spacing between slices in Z direction mm
Where

49
X direction
Figure 1: The X and Y directions set in the TIFF to DICOM converter as they appear on
the image. The dimensions are irrespective of whether the images are axial, saggital or
coronal, they only apply to the dimensions of the TIFF images themselves.

Once these 4 pieces of information are collected, move the converter script entitled
“TIFF_to_DICOM_converter.m” and “info.m” located in “Z:\20_Software\Data Converters\tiff
to DICOM converter” to the local directory where all of the TIFF images are stored. Then open
MatLab and open the script within the editor. Now the changes need to be made to the
highlighted region in Box 10.1.

Box 10.1

50
10.4 PNG
File extension: .png

A PNG image is one of the basic compressed image types. This means that the fewer
colors/shades the image contains, the smaller the image can be. PNGs, like TIFF images, only
contain pixel information and do not store information about the scanner or the size of the
voxels.

PNG images is the preferred data type for image data since it is highly compressed, modern, and
supports 32-bit data storage per pixel.

PNGs are lossless, meaning that they do not lose any of the original data, unlike JPEGs. This
does not mean that it is not compressed however. They are compressed, just in an efficient way
that does not cause loss of data.

10.5 BMP
File extension: .bmp

A BMP image is the most basic image type. It is basic since it is uncompressed, has very little
metadata attached to it. Basically, it is just bits of color data next to each other forming an image.

51
Since it is uncompressed, it is lossless. However, in the modern day, there are few reasons to use
BMP files. PNGs are capable of doing the same thing, except the image can be much smaller due
to compression. Compressed PNGs can be lossless, so they are better than BMPs in almost every
way.

11 CHAPTER 11 – FLUENT Data Formats


FLUENT is a professional PDE solver software that is part of the ANSYS package. It is
convenient to use with CAD structures where it can create 3D volumetric meshes from these
structures and simulate such things as fluid flow and diffusion. We frequently use this software
to simulate large geometries and have a need to compare the data from this software to our other
data. To do this, we would like to visualize the data in our own tool (Walk-In Brain) that can
visualize all of our other datasets as well. This chapter will outline what data structures FLUENT
uses and outputs and what steps need to be taken in order to visualize this data in Walk-In Brain.

11.1 Mesh File Format


The FLUENT msh file format is built on similar logic to the GAMBIT format, but has some key
differences that will be delineated in the following sections. The Mesh file in Fluent consists of a
header, a point coordinate matrix header, a point coordinate matrix, a face matrix header, a face
matrix and a zone section. Each of these is structures the same way as the GAMBIT format (refer
to Chapter 3 and 4) but are in a different order.

11.1.1 Mesh Converter


In order for our tool to correctly read in msh files, the format must strictly follow that of Chapter
3 and Chapter 4. In order to do this, 4 steps must be taken in order to convert the FLUENT msh
file into a GAMBIT msh file;
1. Modify the header
2. Copy over the point coordinate matrix
3. Reorder the face matrix groups so that the exterior face groups are first
4. Reduce the zone section

We currently possess 2 tools to accomplish this goal; the “ooMeshEditor” tool has a capability of
converting this file or the “MeshConverter.exe” tool located in S:\20_Software\Data Converters.
11.1.1.1 Step 1: Modify the Header
The header of the FLUENT mesh can be seen in Box 11.1. In order to read this file into the
converters, the FLUENT version must be altered to “v14.0.3” as seen in Box 11.2.

Box 11.1
52
Box 11.2
(0 " Created by : Fluent_V6 Interface Vers. 14.0.3")
11.1.1.2 Step 2: Reading the point coordinate matrix
The point coordinate matrix from the FLUENT version and the GAMBIT version of the mesh
file both have 3 columns, corresponding to the x, y and z coordinate of each point. Each row of
the point coordinate matrix corresponds to the point number (used in the face matrix).
11.1.1.3 Step 3: Reorder the face matrix
In the FLUENT and the GAMBIT mesh files, the face matrix is broken into multiple groups,
each with its own header. In the FLUENT file format, however, the exterior faces are put at the
end of the face matrix groups. The GAMBIT converter reads in all the faces in the face matrix
and then reorders the groups such that the exterior faces come first in the face matrix.
11.1.1.4 Step 4: Minimize the Zone Section
In the FLUENT format, the cells do not have their own header and the zones correspond to
different materials. Whereas this is useful for the FLUENT simulation engine, for our
visualization purpose, we can simplify all zones to being fluid zones. In this way, the zone
section seen in Box 11.3 is converted to the cells section and zone section in Box 11.4.

Box 11.3
(0 "Zone Sections")
(39 (6 fluid BRAIN_TISSUE)())
(39 (7 fluid TUMOR_MASS)())
(39 (8 interior int_BRAIN_TISSUE)())
(39 (9 interior int_TUMOR_MASS)())
(39 (10 wall TUMOR)())
(39 (11 wall BRAIN)())
Box 11.4
(0 "Cells:")
(12 (0 1 49722 1 2))
(12 (4 1 49722 1 2))

(0 "Zones:")
(45 (2 fluid fluid) ())

53
11.2 Data File Format
FLUENT naturally uses a .cas file that is written in a proprietary binary format and allows
FLUENT to read the data from file very efficiently. In order to visualize this data, we will need
to convert it to our GAMBIT format and write it into a “.mm” file.

11.2.1 Writing a Walk-In Brain Case File From FLUENT


Summary
Write ANSYS Fluent simulation data vector parameter values direct to .mm file format for
loading into Walk-In-Brain. All template files are contained in folder Fluent Data Export
Template. ***Copy entire folder to new directory when changing files.***

Step 1:
Set up simulation by loading mesh file and defining solution parameters or by loading existing
case file using the command found in: File  Read  Case.

Step 2:
Create .mm file with header and save in local file where FLUENT simulation is located. This file
should follow the format in Box 11.5 where the bolded items should be replaced with your
personalized data.

Box 11.5
<<model header>
//comments author=Your Name date=>
problemtype = ANSYS CFD Data
<meshfile>
meshfile=Mesh.GAMBIT.msh

Step 3:
Create a list of information to be inserted into the new “mm” file following the template in Table
11.1.

Table 11.1
Information Description Example
Number of cell zones # of cell zone groups in the mesh {1,2,…,n}
ID number of cell zone Fluent specific ID# of each zone {1=7,2=8,…,n=12}
Number of mesh cells # of total cells in mesh; all zones (dec//hex) 7363422 // 705B5E
*
Variable of interest Parameter to export for each cell pressure, species, etc.
.mm file name Name of file (Step 3) to write data vector to “WriteVectorFile.mm”
*List of UDF specific variable syntax is in Appendix A.1

Table 11.2: ANSYS Fluent Parameter Syntax List


Macro Argument Types Returns
C_R(c,t) cell_t c, Thread *t density
C_P(c,t) cell_t c, Thread *t pressure
54
C_U(c,t) cell_t c, Thread *t u velocity
C_V(c,t) cell_t c, Thread *t v velocity
C_W(c,t) cell_t c, Thread *t w velocity
C_T(c,t) cell_t c, Thread *t temperature
C_H(c,t) cell_t c, Thread *t enthalpy
C_K(c,t) cell_t c, Thread *t turb. kinetic energy
C_NUT(c,t) cell_t c, Thread *t turbulent viscosity for
Spalart-Allmaras
C_D(c,t) cell_t c, Thread *t turb. kinetic energy
dissipation
rate
C_O(c,t) cell_t c, Thread *t specific dissipation rate
C_YI(c,t,i) cell_t c, Thread *t, int i species mass fraction

Note: int i is species index

Step 4:
Now we must set up a user defined function (UDF) in FLUENT format to be ready by FLUENT
during the simulation and that will write our “.mm” file. To configure the UDF file, open
“WriteVectorTemplate.c” and modify it as need be for your simulation. The details of what need
to be modified are as follows:

 Initialize the number of cell zones as Threads; line 11


 Initialize the variable of interest as a real variable; line 16
 Initialize the number of ID variables to match the number of cell zones; line 21
 Set each ID# to the specific Fluent specific cell zone id #; line 30
 Set each ID to the corresponding thread number; line 37
 Set name of .mm file (Step 3); line 43
 Set the number of total mesh cells in dec and hex format; lines 48, 49
 Set variable name and ANSYS UDF variable syntax for each pull loop; line 58
 Copy the loop ‘n’ times for the number of ‘n’ threads
 If using multiple loops change the thread number to match each cell zone

The process for user input of information to the UDF file is demonstrated in Box 11.6. These
sections are necessary to properly configure the UDF code to write data specific to each scenario
and write a unique .mm file.

Box 11.6: Template UDF file for writing a data vector for cell centers from an ANSYS Fluent
simulation. Locations (i) – (viii) are necessary to update for each simulation to ensure the
variable data written directly to .mm file is unique and properly configured.

55
Step 5:
Compile UDF into Fluent as shown in Figure 2:
 Define  User-defined  Functions  Compile
 In the pop up window “Compiled UDFs” click ‘Add’… and navigate to the folder
location where the modified UDF from Step 5 is located.
 Change the Library Name to a unique name and click ‘Build ‘.
 After successful build, the following text (or similar will appear in the text interface)
Copied K:\03_Papers\2016\NW_BrainTumor\KT work\Real Head/K:\03_Papers\2016\NW_BrainTumor\KT work\Real
Head\WriteVectorBrain.c to libudfwritevector2\src
Creating user_nt.udf file for 3ddp ...
(…multiple lines of udf specific text will be written…)
Creating library libudf.lib and object libudf.exp

Done.
 Click ‘Load’ to load the UDF into Fluent; following text will appear if successful:
Opening library "K:\03_Papers\2016\NW_BrainTumor\KT work\Real Head\libudfwritevector2"...
Library "K:\03_Papers\2016\NW_BrainTumor\KT work\Real Head\libudfwritevector2\win64\3ddp\libudf.dll" opened
write_vector
Done.
 Once UDF if loaded into Fluent it must be activated as a ‘function hook’
 Define  User-defined  Function Hooks
56
 Click the ‘Edit’ available for ‘Execute at End’
 Select your UDF that was loaded and click ‘Add’, then Ok

Figure 2: The steps necessary to compile and activate the UDF to write data to an .mm file at
the end of every time step. (Left) Define and compile the UDF which loads the code into
ANSYS Fluent. (Right) Links the UDF to the workflow so the code is executed each time step
to write data to file.

The UDF is now loaded and active in the Fluent module and will be execute at the end of every
time step to write vector(s) of data to the specified .mm file.

12 References

1. http://www.adina.com
2. http://www.fluent.com/
3. http://www.fluent.com/software/gambit/
4. http://www.mscsoftware.com/support/online_ex/Scenario/Manual/man2.pdf
5. Bathe, K.: Finite Element Procedures. New Jersey, Prentice-Hall, Inc., 199

13 Appendix

I.1: Two-dimensional *.msh File consisted of triangles

57
(0 "GAMBIT to Fluent File")
(0 "Dimension:")
(2 2)
(10 (0 1 D 1 2))
(10 (1 1 D 1 2)(
2.0000000000e+000 0.0000000000e+000
2.0000000000e+000 1.0000000000e+000
2.0000000000e+000 5.0000000000e-001
0.0000000000e+000 0.0000000000e+000
6.6666666667e-001 0.0000000000e+000
1.3333333333e+000 0.0000000000e+000
0.0000000000e+000 1.0000000000e+000
1.3333333333e+000 1.0000000000e+000
6.6666666667e-001 1.0000000000e+000
0.0000000000e+000 5.0000000000e-001
3.9306883226e-001 5.0000000272e-001
1.0000001220e+000 5.0000000017e-001
1.6069312862e+000 5.0000000119e-001
))
(0 "Faces:")
(13 (0 1 1a 0))
(13 (3 1 a 3 0)(
2 4 5 2 0
2 5 6 d 0
2 6 1 8 0
2 1 3 7 0
2 3 2 6 0
2 2 8 5 0
2 8 9 a 0
2 9 7 3 0
2 7 a 4 0
2 a 4 1 0
))
(13 (5 b 1a 2 0)(
2 4 b 1 2
2 b a 1 4
2 5 b 2 e
2 7 b 3 4
2 b 9 3 c
2 8 d 5 9
2 d 2 5 6
2 d 3 6 7
2 d 1 7 8
2 d 6 8 b
2 8 c 9 a
2 c d 9 b
2 9 c a c
2 c 6 b d
2 b c c e
2 c 5 d e
))
(0 "Cells:")
(12 (0 1 e 0))
(12 (2 1 e 1 1))
(0 "Zones:")

58
I.2 : Three-dimensional *.msh File consisted of tetrahedral cells
(0 "GAMBIT to Fluent File")
(0 "Dimension:")
(2 3)
(10 (0 1 9 1 3))
(10 (1 1 9 1 3)(
5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 5.0000000000e-001 5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 -5.0000000000e-001
-5.0000000000e-001 -5.0000000000e-001 5.0000000000e-001
3.3882497385e-003 -3.2219810665e-006 4.9988601721e-004
))
(0 "Faces:")
(13 (0 1 1e 0))
(13 (3 1 2 3 0)(
3 6 1 8 3 0
3 4 1 6 4 0
))
(13 (4 3 4 3 0)(
3 3 1 4 a 0
3 2 1 3 b 0
))
(13 (5 5 6 3 0)(
3 5 4 6 6 0
3 3 4 5 8 0
))
(13 (6 7 8 3 0)(
3 7 6 8 2 0
3 5 6 7 5 0
))
(13 (7 9 a 3 0)(
3 7 3 5 7 0
3 2 3 7 c 0
))
(13 (8 b c 3 0)(
3 2 8 1 9 0
3 7 8 2 1 0
))
(13 (a d 1e 2 0)(

59
3 8 9 7 2 1
3 9 8 2 9 1
3 7 9 2 c 1
3 6 9 7 5 2
3 9 6 8 3 2
3 1 9 6 4 3
3 9 1 8 9 3
3 1 9 4 a 4
3 4 9 6 6 4
3 6 9 5 6 5
3 5 9 7 7 5
3 4 9 5 8 6
3 3 9 7 c 7
3 9 3 5 8 7
3 4 9 3 a 8
3 2 9 1 b 9
3 1 9 3 b a
3 2 9 3 c b
))

(0 "Cells:")
(12 (0 1 c 0))
(12 (2 1 c 1 2))
(0 "Zones:")
(45 (2 fluid fluid)())
(45 (3 wall w6)())
(45 (4 wall w5)())
(45 (5 wall w4)())
(45 (6 wall w3)())
(45 (7 wall w2)())
(45 (8 wall wall1)())
(45 (10 interior default-interior)())

I.3: Neutral File for two-dimensional *.msh File consisted of triangles

CONTROL INFO 2.2.30


** GAMBIT NEUTRAL FILE
default_id2520
PROGRAM: Gambit VERSION: 2.2.30
Jan 2008
NUMNP NELEM NGRPS NBSETS NDFCD NDFVL
18 22 1 0 2 2
ENDOFSECTION
NODAL COORDINATES 2.2.30
1 0.00000000000e+000 4.00000000000e+000

60
2 0.00000000000e+000 0.00000000000e+000
3 0.00000000000e+000 2.66666666667e+000
4 0.00000000000e+000 1.33333333333e+000
5 3.00000000000e+000 0.00000000000e+000
6 1.00000000000e+000 0.00000000000e+000
7 2.00000000000e+000 0.00000000000e+000
8 3.00000000000e+000 4.00000000000e+000
9 3.00000000000e+000 1.33333333333e+000
10 3.00000000000e+000 2.66666666667e+000
11 2.00000000000e+000 4.00000000000e+000
12 1.00000000000e+000 4.00000000000e+000
13 7.66518491352e-001 3.19604214921e+000
14 1.96341923329e+000 1.85175317966e+000
15 1.84167491184e+000 3.11659564726e+000
16 9.76065872390e-001 1.00453448902e+000
17 1.99571175745e+000 7.10540275021e-001
18 9.40906865090e-001 2.17994810933e+000
ENDOFSECTION
ELEMENTS/CELLS 2.2.30
1 3 3 12 1 13
2 3 3 13 1 3
3 3 3 8 11 15
4 3 3 8 15 10
5 3 3 4 2 16
6 3 3 16 2 6
7 3 3 16 6 17
8 3 3 17 6 7
9 3 3 5 9 17
10 3 3 5 17 7
11 3 3 15 11 12
12 3 3 4 16 18
13 3 3 4 18 3
14 3 3 17 9 14
15 3 3 14 9 10
16 3 3 3 18 13
17 3 3 12 13 15
18 3 3 10 15 14
19 3 3 16 17 14
20 3 3 16 14 18
21 3 3 18 14 15
22 3 3 15 13 18
ENDOFSECTION
ELEMENT GROUP 2.2.30
GROUP: 1 ELEMENTS: 22 MATERIAL: 2 NFLAGS:
1
fluid

61
0
1 2 3 4 5 6 7 8 9
10
11 12 13 14 15 16 17 18 19
20
21 22
ENDOFSECTION

I.4: A closer look at the different types of element formats: CTRIA3, CQUAD4

13.1 CTRIA3 Format

The format of the CTRIA3 element entry is as follows:

1 2 3 4 5 6 7 8 9 10
THETA
CTRIA3 EID PID G1 G2 G3 ZOFFS
or MCID
T1 T2 T3

Field Contents

EID Element identification number. (Integer > 0)


PID Property identification number of a PSHELL or PCOMP entry.(Integer > 0; Default is
EID)
Gi Grid point identification numbers of connection points. (Integers > 0, all unique)
THETA Material property orientation angle in degrees. (Real; Default = 0.0)
MCID Material coordinate system identification number. The x-axis of the material coordinate
system is determined by projecting the x-axis of the MCID coordinate system (defined
by the CORDij entry or zero for the basic coordinate system) onto the surface of the
element. (Integer ≥ 0; if blank, then THETA = 0.0 is assumed)
ZOFFS Offset from the surface of grid points to the element reference plane. (Real)
Ti Membrane thickness of element at grid points G1, G2, and G3. (Real ≥ 0.0 or blank, not
all zero)

If you don’t supply values for Ti, then the software sets the element’s corner thicknesses T1
through T3 equal to the value of T on the PSHELL entry.

13.2 CQUAD4 Format


The format of the CQUAD4 entry is as follows:
1 2 3 4 5 6 7 8 9 10

62
THETA
CQUAD4 EID PID G1 G2 G3 G4 ZOFFS
or MCID
T1 T2 T3 T4

Field Contents
EID Element identification number.
PID Property identification number of a PSHELL or PCOMP entry.
Gi Grid point identification numbers of connection points.
THETA Material property orientation angle in degrees.
MCID Material coordinate system identification number.
ZOFFS Offset from the surface of grid points to the element reference plane.
Ti Membrane thickness of element at grid points G1 through G4.

Grid points G1 through G4 must be ordered consecutively around the perimeter of the element.
THETA and MCID are not required for homogenous, isotropic materials. ZOFFS is used when
offsetting the element from its connection point. The continuation entry is optional. If you don’t
supply values for T1 to T4, the software sets them equal to the value of T (plate thickness) you
define on the PSHELL entry. Finally, all interior angles of the CQUAD4 element must be less
than 180°.
In general, there are four sections in the Nastran file:

1. File Management Section


The File Management Section is not used in general solution sequences and is optional. If the
problem is large, this section is used to initialize the maximum size of database, member names,
and location of file. Also this is used to perform a restart run.
Example) RESTART
ASSIGN,MASTER=aaa.MASTER

2. Executive Control Section


This section specifies the type of analysis solution to be performed.
Example) SOL 101 <=== Linear Static Type
CEND

3. Case Control Section (CCS)


The Case Control Sections defines the analysis condition with a subcase containing
identification number (ID) of boundary and load conditions specified in the Bulk Data
Section (BDS) for the analysis model and the type of outputs required.

Example) SUBCASE 1 <=== the first analysis condition

63
SPC = 10 <-----define the constraint identified as 10 in BDS
LOAD = 10 <-----define the load identified as 10 in BDS
DISP= ALL <-----print the displacements of all nodes
STRESS = ALL <-----print stresses of all elements
SUBCASE 2 <===the second analysis condition
SPC = 10 <-----define the constraint identified as 10 in BDS
LOAD= 20 <-----define the load identified as 20 in BDS
DISP= ALL <-----print the displacements of all nodes
STRESS = ALL <-----print stresses of all elements

4. Bulk Data Section (BDS)


The Bulk Data Section is mainly generated by the pre-processor and defines everything required
to describe the finite element model as nodes and elements grid data, element data, several
boundary conditions, load conditions, and parameters required.
Example)
SOL 101 ======> define the solution sequence
CEND ======> describe the end of ECS
SPC=10 ======> define the constraint
LOAD=20 ======> define the load condition
DISP=ALL ======> define printing displacements
BEGIN BULK ======> input geometry, loads, and the constraints
GRID,1,,0.,0.,0. ======> define nodes
GRID,2,,5.,0.,0.
GRID,3,,10.,0.,0.
CQUAD4,1,10,1,2,5,4 ======> define elements
CQUAD4,2,10,2,3,6,5
CQUAD4,3,10,4,5,8,7
CQUAD4,4,10,5,6,9,8
PSHELL,10,30,0.25,30 ======> define element properties (thickness : 0.25)
MAT1,30,3.E7,,0.33 ======> define material properties
FORCE,20,5,,-1000.,0.,0.,1. ======> define loads
SPC1,10,123456,1,2,3,4,6,7,+AA1 ====> define the constraints
+AA1,8,9
ENDDATA ======> the end of input file

MAT1 : Homogeneous, Isotropic

64
MAT2 : Anisotropic material for two-dimensional elements, plates or shells. In-plane, transverse
shear material

I.5 : VTK/Python Code for Delaunay Triangulations


import vtk
# Unstructured grid
Points = vtk.vtkPoints()
Points.SetNumberOfPoints(14)
Points.InsertPoint(0, 0, 0, 0)
Points.InsertPoint(1, 2, 0, 0)
Points.InsertPoint(2, 1.5, 0.6, 0)
Points.InsertPoint(3, 3.5, 0, 0)
Points.InsertPoint(4, 4.8, 0.7, 0)
Points.InsertPoint(5, 6, 0, 0)
Points.InsertPoint(6, 6, 1.4, 0)
Points.InsertPoint(7, 5, 1.8, 0)
Points.InsertPoint(8, 6, 2, 0)
Points.InsertPoint(9, 4, 2, 0)
Points.InsertPoint(10, 3.5, 1.1, 0)
Points.InsertPoint(11, 2, 2, 0)
Points.InsertPoint(12, 1.3, 1.2, 0)
Points.InsertPoint(13, 0, 2, 0)
strips = vtk.vtkCellArray()
strips.InsertNextCell(14)
strips.InsertCellPoint(0)
strips.InsertCellPoint(1)
strips.InsertCellPoint(2)
strips.InsertCellPoint(3)
strips.InsertCellPoint(4)
strips.InsertCellPoint(5)
strips.InsertCellPoint(6)
strips.InsertCellPoint(7)
strips.InsertCellPoint(8)
strips.InsertCellPoint(9)
strips.InsertCellPoint(10)
strips.InsertCellPoint(11)
strips.InsertCellPoint(12)
strips.InsertCellPoint(13)
polyData = vtk.vtkPolyData()
polyData.SetPoints(Points)
polyData.SetStrips(strips)
delny = vtk.vtkDelaunay2D()
delny.SetInput(polyData)
delny.SetSource(polyData)

65
deci = vtk.vtkDecimatePro()
deci.SetInput(delny.GetOutput())
deci.SetTargetReduction(0.0)
deci.PreserveTopologyOn
smoother = vtk.vtkSmoothPolyDataFilter()
smoother.SetInput(deci.GetOutput())
smoother.SetNumberOfIterations(50)
normals = vtk.vtkPolyDataNormals()
normals.SetInput(smoother.GetOutput())
normals.FlipNormalsOn
mapmesh=vtk.vtkPolyDataMapper()
mapmesh.SetInput(normals.GetOutput())
meshActor = vtk.vtkActor()
meshActor.SetMapper(mapmesh)
meshActor.GetProperty() .SetColor(0.38, 0.7, 0.16)
meshActor.GetProperty().SetRepresentationToWireframe()
; # the exported image is set to wireframe
writer = vtk.vtkSTLWriter()
writer.SetInput(normals.GetOutput())
writer.SetFileName('delny_smooth.ascii.stl')
#writer.SetFileTypeToBinary()
writer.Write()
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
renWin.SetSize(800, 450)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
ren.SetBackground(1., 1., 1.)
ren.AddActor(meshActor)
ren.ResetCamera()
ren.GetActiveCamera().Azimuth(10)
ren.GetActiveCamera().Elevation(20)
ren.GetActiveCamera().Dolly(1.5)
ren.ResetCameraClippingRange()
w2image = vtk.vtkWindowToImageFilter()
#writer = vtk.vtkSTLWriter()
#writer = vtk.vtkTIFFWriter()
writer = vtk.vtkJPEGWriter()
w2image.SetInput(renWin)
w2image.Update()
writer.SetInputConnection(w2image.GetOutputPort())
#writer.SetFileName("image1.tif")
#writer.SetFileName("image4.stl")
writer.SetFileName("Delaunay_pic.jpg")

66
renWin.Render()
writer.Write()
# Render the scene and start interaction.
iren.Initialize()
renWin.Render()
iren.Start()

14 Delphi converter application

14.1 Summary
The converter application (Figure 6) converts from 2-point networks to spline networks and vice
versa.

Figure 6. Converter software GUI.

The software can be accessed either from the converter application or from the Viewer
application (Figure 7).

67
Figure 7. Accessing converter from the Viewer application.

Five function tabs can be found at the top (Figure 8):


1. Convert NWK Files to Splines.
2. Convert CaseFiles To Splines.
3. Convert Splined to Even2ptmesh.
4. Editing And Movement.
5. CleanSplinedNW.

Three additional function tabs can be found below the main tabs that display the networks loaded
and the statistics of the spline and 2-point networks:
1. DisplayNWK.
2. SplineNWKStatistics.
3. NWKStatistics.

Figure 8. Main tabs in the converter.

The test case file considered in this section can be found in:
 Share\19_ImageInventory\Converter\TestFiles\testCoW.cs31
 Share\19_ImageInventory\Converter\TestFiles\testCoW.nwk

14.2 Main functions

14.2.1 Convert NWK Files to Splines


This function converts 2-point network files (*.nwk) to spline network files (*.snwk). There is no
information of the diameter of the network as there is no casefile (*.cs31). The conversion
procedure is:
1. Load network (*.nwk) using “loadButton”.

68
2. Convert to spline network (*.snwk) using “convertButton”.
3. Save the spline network using “saveButton”. Note that the extension needs to be added to
the file name when saving the newly created spline network.

Figure 9. Procedure to convert 2-point networks to spline networks.

The reset button, “reset”, will clear the display and the loaded network.

Only lines are shown in the display as there is no diameter information. Thus, the
ToggleCylinderView in the DisplayNWK tab will not perform anything.

Figure 10. Conversion of 2-point network to spline network.

69
14.2.2 Convert CaseFiles To Splines
This function converts case files (*.cs31) linked to 2-point network files (*.nwk) to spline case
(*.cs4) and network files (*.snwk). The case file contains the information of the diameter of the
network. The conversion procedure is:
1. Load case file (*.cs31) using “loadCSButton”. The corresponding network file (*.nwk)
will be loaded automatically, with the information being stored in the case file.
2. Convert to spline case (*.cs4) and network (*.snwk) using “ConvertCase”.
3. Save the spline case (*.cs4) and network files (*.snwk) using “saveCase”. The extensions
are automatically added during the saving procedure.

Figure 11. Procedure to convert 2-point networks to spline networks.

The reset button, “reset2” will clear the display and the loaded case and network.

During the conversion, the diameter information is also converted and is followed by a linear
fitting using a least-square deviation approach (Figure 12).

70
Figure 12. Two sample results of the linear fitting of diameter in each spline. The red plot is the original spline
network diameter and the green plot is the linear fit.

Figure 13. Conversion of 2-point case and network to spline case and network. The original 2-point network is on
the left and the spline network on the right.

71
14.2.3 Convert Splined to Even2ptmesh
This function converts spline case (*.cs4) and network files (*.snwk) to 2-point case (*.cs31) and
network files (*.nwk). The case file contains the information of the properties that are being
converted. For example, it contains inlet diameter and its slope along each spline in the network.
The procedure to convert the splined network diameters to a 2-point format is:
1. Determine the number of subdivisions along each spline in the box labelled “Number of
Subdivisions”. The base value is currently set at 10. The used can change this number.
2. Load and convert case file (*.cs4) using “loadSplinedNW_Convert”. The corresponding
spline network file (*.snwk) will be loaded automatically and converted.
3. Save the 2-point case (*.cs31) and network (*.nwk) files using “save2DNWK”. The
extensions are automatically added during the saving procedure.

Figure 14. Procedure to convert spline networks to 2-point networks.

Although there is no reset button in this tab, it is possible to clear the display and the loaded case
and network using any of the reset buttons in the other tabs.

72
Figure 15. Conversion of spline case and network to 2-point case and network. The original spline network is on
the left and the 2-point network is on the right. The face (edge) indices are also represented in both networks.

It is also possible to convert point properties in the splined network to 2-point format by linear
interpolation. This is achieved by:
1. Determine the number of subdivisions along each spline in the box labelled “Number of
Subdivisions”. The base value is currently set at 10. The used can change this number.
2. Load and convert case file (*.cs4) using “convertAndReadProperty”. The corresponding
spline network file (*.snwk) will be loaded automatically and converted.
3. Save the 2-point case (*.cs31) and network (*.nwk) files using “save2DNWK”. The
extensions are automatically added during the saving procedure.

Figure 16. Procedure to convert spline network point properties to 2-point format.

73
Figure 17. Conversion of spline network point property (pressure in this case) to 2-point format.

14.2.4 Editing and Movement


This function moves the displayed image along the horizontal axis. The user needs to choose the
offset factor of the original network by inserting an offset index value between -1 and 1:
1. Insert offset index.
2. Clicking the “toggleOffset” button.

Figure 18. Procedure to convert 2-point networks to spline networks.

The offset is performed on the original image and moves either left (negative offset) or right
(positive offset). Figure 18 shows the original image and Figure 19 shows the offset with an
offset index of 0.5. The original image has moved towards the right.

74
Figure 19. Conversion of spline case and network to 2-point case and network. The original spline network is on
the left and the 2-point network is on the right. The face (edge) indices are also represented in both networks.

14.2.5 CleanSplinedNW
This function finds and removes dangling splines in spline case and network (*.snwk) files. The
finding and removing procedure is as follows:
1. Load the case file (*.cs4) using “loadSplinedNW”. The corresponding spline network file
(*.snwk) will be loaded automatically.
2. Determine the dangling splines using “showAllDanglingSplines”. This will show a list of
splines that are dangling on the “RemoveSplines” box.
3. Remove the dangling splines by clicking the “DoIt” button next to the “RemoveSplines”
box.
4. Save the cleaned spline case (*.cs4) and network (*.snwk) files using “saveSplinedNW”.
The extensions are automatically added during the saving procedure.

75
Figure 20. Procedure to clean spline networks with dangling vessels

The reset button, “reset” in this tab, will clear the display and the loaded case and network. The
user can remove any spline manually by adding the spline numbers that one wants to remove in
the “RemoveSplines” box and then click the “DoIt” button. Note that multiple splines can be
removed at once by inserting a list of splines to be removed in ascending order.

Figure 21. Removal of dangling splines. The original spline network is on the left and the 2-point network is on
the right. The face (edge) indices are also represented in both networks.

14.3 Additional functions for visualization and statistics

14.3.1 DisplayNWK
This function changes the visualization of the network display. There are five options:
76
1. ToggleCylinderView – it changes visualisation from line to cylindrical view of the
diameter, when the information is available.
2. ToggleGroupview – shows the different groups.
3. PointLabels – shows the node (vertex) indices.
4. FaceLabels – shows the face (edge) indices.
5. Direction – shows the direction in which the face was labelled.

14.3.2 SplineNWKStatistics
This function provides statistical information of the spline networks. There are four options:
1. reportSNWKBtn – it provides information of the network.
2. meshFileBtn – [Currently does nothing].
3. PointMx – shows the connection between the node (vertex) indices.
4. ptCoordMx – shows the co-ordinates of the nodes (vertices).

14.3.3 NWKStatistics
This function provides statistical information of the 2-point networks. There are four options:
1. nwkLength – [Currently does nothing].
2. BitBtn2 – [Currently does nothing].
3. BitBtn3 – [Currently does nothing].
4. BitBtn4 – [Currently does nothing].

15 Converting among mesh and stl


Written: Homa Rashidisabet 11/8/2018
Edited: Grant Hartung 11/14/2018

15.1 How to make a mesh for testing converter


In order to validate the conversion between STL and mesh format, a case study must be
procured. The optimal choice for simplicity is a simple tetrahedron.

77
C

E F

Figure 22. Generating a simple mesh in ICEM. Make surface for generating mesh. (d). Save it
as ANSYS Fluent mesh or STL. (E). Simple mesh. (F). Coordinates shown on the mesh. (G).
STL format of same geometry. (H). Gambit format of same geometry.

An STL file shows a normal and three point coordinates for each triangle in the mesh as seen
below:

78
Figure 23. STL and mesh files comparison. (A). STL file of format for the shown geometry in
Figure 24. (B) Gambit file of same geometry.

Note: The resulting STL file does not currently maintain any face grouping. This can be
implemented but is currently not implemented. For more information see Grant Hartung.

Figure 25. Visualization of converted stl to mesh and original mesh in ViewerApplication. (A).
original sled mouse msh file. (B). Converted to STL and back to msh. Note that the anatomical
grouping is lost during the conversion process.
79
15.2 Converter from mesh to stl
The STL format is useful for passing data between softwares, yet it is not used in the model
generation libraries used by the LPPD. As such, the STL converter must only write an STL file,
not make an STL object. Figure 23B displays point coordinate matrix and face matrix
information in addition to headers and number of points, faces, volume and dimensions in the
mesh file. However, the STL file stores each triangle as three vertices and a face normal for each
triangle in the mesh. Thus, the program iterates over each row of the face matrix to retrieve the
indices of connected points on each triangle. This data is formatted in the STL format (Figure
23A) and written to file. The obtained file is called an STL file.
In order to compare the converters, it is important to use one case study among all mesh
converters. To do this, a tetrahedral mesh of a simple tube was created in ICEM by Homai
Rashidisabet. This tube was cut to a very small length in order to expose jagged edges for easy
comparison of point coordinate accuracy and to identify missing mesh elements. The original file
is converted to GAMBIT format using the tool delineated in Section 18.1. The source file to
begin this demonstration is CutCylindetWithVolume.msh located in Z:\__temp\Converter\.
The steps for converting between GAMBIT mesh format and STL are explained below. The
application for the conversion is ViewerApplication_updatedConverter.exe located in
Z:\__temp\Converter\ or is available by Grant. The source codes for the converters are given in
Z:\__temp\Converter\sourceCodes\ or are available via Grant. The source code for building and
compiling the converter are on Grant’s computer. For reference, the original mesh file (in
GAMBIT format) is viewed in Viewer Application below:

Figure 26. Visualization of original GAMBIT mesh file of the cut tube used in this case study.
The coloration is for anatomical grouping, with red being Group Exterior and blue being
Group Interior.

Step 1: Launch the Viewer Application and select the “Converter” button

80
Step 2: select the “mesh/STL converter” tab on the far right

81
Step 3: Load a triangulated (triangulated surface mesh or tetrahedral volumetric mesh) mesh
file by selecting the “Load Triangulated Mesh” button

Step 4: Load your mesh and observe that the display shows the mesh you expected to see:

82
Step 5: Save the file as an STL file by selecting “Save as STL” and choose a new file name:

Verify that the new STL file works by opening it in Windows 10 STL viewer tool:

To exemplify this conveter on a larger scale, the conversion from GAMBIT mesh to STL for
a mouse brain file was performed. The results are offered in Cerebroview in Figure 27. The
visualization of both files are exactly the same.

Figure 27. Visualization of converted mesh to stl and original stl in Cerebroview. (B)
Converted Mesh to stl. (C) original stl.

83
15.3 Converter from stl to msh
In order to convert the stl to mesh, we need to extract point coordinate matrix and face matrix
information to write in the msh file. As it is shown in Figure 23A, an STL file groups every three
vertices together to represent one triangle. This can be extracted while looping across all
traingles in the mesh. In order to avoid repeating points, which does occur in STL format,
duplicate points are identified during the reading process and replaced with the index of the
currently existing point in the ptCoordMx. A walkthrough is offered for converting the tube
mesh from STL format back to GAMBIT mesh format using the
ViewerApplication_updatedConverter.exe located in Z:\__temp\Converter\ or is available by
Grant.
Note: The STL to GAMBIT converter assumes all faces in the STL file are boundary faces.
There is currently no method for extracting interior mesh volumes from an STL file. For more
information, see Grant.

Step 1: Launch the Viewer Application and select the “Converter” button

Step 2: select the “mesh/STL converter” tab on the far right

84
Step 3: Load an STL mesh by selecting the “Load STL” button

Step 4: Select STL file for converting and ensure the visualization is acceptable:

Step 5: Save as GAMBIT file by selecting “Save as GAMBIT Mesh” button and choosing a
suitable file name:

And open the new mesh file in ViewerApplication.exe for validation:

85
16 Walk through of viewer application for visualizing mesh files
We can visualize msh files in the viewer application by just loading them. In Figure 20, we show
step by step guidance for how and where we can visualize the msh files.

86
Figure 28. Viewer application form used for visualizing mesh file. (A). Red box shows the
bottom for loading msh file. (B). Visualizng the mesh in viewer application.

17 Registration of two objects


The Delphi code for writing a system of linear equations is implemented in a sparse format
which is more efficient in the sense of memory allocation. In “ooXSolverSource” unit, we made
a class procedure to test and scrutinize how Delphi code has been programed for writing
equations and solving them. In linear system of A x=b, we write A matrix and b vector in a
sparse format which means we omit all zeroed coefficient variables and zeroed values of right-
hand-side. Furthermore, we might have different subset variables which can be stored in
different vectors, but at the end before solving the equations, we make a global variable that is
made by concatenation of all subset variables. Having all variables in one global variable makes
programing easier because indexing of variables will become easier.
As we showed previously in image registration report, alignment of two object can be considered
as solving a system of equations. Thus, this case study is for aligning two misaligned identical

87
cubes. Thus, we manually choose four points with indices of {1,4,8,2} from one cube and find
their correspondence points in another cube which in this identical case is again point indices of
{1,4,8,2}. The coordinate of four points that we have used for first and second cubes are given in
Error: Reference source not found
Table 1. Coordinates of points on both cubes used for alignment.
COW 1 COW 2
X Y Z X Y Z
1.8900 0.1080 -0.0117 -0.0540 -0.0039 -0.0985
1.8500 -0.0758 -0.0188 0.0379 -0.0063 -0.0879
1.8900 0.0094 -0.0018 -0.0047 -0.0006 -0.0974
1.4000 0.0005 -0.2640 -0.0002 -0.0881 0.0254

−0.1089 −2.0009 −0.001 1.66


[
R= 2.362
−2.302 −0.004 0.006
r max =0 , r norm =0
] [ ]
−0.006 3.004 , t= 0
0
(1)

(2)

17.1 Walk through of viewer application for visualizing mesh files


In Figure 21, we show step by step guidance of how we can use the registration application
code, “RegistrationApplication.pas”. The coordinates for the object 1 and object 2 is given in
table 2.

88
89
Figure 29. Registration form. (A). the general view of the registration form. (B). Red box shows
where user needs to insert four points of object one and two, and the bottom for computing the
transformation information. (C). Red boxes show the transformation matrix, offset vector and
eligned second object on object one. (D). visualization of the eligned points.

After getting the transformation matrix and offset vector, we can insert this information in the
viewer application and apply it on the object 2 to align it by object 1.

90
Figure 30. Applying transformation matrix on the network in viewer application. Two misaligned
objects, Object 2. The last picture is object two that is aligned with object1.

18 Documentation of ICEM/GAMBIT mesh converter.


Written: Grant Hartung 11/14/2018

Sometimes it is convenient to produce a mesh in one tool (Delphi in some cases, ICEM in
others) and want to operate on the mesh in another tool. This could be the case when volumizing
a surface STL mesh, which can only be performed in ICEM (code not written in Delphi for this),
yet the volumetric mesh is simulatable in Delphi where much more control over the
mathematical model generation can occur. The same is true when a parametric mesh is generated
in the Delphi language and is to be simulated in FLUENT for any reason. These two
computational domains use differently organized files and look for different tags, so a converter
is necessary to transpose the information between these two media. Such a tool has been built by
GH in Delphi and is described below using the MeshConverter project in the grantTests folder
(when compiled, this is known as MeshConverter.exe):

18.1 Converting ICEM mesh file to GAMBIT mesh file:


The following walkthrough is explained using a test case named M1V3.icem.msh (an ICEM
mesh generated in ICEM) located in S:\__temp\Converter\. This mesh can be visualized in ICEM

91
as in Figure 31 and converted using the converter in the
ViewerApplication_updatedConverter.exe located in Z:\__temp\Converter\ or is available by
Grant.

Figure 31. Original ICEM mesh file visualized in ICEM. This is a monkey spine. This mesh
was found amongst the data received from a collaborator. At the time, it was not able to be
loaded by any visualization tools or Delphi programs for processing, a perfect case to be used
within the confines of our converter.

Step 1: Launch the Viewer Application and select the “Converter” button

Step 2: select the “ICEM GAMBIT Mesh Converter” tab

92
Step 3: Open an icem-compatible mesh file using the “Load ICEM Mesh” button and validate
that the visualization matches your mesh:
This will create a ooANSYSReader object that has a mesh property that houses the ICEM mesh in memory.

Step 4: Save the mesh in GAMBIT format using the “Save as GAMBIT Mesh” button and
select a suitable mesh file name:

93
Step 5: Validate that the new mesh file is the same as the original mesh file by loading it in
ViewerApplication.exe:

18.2 Instructions for converting from GAMBIT to ICEM:


Any GAMBIT mesh can be converted to ICEM-compatible mesh using the converter in the
ViewerApplication_updatedConverter.exe located in Z:\__temp\Converter\ or is available by
Grant. Note, the new ICEM mesh must be loaded into ICEM using “ImportMeshfromFluent”.
If nothing appears on screen, click off and on the checkboxes for each part of the mesh to reveal
the loaded mesh.

94
Figure 32. Visualization of the GAMBIT file generated using the converter in Section 18.1.
This file is named M1V3.convertedFromIcem.msh and is located in S:\__temp\Converter\.

Step 1: Launch the Viewer Application and select the “Converter” button

Step 2: select the “ICEM GAMBIT Mesh Converter” tab

95
Step 3: Open a GAMBIT mesh file by selecting the “Load GAMBIT Mesh” button:

Step 4: Identify the GAMBIT mesh file to choose and Verify that the mesh file visualization
matches the expected mesh file:

96
Step 5: Save the mesh in ICEM format by selecting “Save as ICEM mesh” button

Step 6: Validate that the ICEM mesh loads and looks correct in the ICEM program:

Figure 33. Visualization of the ICEM file generated from the coversion process. This file is
named M1V3.convertedFromIcem.icem.msh and is located in S:\__temp\Converter. Loaded
into ICEM using ImportMeshFromFluent. If nothing appears on the screen, deselect and
select (uncheck and check the checkboxes) for the parts on the left side of the screen to reveal
the loaded mesh.

19 Documentation of Delphi “.cs31” and “.casx” converter.


Written: Grant Hartung 11/14/2018

All conversions between a cs31 file (case file), with corresponding .nwk file (network file),
and .casx file format can be accomplished with the converter in the

97
ViewerApplication_updatedConverter.exe located in Z:\__temp\Converter\ also available by
request from Grant. This tool allows the user to select a case file, which instantiates a mesh
object from that case file, and save as a .casx file. Also included is an option to read a .casx (case
X file), which also instantiates a tubeMesh object, which can be saved in .cs31/.nwk format.
The option to write a casx file from the mesh is available with a button. This button first
instantiates a ghCaseXFileWriter object and passes the mesh and the file name in. The
ghCaseXFileWriter object located in ghCaseXFileSource.pas writes the header, the point
section, the face section, and the diameter vector section. This button then goes back to the case
(.cs31) file and finds any data that may be contained within it. The button then passes the data it
found previously from the file into the ghCaseXFileWriter. The button then instructs the writer
to save the file. Note, the casx file does not preserve group indexing.
The option to save the mesh as a .cs31 file is available by a single button. This button
instantiates a ooCaseFileWriter object and passes the mesh and the case file name into it. Source
code is available at Z:\__temp\Converter\sourceCodes\ ghCaseXFileSource.pas.

19.1 Converting .cs31 file to .casx file:


This walkthrough will be exemplified by a case study entitled au.1.tortuous.cs31 and
au.1.tortuous.nwk located in S:\__temp\Converter. It is worth noting at this point that the .casx
file convention does not preserve the group affiliation originally stored in the .cs31 file structure.

Figure 34. Visualization of the original cs31 and nwk file created from grant’s artificial
network generation code (version 1), Project_KFGenAndSolvingApp.exe.

Step 1: Launch the Viewer Application and select the “Converter” button

98
Step 2: select the “ICEM GAMBIT Mesh Converter” tab

Step 3: Open a case file (.cs31) using the “LoadCaseFile” button:

Step 4: Select an appropriate case file and verify the visualization matches the expectated
network:

99
Step 5: Save the network in the .casx file format by selecting “Convert and Save As .casx”
button. NOTE: If you wish the casx file to include any data vectors from the cs31 file (aside
from diameter, which is automatically included), the option of “Load data from .cs31 file” must
be selected inside the “Save with data vectors” radiobox (automatically selected). If you do not
wish to save the network with any data aside from a diameter vector, change the selection to
“do not load data”:

Step 6: Choose a suitable file name to save the file as:

Step 7: Open the new casX file to validate that the file has the correct date:

100
Note, there are currently no visualization tools for validating the .casx file except for the option
in the converter to load a .casx file. It is recommended to validate using this button as follows:

101
Step 8: Validate the visualization of the .casx network by clearing the form:

And opening the casx file by selecting “Choose .casx file” button”

And then by reviewing the visualization of the network:

102
19.2 Instructions for converting from .casx to .cs31:
Step 1: Launch the Viewer Application and select the “Converter” button

Step 2: select the “ICEM GAMBIT Mesh Converter” tab

Step 3: Open a case file (.cs31) using the “Choose .casx file” button:

Step 4: Select an appropriate case file and verify the visualization matches the expectated
network:

Step 5: Save the network in the .cs31/.nwk format by selecting “save as .cs31 file” button:

103
Step 6: Choose a suitable file name to save the file as:

Step 7: Open the new .cs31/.nwk file in viewerApplication.exe to verify it converted properly:

104
20 Reading and Visualizing casx files in Matlab
The general logic of the code is given in Figure xx where the only user input is a 1 or 0 to
indicate the presence of labels.

Figure 35: Main workflow of the Matlab code

The code was used for .casx and .cas files of two simple bifurcations and a simplified structure
of the arteries and veins fused.
A

B
P1

P2

P3

P6
P4 P5

Figure 36: (A) Arteries and vein fused structure (B) Simple bifurcation

105
The code was converted into a stand-alone executable called casxReadVisualize_labels that can
be ran without Matlab using the Matlab Compiler application (Figure xx).

Figure 37: Matlab compiler used to convert an .m file into a stand-alone executable

By installing the application using the default settings, the application will be installed in the
following path: C:\Program Files\casxReadVisualize_labels\application. Using the command
line, the directory should be reached using the cd and cd .. commands and then the application
should be ran using 1 or 0 as an input.

Figure 38: Running the stand-alone executable from the command prompt

Appendix A:
Points_Converter: (Conversion of .Msh Fie to Nastran File.)

106
clear all; close all; clc;
fileID =fopen('Sample.txt','r'); j = 1;
while ~feof(fileID)
line = fgetl(fileID);
PointsMx(j,:) = sscanf(line, '%f %f %f')';
j = j+1;
end
fclose(fileID);
x = PointsMx(:,1);
y = PointsMx(:,2);
z = PointsMx(:,3);

f = fopen('Sample.nas','w+');
for i = 1:length(x)
point=sprintf('GRID,%d,,%d,%d,%d\n',i,x(i),y(i),z(i));
fwrite(f,point);
end
fclose(f);

Face_Converter:

clear all; close all; clc;


fileID =fopen('Sample 7_Faces.txt','r'); j = 1;
while ~feof(fileID)
line = fgetl(fileID);
faceMx(j,:) = sscanf(line, '%x %x %x %x %x');
j = j+1;
end
fclose(fileID);
a = faceMx(:,1);
b = faceMx(:,2);
c = faceMx(:,3);
d = faceMx(:,4);
e = faceMx(:,5);
% g = faceMx(:,6);
% h = faceMx(:,7);

f = fopen('Sample 7.nas','w+');
Start_Connection_Number = 1;
for j = 1:length(a)
% point=sprintf('CQUAD4,%d,10,%d,%d,%d\n',j+(Start_Connection_Number-
1),a(j),b(j),c(j));

107
point=sprintf('CTRIA3,%d,7,%d,%d,%d\n',j+(Start_Connection_Number-
1),a(j),b(j),c(j));
fwrite(f,point);
end
fclose(f);

function casxReadVisualize_labels(labelChoice)
% casxReader and visualizer -- Case file must be chosen by the user
% Author: Claudia Vesel, CSP

%% Select file
[filename,pathname] = uigetfile('*.casx');

%% Get network information


nwk = casxRead([pathname filename]);

%% Plot Network
casxVisualizer(nwk.ptCoord,nwk.faceMx,nwk.Dia,labelChoice)
end
function nwk = casxRead(filename)
fid = fopen(filename);

%% Reads through file


none = 0;
pointMx = 1;
faceMx = 2;
dia = 3;
flow = 4;

state = none;
while ~feof(fid)
line = fgetl(fid);

if strncmpi(line,'//diameter',9) == 1
state = dia;
line = fgetl(fid);
i = 0;
elseif strncmpi(line,'//point',7) == 1
state = pointMx;
fgetl(fid);
line = fgetl(fid);
i = 0;
elseif strncmpi(line,'//connectivity',14) == 1
state = faceMx;
fgetl(fid);
line = fgetl(fid);
i = 0;
elseif strncmpi(line,'//flow',6) == 1
state = flow;
line = fgetl(fid);
i = 0;
elseif strncmp(line,'//end',5) == 1
state = none;
end

switch state

108
case dia
i = i + 1;
myDiameter(i,1) = str2double(line); %#ok<AGROW>
case flow
i = i + 1;
myFlow(i,1) = str2double(line); %#ok<AGROW>
case pointMx
i = i + 1;
myPtCoord(i,1:3) = sscanf(line,'%f %f %f'); %#ok<AGROW>
case faceMx
i = i + 1;
% myFaceMx(i,1:2) = sscanf(line,'%i %i'); %#ok<AGROW>
myFaceMx(i,1:2) = (sscanf(line,'%x %x')); %#ok<AGROW>
otherwise
end
end

try
nwk.Dia = myDiameter; nwk.ptCoord=myPtCoord; nwk.faceMx=myFaceMx; nwk.flowVec=myFlow;
catch
end
fclose(fid);
end
function casxVisualizer(ptCoordMx,faceMx,diaVec,labelChoice)
figure; %Plot points,faces scaled by dia
sz=20;
scatter3(ptCoordMx(:,1),ptCoordMx(:,2),ptCoordMx(:,3),sz,...
'MarkerEdgeColor',[0 0 0],'MarkerFaceColor',[0.5 0.5 0.5]);hold on;
for i = 1:size(faceMx,1)
diamPlot = (diaVec(i)/max(diaVec))*3;
plot3([ptCoordMx(faceMx(i,1),1);ptCoordMx(faceMx(i,2),1)],...
[ptCoordMx(faceMx(i,1),2);ptCoordMx(faceMx(i,2),2)], ...
[ptCoordMx(faceMx(i,1),3);ptCoordMx(faceMx(i,2),3)],'r','linewidth',diamPlot);hold
on;
end
hold on; grid off; axis off;

if(labelChoice == 1) %Labels on or off


disp('With Labels')
%Plot points labels
for i = 1:size(ptCoordMx,1)
x = ptCoordMx(i,1)+0.8; y = ptCoordMx(i,2)+0.01; z = ptCoordMx(i,3);
str = ['P',num2str(i)];
text(x,y,z,str,'Fontsize',8.5);hold on;
end
else
disp('No Labels')
end
set(gcf,'color','w');
end

Appendix B: Stl to msh converter code in Delphi:


// Homai 10/19/2018
unit ooSTLtoMeshConverter;
interface

uses SysUtils, Dialogs, Classes, Math, StrUtils,Variants, Windows, psAPI,


ooRoot, SAEInterfaces, ooTubeMeshSource, ooUtilities.V2, ooCaseFileReaderSource,
ooGambitWriterSource, ooSplinedTubeSource;
type

STLReader = class (TObject)


NFaces, NPoints: integer;
LinesInFile: TStringList;

109
mesh: ooMesh;
constructor createfromSTLFiletoMesh(aSTLFileName:string);
procedure loadSTLFile(aSTLFileName:String);
function addPtAtNewOrExistingIdx(var aPtCoordMx: PDblMatrix;p1: PDblArray):integer;
procedure addPoint(var ptCoordMx: PDblMatrix;aP: PDblArray);
class procedure test();
private
function clean(aLine:String):string;
end;

implementation

procedure STLReader.loadSTLFile(aSTLFileName:String); //constructor ooCaseFileReader.Create


var s,localmeshFileName, FileName: string; STLLoaded: boolean;
begin
LinesInFile := TStringList.Create;
if not FileExists(aSTLFileName) then begin
ShowMessage('The selected STL file: ' + aSTLFileName + ' does not exist. Please check *.stl file');
STLLoaded := false; exit; end
else STLLoaded := true;
LinesInFile.LoadFromFile(aSTLFileName);
end;

// dervied from basic clean


function STLReader.clean(aLine:String):string;
begin
aLine := StringReplace(aLine, 'vertex', '',[rfReplaceAll]);
result := aLine;
end;

procedure STLReader.addPoint(var ptCoordMx: PDblMatrix;aP: PDblArray);


var oldNPoints,dimensions: integer;
begin
oldNPoints := length(ptCoordMX)-1; // the zero-th element is not used
NPoints := oldNPoints+1;
dimensions := 3;
SetLength(ptCoordMx, NPoints+1, dimensions); // on bigger than NPoints
CopyVectorContent(PDblArray(ptCoordMx[NPoints]), aP);
end;

function STLReader.addPtAtNewOrExistingIdx(var aPtCoordMx: PDblMatrix;p1: PDblArray):integer;


var Idx: integer; aP: PDblArray;
begin
result := -1;
// loop over the pointCoordMatrix
for Idx:= 1 to NPoints do begin
aP := PdblArray(aPtCoordMx[Idx]);
if IsIdentical(p1, aP) then begin
result := Idx; exit;
end;
end;
// if points is not in aPtCoordMx then add it to the pointCoordMx and return new Idx
if result = -1 then begin
// add point at the end of the mesh
addPoint(aPtCoordMx,P1);
result := NPoints; // the new index;

110
end;
end;

constructor STLReader.createfromSTLFiletoMesh(aSTLFileName: string);


var n, p1Idx, p2idx, p3Idx, iFacetLineIdx, grpID: integer;
ptCoordMx:PdblMatrix; p1,p2,p3,coordinate:pdblArray;
faceMx: PIntMatrix;aLine:string;
begin
NPoints := 0;
setlength(ptCoordMx,1,3); //dimentions
setlength(faceMx,2, 6);
grpId := 10000; //name eof the group
loadSTLFile(aSTLFileName);

NFaces:=0;
iFacetLineIdx := 3;
while iFacetLineIdx < LinesInFile.count-1 do begin
Nfaces := NFaces+1;
setlength(faceMx,NFaces+1, 6); // add an index for zeroth element
aLine := clean(LinesInFile[iFacetLineIdx]);
p1 := ooCaseFileReader.parseLineForDouble(aLine, n);
p1Idx := addPtAtNewOrExistingIdx(ptCoordMx, p1);
aLine := clean(LinesInFile[iFacetLineIdx+1]);
p2 := ooCaseFileReader.parseLineForDouble(aLine, n);
p2Idx := addPtAtNewOrExistingIdx(ptCoordMx, p2);
aLine := clean(LinesInFile[iFacetLineIdx+2]);
p3 := ooCaseFileReader.parseLineForDouble(aLine, n);
p3Idx := addPtAtNewOrExistingIdx(ptCoordMx, p3);
// NFaces points to the last faceIDx
PintArray(faceMx[NFaces]) := iVector([grpId , P1idx, P2Idx, P3Idx, 0, 0 ]);
//
iFacetLineIdx := iFacetLineIdx + 7;
end;
// needsa work --- try tyo avoid mesh
mesh:= ooMesh.create;
mesh.ptCoordMx := ptCoordMx;
mesh.faceMx := faceMx;
mesh.makeGroupMxFromFaceMx(mesh.groupMx,mesh.faceMx);
ooGambitWriter.CreateFlat('MyFirstSTLFileConervetedHoMai.msh', mesh.ptCoordMx, mesh.faceMx, mesh.groupMx, 0);
end;

class procedure STLReader.test();


var fileName:string;
begin
fileName:='C:\andi\MG.V1\PurePascalMGV88.1\Data\HomaiData\Cube_10_10_10HR.stl';
//fileName:='C:\andi\MG.V1\PurePascalMGV88.1\Data\HomaiData\IGICEM.stl';
//fileName:='C:\Users\Homa\Desktop\IG_vmtk_davidResults\UIC2012-1070_SubjectIV_COWmeshV2.stl';
//STLCreator(fileName);
//open(fileName);
//STLtoMesh(fileName);
STLReader.createfromSTLFiletoMesh(fileName);
end;

{ STLReader }

111
begin
STLReader.test();
end.

Appendix B: msh to stl converter code in Delphi:


// Homai 10/19/2018
// based on linearVascularTest by Lin
// Lin 8/21/2013
// Lin 1/3/2017 --- updated for compatibility with libraries
// Lin&GH 1/9/2017 -- visualization
unit HomaiMeshSTLConverter;

interface

uses SysUtils, Dialogs, Classes, Math, StrUtils,Variants, Windows, psAPI,


{ooRoot,} SAEInterfaces, ooTubeMeshSource, ooUtilities.V2, ooCaseFileReaderSource,ooRoot;

procedure STLwriterFromMesh();
//function Add(const S: string): Integer;
//function AddObject(const S: string; AObject: TObject): Integer; override;
implementation

{function TStringList.Add(const S: string): Integer;


begin
Result := AddObject(S, nil);
end;

function TStrings.AddObject(const S: string; AObject: TObject): Integer;


begin
Result := Add(S);
PutObject(Result, AObject);
end; }

procedure STLwriterFromMesh();
var fileName,FirstLine,Directory:string; aMesh:ooMesh; facMx:PIntMatrix; aline : TStringlist;
i,_p1Idx,_p2Idx,_p3Idx: Integer; Point1,Point2,Point3,Vector1,Vector2,normalVec:PdblArray;
begin
//read the face matrix from the mesh file
_p1Idx:=1; _p2Idx:=2; _p3Idx:=3;
fileName:= ('C:\andi\MG.V1\PurePascalMGV88.1\Data\HomaiData\MySTLConervetedmsh_cube_10_10_10.msh');
Directory:= 'C:\Users\Homa\Desktop';
aMesh := ooMesh.Create(fileName);
aline:= TStringlist.create;
aline.add('solid MESH');
for i := 1 to aMesh.NFaces do begin
Point1:= PDblArray(aMesh.ptCoordMx[aMesh.faceMx[i,_p1Idx]]);
Point2:= PDblArray(aMesh.ptCoordMx[aMesh.faceMx[i,_p2Idx]]);
Point3:= PDblArray(aMesh.ptCoordMx[aMesh.faceMx[i,_p3Idx]]);

112
Vector1:= getAsVector(Point1,Point2);
Vector2:= getAsVector(Point2,Point3);
normalVec:= aXb3d(Vector1,Vector2);
aline.add(' facet normal '+ printvectorAsstring(normalVec));
aline.add(' outer loop');
aline.add(' vertex '+ printvectorAsstring(Point1));
aline.add(' vertex '+ printvectorAsstring(Point2));
aline.add(' vertex '+ printvectorAsstring(Point3));
aline.add(' end loop');
aline.add(' endfacet');
end;
aline.SaveToFile(Directory+'\HR.stl');
end;
begin
STLwriterFromMesh();
end.

Appendix C: Registration application code.


// Lin 10/15/2018
// Interfaces for the 3d Registration application to show to Homa
// use 4 pts in Object1 and 4 pts in Object2 to solve for the transformation matrix
//
unit ooRegistrationApplicationSource;
interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, ComCtrls, StdCtrls, Buttons,

ooRoot, ooGLPanelSimpleByAL, ooGLPanelWithButtons, ooRendererSource,


SAEInterfaces, ooUtilities.V2, ooCaseFileReaderSource,ooXSolverSource;

type
TRegistrationForm = class(TForm)
PageControl1: TPageControl;
TabSheet1: TTabSheet;
Splitter1: TSplitter;
TabSheet2: TTabSheet;
BitBtn1: TBitBtn;
GroupBox1: TGroupBox;
Memo1: TMemo;
Memo2: TMemo;
GroupBox2: TGroupBox;
Memo3: TMemo;
SpeedButton1: TSpeedButton;
GroupBox3: TGroupBox;
Memo5: TMemo;
Memo4: TMemo;
PageControl2: TPageControl;

113
TabSheet3: TTabSheet;
Panel1: TPanel;
TabSheet4: TTabSheet;
procedure FormCreate(Sender: TObject);
procedure BitBtn1Click(Sender: TObject);
procedure testviews();
//Homai-- 10/22/2018
procedure SpeedButton1Click(Sender: TObject);
function VectorToSquerMatrix(A:PdblArray;N:integer):PdblMatrix;
function OffsetVector(a:PdblArray):PdblArray;
procedure transFormObject(JJ:PDblMatrix; b: PDblArray);
//function TransformedObj(b:pdblArray):PdblMatrix;
function MatrixMultiplication(A,B:PDblMatrix):PdblMatrix;
//function hardcodedTranspose(A:PdblMatrix):PdblMatrix;
private
{ Private declarations }
ptsObj1, ptsObj2:PDblMatrix;
public
{ Public declarations }
myGLPanel: TGLPanelSimple;
end;

var
RegistrationForm: TRegistrationForm;

implementation

{$R *.dfm}

procedure TRegistrationForm.FormCreate(Sender: TObject);


begin
myGLPanel := TGLPanelSimple.CreateOnExistingForm(Panel1);

end;

procedure TRegistrationForm.BitBtn1Click(Sender: TObject);


var aR1, aR2, aR3, aR4: PDblArray; n,i: integer;
begin
Setlength(ptsObj1,4,3);Setlength(ptsObj2,4,3);
for i:= 0 to 3 do begin
PdblArray(ptsObj1[i]) := ooCaseFileReader.parseLineForDouble(memo1.Lines[i], n);
PdblArray(ptsObj2[i]) := ooCaseFileReader.parseLineForDouble(memo2.Lines[i], n);
end;
testViews();
//testviewsHardCoded;
end;

function TRegistrationForm.VectorToSquerMatrix(A:PdblArray;n:integer):PdblMatrix;
var i,j,u:integer; B:Pdblmatrix;
begin

114
u:=0;
setlength(B,n,n);
for i := 0 to n-1 do begin
for j:=0 to n-1 do begin
u:=u+1;
B[i,j]:= A[u];
end;
end;
result:=B;
end;
function TRegistrationForm.OffsetVector(a:PdblArray):PdblArray;
begin
setlength(result,3);
result[0]:=a[10];
result[1]:=a[11];
result[2]:=a[12];
end;

procedure TRegistrationForm.transFormObject(JJ:PDblMatrix; b: PDblArray);


var i,n: integer; pt:PDblArray; InsertedPts:integer; ptsObj1:pdblArray; Obj1Coord:Tstringlist;
begin
InsertedPts:=4; n:=4; /// both numbers should change from hsrdcode to global vars //HR--10/29/2018
setlength(ptsObj1,3);
Obj1Coord:= TStringlist.create;
Obj1Coord.addStrings(Memo1.lines);
for i:= 1 to InsertedPts do begin
//PdblArray(ptsObj1[1]) := ooCaseFileReader.parseLineForDouble(memo1.Lines[1], n);
//pt := PdblArray(ptsObj1[1]);
pt:= parseLineForDouble(Obj1Coord[i]);
pt := AAx(JJ,pt);
ptsObj1 := plus(pt, b);
memo5.lines.addStrings(printvectorAsStringList(ptsObj1));
end;
end;

function TRegistrationForm.MatrixMultiplication(A,B:PDblMatrix):PdblMatrix;
var i,j,k:integer; sum:double;
begin
sum:=0;
setlength(result,3,3);
for i:=1 to 3 do begin
for j:=1 to 3 do begin
for k:=1 to 4 do begin
sum:= ((A[i,k]*B[k,j])+sum);
end;
result[i,j]:=sum;
sum:=0;
end;
end;
end;

115
{procedure TRegistrationForm.testviewsHardCoded();
var aR:ooRenderer; myPanel: TGLPanelSimple;
pts, pts2:PDblMatrix; labels: TStringList; lines: PDBlMxArray;
begin
Setlength(pts,4,3);
/// object1
copyVectorContent(PdblArray(pts[0]), Vector([0 ,0 ,0]));
copyVectorContent(PdblArray(pts[1]), Vector([10 ,10 ,0]));
copyVectorContent(PdblArray(pts[2]), Vector([11 ,1 ,1]));
copyVectorContent(PdblArray(pts[3]), Vector([12 ,2 ,2]));
labels:=TStringList.create; labels.add('A');labels.add('B');labels.add('C');labels.add('D');
aR := ooLabelRenderer.create(pts, labels);
aR.setDefaultColor(0);
myGLPanel.addRenderer(aR);
/// object2
Setlength(pts2,4,3);
copyVectorContent(PdblArray(pts2[0]), Vector([1 ,1 ,0]));
copyVectorContent(PdblArray(pts2[1]), Vector([11 ,11 ,0]));
copyVectorContent(PdblArray(pts2[2]), Vector([12 ,1 ,1]));
copyVectorContent(PdblArray(pts2[3]), Vector([13 ,2 ,2]));
labels:=TStringList.create; labels.add('A"');labels.add('B"');labels.add('C"');labels.add('D"');
aR := ooLabelRenderer.create(pts2, labels);
aR.setDefaultColor(1);
myGLPanel.addRenderer(aR);
end; }

procedure TRegistrationForm.testviews();
var aR:ooRenderer; myPanel: TGLPanelSimple;
pts, pts2:PDblMatrix; labels: TStringList; lines: PDBlMxArray;
begin
labels:=TStringList.create; labels.add('A');labels.add('B');labels.add('C');labels.add('D');
aR := ooLabelRenderer.create(ptsObj1, labels);
aR.setDefaultColor(0);
myGLPanel.addRenderer(aR);
/// object2
labels:=TStringList.create; labels.add('__A"');labels.add('___B"');labels.add('___C"');labels.add('___D"');
aR := ooLabelRenderer.create(ptsObj2, labels);
aR.setDefaultColor(1);
myGLPanel.addRenderer(aR);

labels:=TStringList.create; labels.add('____A""');labels.add('____B""');labels.add('____C""');labels.add('____D""');
aR := ooLabelRenderer.create(ptsObj1, labels);
aR.setDefaultColor(11);
myGLPanel.addRenderer(aR);
end;

procedure TRegistrationForm.SpeedButton1Click(Sender: TObject);


var Obj1Coord,Obj2Coord:TStringlist; p1,p2,p3,p4,pprim1,pprim2,pprim3,pprim4,b,OffsetVec:PdblArray;
transformationMx,projectedOBj2,transposeObj2: PdblMatrix;

116
begin
b:=vector(0,12); Setlength(transformationMx,4,3);
Setlength(projectedOBj2,4,3); Setlength(transposeObj2,4,3);
OffsetVec:=vector(0,3);
Obj1Coord:= TStringlist.create;
Obj1Coord.addStrings(Memo1.lines); // this command reads the lines on the memo
p1:= parseLineForDouble(Obj1Coord[1]);
P2:= parseLineForDouble(Obj1Coord[2]);
p3:= parseLineForDouble(Obj1Coord[3]);
p4:= parseLineForDouble(Obj1Coord[4]);

Obj2Coord:=Tstringlist.create;
Obj2Coord.addStrings(Memo2.lines);
pprim1:= parseLineForDouble(Obj2Coord[1]);
pprim2:= parseLineForDouble(Obj2Coord[2]);
pprim3:= parseLineForDouble(Obj2Coord[3]);
pprim4:= parseLineForDouble(Obj2Coord[4]);

//b:=TransformationMx(p1,p2,p3,p4,pprim1,pprim2,pprim3,pprim4);
b:=ooXSolver.Homaitest(p1,p2,p3,p4,pprim1,pprim2,pprim3,pprim4);
//memo3.lines.addStrings(printVectorAsStringList(b)); // shows the whole transformation vector including offset as
vector
transformationMx:= VectorToSquerMatrix(b,3);
OffsetVec:= OffsetVector(b);
memo3.lines.addStrings(printMatrixDense(transformationMx));
memo4.lines.addStrings(printVectorAsStringList(OffsetVec));
transFormObject(transformationMx,OffsetVec);
end;

end.

Appendix D: Code for saving the case file as a .casx file


This is a callback code for a button onClick on a form. The method uses a global parameter
(mesh) that has been previously instantiated. The writer creates the .casx file using the
aCaseFileName parameter. The saveToFile procedure saves the .casx file to the hard drive. The
aCaseFileName parameter must be a full file name including path. This procedure requires the
following procedures (currently located in ghCaseFileUtilities.pas). The findDataInFile function
returns a boolean true if data is found in the case file and false if there is only a diameter vector.
In this way, the method is robust; in the event that a case file does not have data, the procedure
just writes the file as is (with diameter vector only). The data is written using the
writeVectorProperty procedure.

procedure TForm2.Button2Click(Sender: TObject);


var minimalistCaseFileWriter:ghCaseXFileWriter; aPropNameList:TSTringList; aPropMx:PDblMatrix; i:integer;
begin
minimalistCaseFileWriter := ghCaseXFileWriter.create(aCaseFileName,mesh.dia,mesh);

117
if findDataInFile(aCaseFileName,mesh,aPropNameList,aPropMx) then begin
for i := Low(aPropMx) to High(aPropMx) do begin
if (aPropNameList[i] = 'flow') or (aPropNameList[i] = 'averagePressure') or (aPropNameList[i] = 'hematocrit') then
minimalistCaseFileWriter.writeVectorProperty(aPropNameList[i],PDBlArray(aPropMx[i]));
end;
end;
minimalistCaseFileWriter.saveToFile;
end;

Appendix E: Code for finding the data from the case file
This is a string of methods and procedures required to read data from file. The method first
searches the file for all events of the key “vector” and stores the names of the vector in a
tStringList. The dataMatrix is then filled using the
ooCaseFileReader.getTubeVectorPropertyForGroups procedure. This method does not currently
work for point property vectors and should be updated in the future.

Definitions:

function findDataInFileWithDiameter(aCaseFileName:string; nwk:ooTubeMesh; var aPropNameList:TSTringList; var


aPropValueVector:PDblMatrix):boolean;
procedure findPropertyVectors(linesInFile:TSTringList; var aPropNameList:TStringList);
procedure fillDataVectorsFromFile(aCaseFileR:ooCaseFileReader; aPropNameList:TSTringList; var
aPropValueVector:PDblMatrix; groupMx:PIntMatrix);

Implementation:

findDataInFile(aCaseFileName:string; nwk:ooTubeMesh; var aPropNameList:TSTringList; var


aPropValueVector:PDblMatrix):boolean;
var i:integer; aCaseFileR: ooCaseFileReader; aTempList:TstringList;
begin
aCaseFileR := ooCaseFileReader.Create(aCaseFileName);
aTempList := TSTringList.Create; findPropertyVectors(aCaseFileR.linesInFile, aTempList);
setLength(aPropValueVector,aTempList.Count, nwk.NFaces+1);
fillDataVectorsFromFile(aCaseFileR,aTempList,aPropValueVector,nwk.groupMx);

aPropNameList := TStringList.Create;
for i := 1 to aTempList.Count-1 do begin
aPropNameList.add(aTempList[i]); aPropValueVector[i-1] := aPropValueVector[i];//remove the diameter vector
end;
setLength(aPropValueVector,aPropNameList.Count,nwk.NFaces+1);
if aPropNameList.Count > 0 then result := true
else result := false;
end;

procedure findPropertyVectors(linesInFile:TSTringList; var aPropNameList:TStringList);


var i,n:integer; key,aLine,aPropName:string; aStrL:TStringList;
begin

118
key := 'vector=';
for i := 0 to LinesInFile.Count-1 do begin
aLine := LinesInFile[i];
if containsText(aLine,key) then begin
aStrL := TStringList.Create;
n := ExtractStrings([' '], [' '], Pchar(aLine), aStrL);
aPropName := StringReplace(aStrL[0], '(vector=','',[rfIgnoreCase]);
if aPropNameList.Count = 0 then aPropNameList.Add(aPropName)
else if not (aPropName = aPropNameList[aPropNameList.Count-1]) then aPropNameList.Add(aPropName);
end;
end;
end;

procedure fillDataVectorsFromFile(aCaseFileR:ooCaseFileReader; aPropNameList:TSTringList; var


aPropValueVector:PDblMatrix; groupMx:PIntMatrix);
var i:integer;
begin
for i := 0 to aPropNameList.Count-1 do begin //first set is always diameter
aCaseFileR.getTubeVectorPropertyForGroups(aPropNameList[i],PDblArray(aPropValueVector[i]),groupMx);
aPropValueVector[i,0] := aPropValueVector[i,1]; //for visualization, never let 1st index be 0, it affects range
end;
end;

Appendix F: Code for reading a casx file


This is a callback code for a button onClick on a form. The method creates a local case file
reader of type ghCaseXFileReader. This reader creates a mesh inside itself. The global parameter
(mesh) that is of type ooTubeMesh is then assigned to the mesh within the ghCaseXFileReader.

procedure TForm2.Button3Click(Sender: TObject);


var minimalistCaseFileReader:ghCaseXFileReader; aR:ooTubeRenderer; aPanel:TGLPanelWithButton;
begin
OpenDialog1.Title := 'Open a Vasculature Network File....';
OpenDialog1.Filter := 'Case Files|*.*';
if OpenDialog1.Execute then begin
aCaseFileName := OpenDialog1.Filename;
minimalistCaseFileReader := ghCaseXFileReader.create(aCaseFileName);
mesh := minimalistCaseFileReader.mesh;
end;
end;

Appendix G:Code for writing and reading a .casx file


This is a standalone class that includes both the ghCaseXFileWriter and ghCaseXFileReader
object definitions and implementations. Both objects include the same parameters; a
caseFileName (string), linesInFile (TStringList), and a mesh (ooTubeMesh). These parameters
are global and can be accessed by the user outside of the class.

119
//GHartung 10/9/2018 -- a minimalist way of storing case and network files
//Derived from ooCaseFileWriterSource by ALinninger
//since our vascular generation code has become so popularized, people want our structures, but instead of sharing the whole
logic
//of the case/nwk/GAMBIT format with them, it is easier to make a minimalist format for them, this is such a writer and
reader.
//takes a network and writes a single .casx file

unit ghCaseXFileSource;

interface

uses
Classes, SysUtils, SAEInterfaces, Dialogs,
ooRoot, ooTubeMeshSource, ooGambitWriterSource, ooCaseFileReaderSource, ooGambitConstants,
ooUtilities.V2, ghCaseFileUtilitiesSource;

const
ooVectorId = 'vector';
ghMinimalistCaseFileExt= '.casx';
pathTag = 'path=';
caseFileExt = '.cs31';
nwkExt = '.nwk';
authorString = 'File format designed by GHartung and ALinninger 10/9/2018';
dateString = 'This file was created by LPPD in Chicago,US on: ';
copyrightString = 'copyright by LPPD in Chicago,US; not for use without expressed permissions by ALinninger';
instructionString = 'the header tags are, "point coordinates", "connectivity matrix", and "vector"';
NoteString = 'note, there may be more than one vector';

ptCoordHeader = 'point coordinates';


connectivityHeader = 'arc connectivity matrix';
vectorHeaderFaces = ': vector on arc;';
vectorHeaderPoints = ': vector on arc;';
commentConstant = '//';
endSectionConstant = '//end ';

type
ghCaseXFileWriter = class
caseFileName:string;
LinesInFile: TStringList;
mesh: ooTubeMesh;
constructor create(aCaseFileName:String; aDvector: PDblArray; aMesh: ooTubeMesh; anExt:string=caseFileExt);
procedure writeHeader;
procedure addPointSection;
procedure addFaceSections;
function writeFace(aFaceInfo: PIntArray):string;
procedure writeVectorProperty(propName: string; anArray: PDblArray);
procedure saveToFile;
end;

120
ghCaseXFileReader = class
caseFileName:string;
LinesInFile: TStringList;
mesh: ooTubeMesh;
constructor create(matrixFileName:string);
procedure readPointCoordinates(var ptCoordMx: PDblMatrix; startIdx,endIdx:integer);
procedure readFaceMatrix(var faceMx: PIntMatrix; startIdx,endIdx:integer);
function parseLineForIntegersNew(aLine:string): PIntArray;
function parseLineForDoublesNew(aLine:string):PDblArray;
procedure readVector(var aVec:PDblArray; startIdx,endIdx:integer);
end;
ghTubeMesh = class(ooTubeMesh)
constructor createWithSize1;
end;

implementation

constructor ghTubeMesh.createWithSize1;
var i:integer;
begin
NPoints := 1; nFaces := 1; setLength(dia,1);
setLength(ptCoordMx,1,3); setLength(faceMx,1,6); setLength(groupMx,1,6);
setLength(pointMx,nPoints+1,maxPointCol); setLength(cellMx,nFaces+1,6);
end;

//minimalist writer //////////////////////////////////////////


//Create procedure allows you to create, then add more vectors on the fly, then saveToFile
//for this, use (1) Create, (2) writeVectorProperty, (3) saveToFile
constructor ghCaseXFileWriter.create(aCaseFileName:String; aDvector: PDblArray; aMesh: ooTubeMesh;
anExt:string=caseFileExt);
begin
LinesInFile := TStringList.create;
caseFileName := stringReplace(aCaseFileName,caseFileExt,'',[rfReplaceAll]);
caseFileName := stringReplace(aCaseFileName,nwkExt,'',[rfReplaceAll]);
caseFileName := aCaseFileName+ghMinimalistCaseFileExt;
mesh := aMesh;
writeHeader;
addPointSection;
addFaceSections;
writeVectorProperty('diameter',mesh.dia);
end;

procedure ghCaseXFileWriter.saveToFile;
begin saveToFileFromSTringList(linesInFile,caseFileName); end; //use method from ghCaseFileUtilities to save to file

procedure ghCaseXFileWriter.writeHeader;
var nwkFileNameWithoutPath: string;
begin
LinesInFile.add(commentConstant+modelHeader);
LinesInFile.add(commentConstant+authorString);

121
LinesInFile.add(commentConstant+dateString+DateToStr(Now));
LinesInFile.add(commentConstant+copyrightString);
LinesInFile.add(commentConstant+instructionString);
LinesInFile.add(commentConstant+NoteString);
LinesInFile.add(GambitBlank);
end;

procedure ghCaseXFileWriter.writeVectorProperty(propName: string; anArray: PDblArray);


var Header, aLine: string; iGrp, iFace, groupIdx, N: integer;
begin
if high(anArray) = mesh.NFaces then
LinesInFile.add(commentConstant+propName+vectorHeaderFaces+' nArcs='+intToStr(mesh.NFaces))
else if high(anArray) = mesh.NPoints then
LinesInFile.add(commentConstant+propName+vectorHeaderPoints+' nPoints='+intToStr(mesh.nPoints))
else showMessage('length of vector does not match points or faces');
for iFace := 1 to high(anArray) do begin
aLine := FloatToStr(anArray[iFace]);
LinesInFile.add(aLine);
end;
LinesInFile.add(endSectionConstant+propName);
LinesInFile.add(GambitBlank);
LinesInFile.add(GambitBlank);
end;

procedure ghCaseXFileWriter.addPointSection;
var i,j,I1, I2:integer; aLine: string;
begin
LinesInFile.add(commentConstant+ptCoordHeader+'; nPoints='+intToStr(mesh.nPoints));
for i := 1 to high(mesh.ptCoordMx) do begin
aLine := '';
for j := low(mesh.ptCoordMx[i]) to high(mesh.ptCoordMx[i]) do begin
aLine:= aLine + Format(' %15.10e' , [mesh.ptCoordMx[i,j]])+ ' '; end;
LinesInFile.add(aLine);
end;
LinesInFile.add(endSectionConstant+ptCoordHeader);
LinesInFile.add(GambitBlank);
LinesInFile.add(GambitBlank);
end;

procedure ghCaseXFileWriter.addFaceSections;
var iFace,Nfaces:integer; aLine:string;
begin
LinesInFile.add(commentConstant+connectivityHeader+'; nArcs='+intToStr(mesh.NFaces));
Nfaces := high(mesh.faceMx);
for iFace := 1 to Nfaces do begin
aLine := writeFace(PIntArray(mesh.faceMx[iFace]));
LinesInFile.add(aLine);
end;
LinesInFile.add(endSectionConstant+connectivityHeader);
LinesInFile.add(GambitBlank);

122
LinesInFile.add(GambitBlank);
end;

//only use ptIdx in minimalist method


function ghCaseXFileWriter.writeFace(aFaceInfo: PIntArray):string;
var i:integer;aLine:string;
begin for i:= 1 to 2 do aLine := aLine + Format('%s ', [IntToHexStr(aFaceInfo[i])]);
result := aLine; end;
/////////////////////////////////////////////////////////

//minimalist reader //////////////////////////////////////////


constructor ghCaseXFileReader.Create(matrixFileName:string);
var idxPtMxStart,idxFaceMxStart,idxDiaVectorStart,idxDiaVectorEnd,idxPtMxEnd,idxFaceMxEnd:integer;
tempMesh:ghTubeMesh;
begin
caseFileName := matrixFileName;
tempMesh := ghTubeMesh.createWithSize1;// need to create mesh here
mesh := ooTubeMesh(tempMesh);
linesInFile := TStringList.create;
linesInFile.LoadFromFile(caseFileName);
idxPtMxStart := LinesInFile.IndexOf(commentConstant+ptCoordHeader)+1;
idxPtMxEnd := LinesInFile.IndexOf(endSectionConstant+ptCoordHeader)-1;
idxFaceMxStart := LinesInFile.IndexOf(commentConstant+connectivityHeader)+1;
idxFaceMxEnd := LinesInFile.IndexOf(endSectionConstant+connectivityHeader)-1;
idxDiaVectorStart := LinesInFile.IndexOf(commentConstant+vectorHeaderFaces+'diameter')+1;
idxDiaVectorEnd := LinesInFile.IndexOf(endSectionConstant+'diameter')-1;
readPointCoordinates(mesh.ptCoordMx,idxPtMxStart,idxPtMxEnd);
readFaceMatrix(mesh.faceMx,idxFaceMxStart,idxFaceMxEnd);
readVector(mesh.dia,idxDiaVectorStart,idxDiaVectorEnd);
setLength(mesh.pointMx,mesh.NPoints +1, maxPointCol);
mesh.makeGroupMxFromFaceMx; mesh.makeTubeConnections(mesh.faceMx,mesh.pointMx);
end;

procedure ghCaseXFileReader.readPointCoordinates(var ptCoordMx: PDblMatrix; startIdx,endIdx:integer);


var iFace,i,j, toIdx, fromIdx:integer; faceInfo: PIntArray; aLine:string; isBoundary: boolean;SectionIdx: integer;
begin
iFace := 1; setLength(ptCoordMx,endIdx-startIdx+2,3);
for i := startIdx to endIdx do begin
copyVectorContent(pDblArray(ptCoordMx[iFace]),parseLineForDoublesNew(LinesInFile[i]));
iFace := iFace + 1;
end;
mesh.NPoints := iFace-1;
end;

procedure ghCaseXFileReader.readFaceMatrix(var faceMx: PIntMatrix; startIdx,endIdx:integer);


var iFace,i,j, toIdx, fromIdx:integer; faceInfo: PIntArray; aLine:string; isBoundary: boolean;SectionIdx: integer;
begin
iFace := 1; setLength(faceMx,endIdx-startIdx+2,5);
for i := startIdx to endIdx do begin

123
faceInfo := parseLineForIntegersNew(LinesInFile[i]);
for j := 0 to 4 do begin
case j of
0: faceMx[iFace,j] := 1;
1: faceMx[iFace,j] := faceInfo[0];
2: faceMx[iFace,j] := faceInfo[1];
3: faceMx[iFace,j] := 0;
4: faceMx[iFace,j] := 0;
end;
end;
iFace := iFace + 1;
end;
mesh.nFaces := iFace-1;
end;

procedure ghCaseXFileReader.readVector(var aVec:PDblArray; startIdx,endIdx:integer);


var iFace,i,j, toIdx, fromIdx:integer; faceInfo: PIntArray; aLine:string; isBoundary: boolean;SectionIdx: integer;
begin
setLength(aVec,endIdx-startIdx+2);
iFace := 1;
for i := startIdx to endIdx do begin
aVec[iFace] := strToFloat(LinesInFile[i]);
iFace := iFace + 1;
end;
end;

function ghCaseXFileReader.parseLineForIntegersNew(aLine:string): PIntArray;


var i,n:integer; aStrL:TStringList;
begin
aStrL := TStringList.Create;
n := ExtractStrings([' '], [' '], Pchar(aLine), aStrL);
Setlength(result, n);
for i := 0 to n-1 do result[i] := StrToInt('$'+ aStrL[i]);
aStrl.destroy;
end;

function ghCaseXFileReader.parseLineForDoublesNew(aLine:string):PDblArray;
var i,n:integer; aStrL: TStringList;
begin
aStrL := TStringList.Create;
n := ExtractStrings([' '], [' '], Pchar(aLine), aStrL);
Setlength(result,n);
for i:= 0 to n-1 do begin result[i] := StrToFloat(aStrL[i]); end;
aStrL.destroy;
end;

end.

Appendix H: Calling the icem mesh file reader

124
This is a callback procedure from an onClick action to a button on a Windows Form (TForm)
object. The procedure uses an openDialog function to allow the user to select a icem mesh file.
The global parameter mesh (type ooMesh).

procedure TForm2.Button1Click(Sender: TObject);


var aR:ooRenderer;
begin
OpenDialog1.Title := 'Select an ANSYS Volumetric Mesh file in ASCII Format...';
OpenDialog1.filter := 'MSH files|*.msh';
if OpenDialog1.execute then begin
aMeshFileName := openDialog1.filename;
memo1.Lines.clear();
memo1.Lines.Add('You have selected meshFile'+aMeshFileName);
aAR := ooANSYSReader.createFlat(aMeshFileName);
mesh := aAR.mesh;
SetLength(aMeshFileName, length(aMeshFileName)-3);
aMeshFileName := aMeshFileName + 'GAMBIT.msh';
memo1.Lines.Add('Mesh File is Loaded');
//aR := ooMesRenderer.Create();
end;
end;

Appendix G: Calling the ICEM writer

procedure TForm2.Button6Click(Sender: TObject);


var aW:ooANSYSwriter;
begin
aW := ooANSYSwriter.CreateAndSave(aMeshFileName,mesh);
end;
Appendix H: ICEM reader/writer source code

This is the code that encompasses the ooANSYSreader and ooANSYSwriter objects. These
objects each have the global parameters linesInFile (TStringList) and mesh (ooMesh). To
retrieve the mesh from an icem mesh writer, the user can access the public global mesh object
within the ooANSYSreader directly. Note, original lines by IGG and AL are still included to
show history of the reader.

//GH 10/31/2018 -- fully inhereted the code, major rewrites to streamline and make more robust.
//also, cleanup of unused information and methods
//updated header tags, how the program searches for header tags, and how to store volumes
//Created 2/24/12 to read .msh files created by ANSYS in the fluent format
//and then writes the file in the necessary GAMBIT format

unit ghANSYSMeshFileSource;

125
interface

uses WinTypes, WinProcs,Stdctrls,Classes,ExtCtrls,SysUtils,


Dialogs, Controls, Forms, IDGlobal, ooUtilities, strUtils,
ooRoot,SAEinterfaces, ooGambitConstants;

procedure __readMeshFromANSYSFileIntoLists(aFileName:string; var ptCoordMx: PDblMatrix; var faceMx,


groupMx:PIntMatrix);

const
ANSYSDim = '(0 " Created by : FLUENT'; //GH 10/31/2018 -- shortened
ANSYSFaces = '(13 (0'; //GH 10/31/2018 -- updated, ansys switchis this tag all the time
ANSYSBoundaryFaces = '(0 "Faces of zone PART.1")';
ANSYSZones = '(0 "Zone Sections")';
ANSYSPtBegin = '(10 ('; // beginning of points Block
ANSYSCellBegin = '(12 ('; // beginning of Cell Block
ANSYSFaceBegin = '(13 ('; // beginning of face block

//GH -- for writer


ANSYSHeader = '(0 " Created by : Fluent_V6 Interface Vers. 14.0.3")';
ANSYSDimensions = '(2 3)';
ANSYSPointHeader = '(0 "Node Section")';

type
ooANSYSReader = Class(TObject)
IdxS1, IdxS2, IdxS3, IdxS4: integer;
mesh:ooMesh;
thefile: TextFile;
LinesInFile: TStringList;
aLine: String;

pointList:TList;
tempFMx: PIntMatrix;
bArray: array of boolean; //array of isBoundaryGroup

constructor createFlat(Fname: String);


function readPointSectionHeader(var nrPoints, fromIdx, toIdx, dimensions: integer): integer;
function findLineContainingKey(aKey:string):integer;
function findStringBetweenLines(idx1,idx2:integer;aString:string):integer;
function Clean(var aLine: string): string;
procedure readPointCoordinates(var ptCoordMx:PDblMatrix;dimensions, fromIdx, toIdx:integer); virtual;
function parseLineForDoublesNew(aLine:string):PDblArray;
procedure readFaceMatrixAndGroups(var faceMx,aGrMx:PIntMatrix); virtual;
function readGlobalFaceSectionHeader(var nrFaces, nrOfPointsPerFace: integer): integer;
function readFaceSectionFlat(var faceMx: PIntMatrix; startIdx: integer; var endIdx, iFace: integer; var aGrMx: PIntMatrix):
boolean;
class procedure StoreSectionInGroupMX(var aGM: PIntMatrix; aFaceHeader: PIntArray);
function parseForNrOfVolumes: integer;
function getIndexFromHeader(lineIndex, headerIndex: integer): integer;

126
procedure allocateFaceMx(nrOfPointsPerFace: integer);
procedure readFaceSection(var faceMx: PIntMatrix; startIdx: integer; var endIdx, iFace: integer; var aGrMx: PIntMatrix);
procedure updateFaceInfo(var faceInfo: PIntArray; iFace: integer);
procedure swapBoundaryFaceValues(anArray: PIntArray);

//GH
function parseFaceMxForNVolumes:integer;

private
procedure arrangeFaceMx(var tempFMx, faceMx: PIntMatrix);
procedure addSectionToFaceMx(var tempFMx, faceMx: PIntMatrix;
var grpIdx, faceCount: integer);
procedure makeGroupMxFromFaceMx(var aGroupMx, aFaceMx: PIntMatrix);
end;

ooANSYSwriter = Class(TObject)
meshFileName:string;
LinesInFile: TStringList;
mesh: ooMesh;
sectionCounter:integer;
constructor createAndSave(aMeshFileName:String; aMesh: ooMesh);
procedure writeHeader;
procedure addPointSection;
procedure addVolumeSection;
procedure addFaceSections;
function writeFace(aFaceInfo: PIntArray):string;
procedure addZoneSection;
procedure saveToFile;
End;

//GH
ghMesh = class(ooMesh)
constructor createWithSize1;
end;

function IntToHexStr(i:integer):string;

function parseLineForIntegersNew(aLine:string):PIntArray;
function IsThisABoundarySection(aLine:string): boolean;

implementation

var aAR: ooANSYSReader;

///////////////////////////////////// reader //////////////////////////////////////////////////


constructor ooANSYSReader.createFlat(Fname:String);
var str:string; dimensions: integer;fromIdx, toIdx, pointIdx:integer;
begin
LinesInFile := TStringList.create;

127
if not FileExists(Fname) then begin ShowMessage('The selected meshfile: ' + Fname + ' does not exist. Please check *.msh
file'); exit; end;
LinesInFile.LoadFromFile(Fname);
IDxS1 := findLineContainingKey(ANSYSDim); //GH 10/31/2018
IdxS2 := findLineContainingKey(ANSYSFaces);
IdxS3 := LinesInFile.IndexOf(ANSYSZones);
if (IdxS1 = -1) then begin ShowMessage(ANSYSDim + ' tag not found'); exit; end;
if (IdxS2 = -1) then begin ShowMessage(ANSYSFaces + ' tag not found'); exit; end; //GH 7/26/2018 -- wrong statement
in warning
if (IdxS3 = -1) then begin ShowMessage(ANSYSZones + ' tag not found'); exit; end;
LinesInFile.Delimiter := #0032;
LinesInFile.QuoteChar := #0032;
//need to instantiate mesh
mesh := ooMesh(ghMesh.createWithSize1); //GH method for making a mesh from nothing
// read points section
readPointSectionHeader(mesh.NPoints,fromIdx, toIdx, dimensions);
Setlength(mesh.ptCoordMx,mesh.NPoints+1, dimensions); // 2 or 3 dimensional Points section
readPointCoordinates(mesh.ptCoordMx, dimensions, fromIdx, toIdx);
// read face section
SetLength(mesh.groupMx , maxGroupRow, maxGroupCol);
readFaceMatrixAndGroups(mesh.faceMx, mesh.groupMx);
end;

//GH 10/31/2018 -- a method that allows finding idx of partial string in a line
function ooAnsysReader.findLineContainingKey(aKey:string):integer;
var i:integer;
begin
result := -1;
for i := 0 to LinesInFile.Count-1 do begin
aLine := LinesInFile[i];
if containsText(aLine,aKey) then begin result := i; break; end;
end;
end;

function ooANSYSReader.readPointSectionHeader(var nrPoints,fromIdx,toIdx, dimensions: integer):integer;


var idx:integer;aLine:string;ptArray: PIntArray;
begin
//Find First Point Block
idx := findStringBetweenLines(IdxS1, IdxS2, ANSYSPtBegin);
aLine := LinesInFile[idx+1]; // skip a line
ptArray := parseLineForIntegersNew(Clean(aLine));
fromIdx := idx + 2 + ptArray[1];
toIdx := idx + 2 + ptArray[2];
dimensions := ptArray[4]; // 2 means 2d points coordinates (x,y), 3 means 3d point coordinates (x,y,z)
// Lin 1/4/2008
nrPoints := toIdx-fromIdx+1;
end;

function ooANSYSReader.findStringBetweenLines(idx1,idx2:integer;aString:string):integer;
var i:integer;

128
begin
for i := idx1 to idx2 do begin;
if Pos(aString, LinesInFile[i]) <> 0 then begin result := i; exit; end;
end;
result := -1;
end;

function ooANSYSReader.Clean(var aLine:string):string;


var pos1, pos2:integer; s: string;
begin
aLine := StringReplace(aLine, ANSYSPtBegin, '',[rfReplaceAll]);
aLine := StringReplace(aLine, ANSYSFaceBegin, '',[rfReplaceAll]);
aLine := StringReplace(aLine, ANSYSCellBegin, '',[rfReplaceAll]);
aLine := StringReplace(aLine, ')', '',[rfReplaceAll]);
aLine := StringReplace(aLine, '(', '',[rfReplaceAll]);
result := aLine;
end;

procedure ooANSYSReader.readPointCoordinates(var ptCoordMx:PDblMatrix; dimensions, fromIdx, toIdx:integer);


var i,j, pointIdx:integer; aPtCoordArray: PDblArray;
begin
pointIdx := 1;
for i := fromIdx to toIdx do begin
aPtCoordArray := parseLineForDoublesNew(LinesInFile[i]);
for j := 0 to dimensions-1 do begin ptCoordMx[pointIdx, j] := aPtCoordArray[j];end;
pointIdx := pointIdx+1;
end;
end;

function ooANSYSReader.parseLineForDoublesNew(aLine:string):PDblArray;
var i,n:integer; aStrL: TStringList;
begin
aStrL := TStringList.Create;
n := ExtractStrings([' '], [' '], Pchar(aLine), aStrL);
Setlength(result,n);
for i:= 0 to n-1 do begin result[i] := StrToFloat(aStrL[i]); end;
aStrL.destroy;
end;

procedure ooANSYSReader.readFaceMatrixAndGroups(var faceMx,aGrMx:PIntMatrix);


var fromIdx, toIdx,iFace:integer; isBoundary:boolean; nrOfPointsPerFace: integer;
grpCount: integer;
begin
fromIdx := readGlobalFaceSectionHeader(mesh.NFaces, nrOfPointsPerFace); // total number of Faces
allocateFaceMx(nrOfPointsPerFace); // Read 2Pt faceMx or 3Pt faceMx
SetLength(tempFMx, length(faceMx), length(faceMx[0]));
iFace := 1; // write into first row of matrix

grpCount := 0;

129
while (iFace < mesh.NFaces+1) do begin
SetLength(bArray,grpCount+1);
bArray[grpCount] := readFaceSectionFlat(tempFMx, fromIdx, toIdx, iFace, aGrMx);
fromIdx := toIdx; grpCount := grpCount +1;
end;
arrangeFaceMx(tempFMx, faceMx);
makeGroupMxFromFaceMx(aGrMx, faceMx);
mesh.iNVolumes := parseForNrOfVolumes; // changed IGG 2/28/12
end;

procedure ooANSYSReader.arrangeFaceMx(var tempFMx, faceMx: PIntMatrix);


var faceCount, grpIdx, iFace: integer;
begin
faceCount := 1;
for grpIdx := low(bArray) to high(bArray) do
if bArray[grpIdx] = True then
addSectionToFaceMx(tempFMx, faceMx, grpIdx, faceCount);
for grpIdx := low(bArray) to high(bArray) do
if bArray[grpIdx] = False then
addSectionToFaceMx(tempFMx, faceMx, grpIdx, faceCount);
end;

procedure ooANSYSReader.addSectionToFaceMx(var tempFMx, faceMx: PIntMatrix; var grpIdx, faceCount: integer);


var iFace: integer;
begin
for iFace := mesh.groupMx[grpIdx,_b] to mesh.groupMx[grpIdx,_e] do begin
faceMx[faceCount] := tempFMx[iFace];
faceCount := faceCount +1;
end;
end;

procedure ooANSYSReader.allocateFaceMx(nrOfPointsPerFace:integer);
begin
if nrOfPointsPerFace = 2 then SetLength(mesh.faceMx,mesh.NFaces+1, MaxFaceCol); // 2Pt faces
if nrOfPointsPerFace = 3 then SetLength(mesh.faceMx,mesh.NFaces+1, MaxFaceCol+1); // 3Pt faces
if nrOfPointsPerFace = 4 then SetLength(mesh.faceMx,mesh.NFaces+1, MaxFaceCol+2); // 4Pt faces
end;

function ooANSYSReader.readGlobalFaceSectionHeader(var nrFaces, nrOfPointsPerFace: integer):integer;


var aLine:string;ptArray: PIntArray;
begin
nrFaces := getIndexFromHeader(IdxS2, 2); //GH 10/31/2018 -- to read the correct line
nrOfPointsPerFace := getIndexFromHeader(IdxS2+2, 4); //GH 10/31/2018
result := IdxS2+2; //GH 10/31/2018
end;

function ooANSYSReader.getIndexFromHeader(lineIndex, headerIndex:integer):integer;


var aLine:string;ptArray: PIntArray;
begin
aLine := LinesInFile[lineIndex];

130
ptArray := parseLineForIntegersNew(Clean(aLine));
result := ptArray[headerIndex];
end;

function ooANSYSReader.readFaceSectionFlat(var faceMx: PIntMatrix;startIdx:integer; var endIdx, iFace: integer; var


aGrMx: PIntMatrix):boolean;
var i,j, toIdx, fromIdx:integer; faceInfo: PIntArray; aLine:string; isBoundary: boolean;SectionIdx: integer;
begin
// read section header
aLine := LinesInFile[startIdx];
faceInfo := parseLineForIntegersNew(Clean(aLine));
// Section Index;
sectionIdx := faceInfo[0];
updateFaceInfo(faceInfo, iFace); //update faceInfo for writing groupMx accordingly
StoreSectionInGroupMX(aGrMx, faceInfo);
fromIdx := startIdx + 1;
// Lin 5/10/2010 - read one line less
toIdx := fromIdx + (faceInfo[2] - faceInfo[1]) ; // number of new faces
result := IsThisABoundarySection(LinesInFile[fromIdx]);
for i := fromIdx to toIdx do begin
faceInfo := parseLineForIntegersNew(LinesInFile[i]);
swapBoundaryFaceValues(faceInfo); // Lin 10/2/2005 - Swap if face starts with a zero
for j := low(faceInfo) to high(faceInfo) do
begin faceMx[iFace,j+1] := faceInfo[j]; end; //j+1 so store faces correctly IGG 2/24/12
// Lin 1/6/2008
faceMx[iFace,_GroupID]:=sectionIdx;
iFace := iFace + 1;
end;
endIdx := toIdx + 4; // push to new face and include two linefeeds for ');)' and header
end;

procedure ooANSYSReader.makeGroupMxFromFaceMx(var aGroupMx,aFaceMx:PIntMatrix);


var cGrp, pGrp: TGroupIdx; iFace: TFaceIdx; bi, ei: integer;faceInfo:PintArray;
begin
aGroupMx := nil;
SetLength(aGroupMx , maxGroupRow, maxGroupCol);
bi := 1; pGrp := aFaceMx[1,_GroupId]; // first section
Setlength(faceInfo,5);
for iFace := 1 to high(aFaceMx) do begin
cGrp := aFaceMx[iFace,_GroupId];
if cGrp <> pGrp then begin // this is the end of a section
// store grp indices
ei := iFace -1;
faceInfo[_GroupID]:= pGrp; faceInfo[_b]:= bi; faceInfo[_e]:= ei;
StoreSectionInGroupMX(aGroupMx,faceInfo);
// start new grp
bi := iFace;
pGrp := cGrp;
end;
end;

131
// write last group
ei := iFace -1;
faceInfo[_GroupID]:= pGrp; faceInfo[_b]:= bi; faceInfo[_e]:= ei;
StoreSectionInGroupMX(aGroupMx,faceInfo);
end;

procedure ooANSYSReader.updateFaceInfo(var faceInfo: PIntArray; iFace: integer);


begin
faceInfo[2] := faceInfo[2] - faceInfo[1] +iFace;
faceInfo[1] := iFace;
end;

procedure ooANSYSReader.readFaceSection(var faceMx: PIntMatrix;startIdx:integer; var endIdx, iFace: integer; var aGrMx:
PIntMatrix);
var i,j, toIdx, fromIdx:integer; faceInfo: PIntArray; aLine:string; isBoundary: boolean;SectionIdx: integer;
begin
// read section header
aLine := LinesInFile[startIdx];
faceInfo := parseLineForIntegersNew(Clean(aLine));
// Section Index;
sectionIdx := faceInfo[0];
StoreSectionInGroupMX(aGrMx, faceInfo);
fromIdx := startIdx + 1;
// Lin 5/10/2010 - read one line less
toIdx := fromIdx + (faceInfo[2] - faceInfo[1]) ; // number of new faces
//result := IsThisABoundarySection(fromIdx);
for i := fromIdx to toIdx do begin
faceInfo := parseLineForIntegersNew(LinesInFile[i]);
swapBoundaryFaceValues(faceInfo); // Lin 10/2/2005 - Swap if face starts with a zero
for j := low(faceInfo) to high(faceInfo) do
begin faceMx[iFace,j+1] := faceInfo[j]; end; //j+1 so store faces correctly IGG 2/24/12
// Lin 1/6/2008
//storeSectionIdxInCurrentFace(iFace,faceMx,sectionIdx);
faceMx[iFace,_GroupID]:=sectionIdx;
iFace := iFace + 1;
end;
endIdx := toIdx + 4; // push to new face and include two linefeeds for ');)' and header
end;

class procedure ooANSYSReader.StoreSectionInGroupMX(var aGM: PIntMatrix;aFaceHeader: PIntArray);


var i:integer;
begin for i:= low(aGM) to high(aGM) do begin
if aGM[i,_GroupID] = 0 then begin
aGM[i,_GroupID] := aFaceHeader[_groupId];
aGM[i,_b] := aFaceHeader[_b];
aGM[i,_e] := aFaceHeader[_e];
aGM[i,_i1] := aFaceHeader[_i1];
aGM[i,_i2] := aFaceHeader[_i2];
exit; end;
end;

132
end;

function ooANSYSReader.parseForNrOfVolumes:integer;
var I1:integer; ptArray: PIntArray;
begin
I1 := findStringBetweenLines(0, LinesInFile.count-1, ANSYSCellBegin);
// if length(aLine) > 0 then begin //GH
if I1 > 0 then begin
aLine := LinesInFile[I1];
ptArray := parseLineForIntegersNew(Clean(aLine));
result := ptArray[2];
end
else result := parseFaceMxForNVolumes;
// else result := 0;
end;

//GH -- this method is time consuming and poorly written, but is a bandaid for an impressing deadline
function ooANSYSReader.parseFaceMxForNVolumes:integer;
var i,ptsInFace:integer; volumeList:PIntArray;
begin
mesh.iNFaces := 0; mesh.bNFaces := 0;
setLength(volumeList,mesh.NFaces+1); //dangerous assumption here - nVolumes is <= nFaces
ptsInFace := mesh.nrOfPtsInFaceMx;
for i := 1 to mesh.nFaces do begin
addIdxNoDuplicate(mesh.faceMx[i,1+ptsInFace],volumeList);
addIdxNoDuplicate(mesh.faceMx[i,1+ptsInFace+1],volumeList);
if (mesh.NFaces-howManyNonZeroElements(volumeList))<2 then
setLength(volumeList,howManyNonZeroElements(volumeList)+2);
if isIdxInArray(0,PIntArray(mesh.faceMx[i])) then mesh.bNFaces := mesh.bNFaces + 1
else mesh.iNFaces := mesh.iNFaces + 1
end;
removeTrailingZeros(volumeList); result := length(volumeList);
end;

procedure ooANSYSReader.swapBoundaryFaceValues(anArray: PIntArray);


var anIndex: integer;
begin
if anArray[4] = 0 then exit;
if anArray[3] = 0 then begin
anIndex := anArray[1]; anArray[1] := anArray[2]; anArray[2]:= anIndex; // swap point indixes
anIndex := anArray[3]; anArray[3] := anArray[4]; anArray[4]:= anIndex; // swap volume indices
end;
end;
///////////////////////////////////// end reader //////////////////////////////////////////////////

///////////////////////////////////// writer //////////////////////////////////////////////////


constructor ooANSYSwriter.createAndSave(aMeshFileName:String; aMesh: ooMesh);
begin
LinesInFile := TStringList.create;

133
meshFileName := stringReplace(aMeshFileName,'.msh','.icem.msh',[]);
mesh := aMesh;
sectionCounter := 1;
writeHeader;
addPointSection;
addVolumeSection;
sectionCounter := sectionCounter + 1;
addFaceSections;
sectionCounter := sectionCounter + 1;
addZoneSection;
saveToFile;
end;

procedure ooANSYSwriter.writeHeader;
begin
linesInFile.Add(ANSYSHeader);
linesInFile.Add(ANSYSDimensions);
linesInFile.Add(ANSYSPointHeader);
end;

procedure ooANSYSwriter.addVolumeSection;
var aLine:string;
begin
aLine := '(12 (0 1 '+ IntToHexStr(mesh.iNVolumes) + ' 0 3))'; LinesInFile.add(aLine);
aLine := '(12 (1 1 '+ IntToHexStr(mesh.iNVolumes) + ' 1 2))'; LinesInFile.add(aLine);
end;

procedure ooANSYSwriter.addPointSection;
var aLine:string; i,j:integer;
begin
aLine := '(10 (0 1 '+ IntToHexStr(mesh.nPoints)+' 0 0))'; LinesInFile.add(aLine);
aLine := '(10 (1 1 ' + IntToHexStr(mesh.nPoints) + ' ' + intToStr(sectionCounter) + ' 3)'; LinesInFile.add(aLine);
LinesInFile.add('(');
for i := 1 to high(mesh.ptCoordMx) do begin
aLine := '';
aLine:= aLine + floatToStr(mesh.ptCoordMx[i,_X])+ ' ';
aLine:= aLine + floatToStr(mesh.ptCoordMx[i,_Y])+ ' ';
aLine:= aLine + floatToStr(mesh.ptCoordMx[i,_Z]);
LinesInFile.add(aLine);
end;
LinesInFile.add('))');
end;

procedure ooANSYSwriter.addFaceSections;
var i,Nfaces,fromIdx, toIdx, sectionIdx:integer; aLine:string;iFace, iG, groupIdx:integer;
begin
Nfaces := high(mesh.faceMx);
aLine := '(13 (0 1 '+ IntToHexStr(NFaces) + ' 0 0))'; LinesInFile.add(aLine);
iFace := 1;
for iG := low(mesh.groupMx) to high(mesh.groupMx) do begin

134
groupIdx := mesh.groupMx[iG,_GroupID];
if groupIdx = 0 then break;
if mesh.groupMx[iG,1] > mesh.bnVolumes then aLine := '(0 "Interior faces of zone ' + IntToHexStr(groupIdx)+'")'
else aLine := '(0 "Faces of zone ' + IntToHexStr(groupIdx)+'")';
LinesInFile.add(aLine);
aLine := '(13 ('+IntToHexStr(groupIdx)+' ' + IntToHexStr(mesh.groupMx[iG,_b])+ ' '+
IntToHexStr(mesh.groupMx[iG,_e]) +
' ' + intToStr(sectionCounter)+' 3)(';
fromIdx := LinesInFile.add(aLine);
for iFace := mesh.groupMx[iG,_b] to mesh.groupMx[iG,_e] do begin
aLine := writeFace(PIntArray(mesh.faceMx[iFace]));
LinesInFile.add(aLine);
end;
LinesInFile.add(')'); LinesInFile.add(')');
end;
end;

function ooANSYSwriter.writeFace(aFaceInfo: PIntArray):string;


var i:integer;aLine:string;
begin
if length(aFaceInfo) = 5 then begin
aLine := ''; // 2Pt faces
if aFaceInfo[_v2Idx] > mesh.iNVolumes then aFaceInfo[_v2Idx] := 0;
end;
if length(aFaceInfo) = 6 then begin
aLine := ''; // 3Pt faces
//IGG 8/9/12 bNVolumes must be written to msh file as zeros!
if aFaceInfo[_v3Idx] > mesh.iNVolumes then aFaceInfo[_v3Idx] := 0;
end;
if length(aFaceInfo) = 7 then begin
aLine := ''; // 4Pt faces
//IGG 8/9/12 bNVolumes must be written to msh file as zeros!
if aFaceInfo[6] > mesh.iNVolumes then aFaceInfo[6] := 0;
end;
for i:= 1 to high(aFaceInfo) do begin
aLine := aLine + Format('%s ', [IntToHexStr(aFaceInfo[i])]);
end;
result := aLine;
end;

//GH -- this procedure assumes the boundary faces come first


//Sections of file: 1->points, volumes, 3+ ->faces
procedure ooANSYSwriter.addZoneSection;
var aLine:string; i,j:integer;
begin
mesh.iNFaces := mesh.nFaces - mesh.bNFaces;
aLine := '(0 "Zone Sections")'; LinesInFile.add(aLine);
LinesInFile.add('(39 (' + IntToHexStr(2) + ' fluid 2)())');
for i := 2 to sectionCounter do begin
if (mesh.groupMx[i-2,2] <= mesh.bNFaces) then

135
LinesInFile.add('(39 (' + IntToStr(mesh.groupMx[i-2,0]) + ' wall ' + IntToHexStr(mesh.groupMx[i-2,0])+')())')
else LinesInFile.add('(39 (' + IntToStr(mesh.groupMx[i-2,0]) + ' interior int_' + IntToHexStr(mesh.groupMx[i-
2,0])+')())');
end;
end;

procedure ooANSYSwriter.saveToFile;
begin linesInFile.SaveToFile(meshFileName); end;
///////////////////////////////////// end writer //////////////////////////////////////////////////

// a method that creates a mesh object


constructor ghMesh.createWithSize1;
var i:integer;
begin
NPoints := 1; nFaces := 1;
setLength(ptCoordMx,1,3); setLength(faceMx,1,6); setLength(groupMx,1,6);
setLength(pointMx,nPoints+1,maxPointCol); setLength(cellMx,nFaces+1,6);
end;

procedure __readMeshFromANSYSFileIntoLists(aFileName:string; var ptCoordmx: PDblMatrix; var


faceMx,groupMx:PIntMatrix);
begin aAr := ooANSYSReader.createFlat(aFileName); end;

//ripped from ooGambitWriterSource


function IntToHexStr(i:integer):string;
begin
result := IntTohex(i,1);
end;

function parseLineForIntegersNew(aLine:string): PIntArray;


var i,n:integer; aStrL:TStringList;
begin
aStrL := TStringList.Create;
n := ExtractStrings([' '], [' '], Pchar(aLine), aStrL);
Setlength(result, n);
for i := 0 to n-1 do result[i] := StrToInt('$'+ aStrL[i]);
aStrl.destroy;
end;

function IsThisABoundarySection(aLine:string):boolean;
var firstFaceInfo: PIntArray;
begin
// firstFaceInfo := parseLineForIntegersNew(LinesInFile[firstLineIdx]);
firstFaceInfo := parseLineForIntegersNew(aLine);
if (firstFaceInfo[high(firstFaceInfo)] = 0) or ((firstFaceInfo[high(firstFaceInfo)-1] = 0)) then result := TRUE
else result := False;
end;

end.

136
Appendix I: Calling the Gambit Mesh Writer (already known, but included for
completeness)
procedure TForm2.Button2Click(Sender: TObject);
var aCaseFileWriter:ooCaseFileWriter; aGambitWriter:ooGambitWriter; aDiaVect:PDblArray;
begin // save mesh file button //GH 12/18/2017 -- add save mm file
aGambitWriter := ooGambitWriter.CreateFlat(aMeshFileName, aAR.mesh.ptCoordMx, aAR.mesh.faceMx,
aAR.mesh.groupMx, aAR.mesh.NVolumes);
writeMMFile(aMeshFileName);
end;

Calling the GAMBIT reader (already known, but included for completeness)

procedure TForm2.Button5Click(Sender: TObject);


begin
OpenDialog1.Title := 'Select an ANSYS Volumetric Mesh file in ASCII Format...';
OpenDialog1.filter := 'MSH files|*.msh';
if OpenDialog1.execute then begin
aMeshFileName := openDialog1.filename;
mesh := ooMesh.Create(aMeshFileName);
end;
end;
Appendix J: Code for creating tube mesh from file (already known, but included for
completeness)
This is a callback code for a button onClick on a form. The method creates a global
parameter (mesh) that is of type ooTubeMesh. This mesh is instantiated from the
ooTubeMesh.CreateFromCasefile method.

procedure TForm2.selectCaseFileButtonClick(Sender: TObject);


var aR:ooTubeRenderer; aPanel:TGLPanelWithButton;
begin
OpenDialog1.Title := 'Open a Vasculature Network File....';
OpenDialog1.Filter := 'Case Files|*.cs31;*.cs4';
if OpenDialog1.Execute then begin
aCaseFileName := OpenDialog1.Filename;
Caption := 'CurrentCase: ' + aCaseFileName;
mesh := ooTubeMesh.CreateFromCaseFile(aCaseFileName);
end;
end;
Appendix k: Code for writing a .cs31 file (already known, but included for the sake of
completeness)
This is a callback code for a button onClick on a form. The method uses a global parameter
(mesh) that is of type ooTubeMesh and writes the .cs31 and .nwk file using the method
ooTubeMesh.SaveAsCaseAndNWKFile.

137
procedure TForm2.Button5Click(Sender: TObject);
begin
aCaseFileName := stringreplace(aCaseFileName, 'casx','',[]);
mesh.saveAsCaseAndNWKFile(aCaseFileName+'v2.cs31');
finishedLabel.Caption := 'finished';
end;

138

You might also like