Examples and mini tutorial on using the tecio library
 
Formatting structured data for plotting with AMTEC's TecPlot
When plotting data, one typically formats the data according to what
software package will be used for visualization. Many use in-house programs,
or programs that are freely distributable and widely available, such as
GNUplot. In the CFD community, it is common practice to use TecPlot, a very
nice software package made by AMTEC. It is easy to format data for plotting
with TecPlot. Typically one "dumps" the data as ASCII text and TecPlot reads
them accordingly.
However, using text files for output can be heavy on disk usage -- because
there are several characters (bytes) used to represent one number that has
the precision of just a few bytes (4 for floats, 8 for doubles, etc). If
you add to this all the delimiter symbols (commas or spaces) and newline
characters, there is a lot of wasted disk space! Additionally, writing
and reading large datafiles takes a long time, and in some cases TecPlot
has to go through a conversion process from text to its native format
before loading the data, which forces the use of temporary data files,
and surely introduces extra steps to the visualization process.
A solution to all these problems was provided by AMTEC. A library was
issued which can be used at compile time and creates native TecPlot binary
files directly from your own program. The library is called "tecio.a"
and comes with a standard installation of TecPlot. Although I am not aware
of how TecPlot and the library deal with the "endian-ness" of the machine
that is used, it has worked flawlessly for me, so here is an example of
how to use it!
Here is the "naive" FORTRAN example for writing structured datasets (code.f),
which can be compiled as:
f90 code.f -o code /path_where_library_resides/tecio.a
In this example a Cartesian grid is created and the variable u and
v are filled with integers for each of the nodes, with increasing
u and v for increasing i and j respectively.
The plotting routine is by no means general, and should only serve as
an example for this case (but you have figured that already). I do not
think there is a need to disect this code, so here it is:
|
program main
parameter(ni=10,nj=50)
Real*8 x(ni,nj),y(ni,nj)
Real*8 u(ni,nj),v(ni,nj)
do i = 1,ni
do j = 1,nj
x(i,j) = dble(i-1)/dble(ni-1)
y(i,j) = dble(j-1)/dble(nj-1)
u(i,j) = dble(i-1)
v(i,j) = dble(j-1)
enddo
enddo
call plot_tecplot(ni,nj,x,y,u,v)
stop
end
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C *** Subroutine to write a .plt file (uses TecPlot tecio.a library)
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
subroutine plot_tecplot(ni,nj, x,y,u,v)
implicit none
integer ni,nj
Real*8 x(ni,nj),y(ni,nj)
Real*8 u(ni,nj),v(ni,nj)
integer i,j,imax,jmax,kmax
integer debug,ier,itot
integer tecini,tecdat,teczne,tecnod,tecfil,tecend
integer visdouble,disdouble
character*1 nulchar
nulchar = char(0)
debug = 0
visdouble = 0
disdouble = 1
imax = ni
jmax = nj
kmax = 1
!
! Open the file and write the tecplot datafile header information.
!
ier = tecini('flowfield'//nulchar,
& 'x,y,u,v'//nulchar,
& 'output.plt'//nulchar,
& '.'//nulchar,
& debug,visdouble)
!
! Write the zone header information.
!
ier = teczne('Flowfield'//nulchar,
& imax,jmax,kmax,
& 'BLOCK'//nulchar,nulchar)
!
! Write out the field data.
!
itot = imax*jmax*kmax
ier = tecdat(itot,x,disdouble)
ier = tecdat(itot,y,disdouble)
ier = tecdat(itot,u,disdouble)
ier = tecdat(itot,v,disdouble)
!
! close file
!
ier = tecend()
return
end
 
Now that you know
It is my understanding that the tecio library works like a "state machine."
People who are familiar with OpenGL (a library used widely for interactive
computer graphics) are very familiar with the idea. From the examples that
come with tecio.a from AMTEC, it seems like one can open multiple .plt
files and write different data to the different files in no particular order
by putting the library in the appropriate state. Although there must be some
benefit for having that functionality, the programmer must be cautioned
that if multiple files are to remain open simultanuously, (s)he must keep
track of the state... In short,... study the examples!
 
Using tecio.a for writing unstructured data files
Yes! Of course you can do that! It is a lot like writing structured data,
only some of the arguments that are passed to different calls have different
meanings.
Below is a subroutine that creates TecPlot .plt files with unstructured
data. It is a lot like the structured .plt file creation procedure, only
the data is arranged in a way that nothing is really "implied," as is
the case with structured data. For structured data, if I were to specify that
a particular zone will have "imax" "jmax" and "kmax" points in the
i,j, and k directions respectivelly, I could stream the data to a file
-- or any stream for that matter. Knowing just the maxima of each
of the i,j,k and also the convention of how those indexes "unroll" when
the data is dumped into the stream, I could easily recover the data from
the stream (or file) by reading them in the same order. By "unroll" I
mean the nesting order of the i,j, and k loops. Tecio.a does this in
each own internal way when imax, jmax and kmax are specified by simply
accepting a pointer to the first data point.
Unstructured data are specified on a "per-point" basis, exactly like
structured data, but the connectivity of each data point is not implied,
and therefore, unless connectivity information is also provided, the
data simply exist as a "cloud of points." To connect the points in this
set, and therefore represent it as a mesh, we need to provide the
"per-element connectivity." But this would be really ambiguous if we
had not specified first what type of elements make up the mesh; for
example, the "simplex" for each of the zero, one, two and three dimensions
are a point, a line segment, a triangle and a tetrahedron respectivelly.
Note that if
the data is represented with a structured mesh, the number of dimensions
and number of points in each of the directions automatically determine
what the element type is.
Therefore, tecio.a expects all of this information and imposes its own
restrictions on what kind of data it can accept. To my knowledge, it can
accept line-segements, triangles, tetrahedra and "bricks" which is what
I chose for this example. So, first you need to open the file and
specify the number of variables that will be outputed, the element type,
the number of nodes and the number of elements. Here is what a subroutine
would look like:
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
c *** demo subroutine to write a TecPlot .plt file with the real*8 data.
c *** Written by Ioannis Nompelis 16.12.2005
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
subroutine field_output(ne,nno,nel,ien,xyz,q)
Implicit none
Integer itp,ierr
Integer ne
Integer nno,nel
Integer, dimension(nel,0:8) :: ien
Real*8, dimension(nno,3) :: xyz
Real*8, dimension(nno,ne) :: q
Integer n
Integer tecini,tecdat,teczne,tecnod,tecfil,tecend
Integer,allocatable,dimension(:,:) :: icon
Integer debug,visdouble,disdouble
Character*1 NULCHAR
c
c *** setup futility contants
c
debug = 0
visdouble = 0
disdouble = 1
NULCHAR = char(0)
c
c *** create the .plt file
c
ierr = TECINI('flowfield'//NULCHAR,
& 'x y z q1 q2'//NULCHAR,
& 'output.plt'//NULCHAR,
& '.'//NULCHAR,
& debug,visdouble)
c
c *** make sure your are working on the first open file
c
ierr = TECFIL(1)
c
c *** Set up the zone and declare it as finite element data
c
ierr = TECZNE('Flowfield'//NULCHAR,
& nno,
& nel,
& 3, ! Brick
& 'FEBLOCK'//NULCHAR,
& NULCHAR)
c
c *** dump node data sequentially, grid first, then field variables
c
ierr = TECDAT(nno,xyz(1,1),1)
ierr = TECDAT(nno,xyz(1,2),1)
ierr = TECDAT(nno,xyz(1,3),1)
do n = 1,ne
ierr = TECDAT(nno,q(1,n),1)
enddo
c
c *** allocate memory for and assemble connectivity data
c
allocate(icon(8,nel))
do n = 1,nel
Select Case(ien(n,0))
Case(4)
icon(1:3,n) = ien(n,1:3)
icon(4,n) = ien(n,3)
icon(5,n) = ien(n,4)
icon(6,n) = ien(n,4)
icon(7,n) = ien(n,4)
icon(8,n) = ien(n,4)
Case(5)
icon(1:4,n) = ien(n,1:4)
icon(5,n) = ien(n,5)
icon(6,n) = ien(n,5)
icon(7,n) = ien(n,5)
icon(8,n) = ien(n,5)
Case(6)
icon(1:3,n) = ien(n,1:3)
icon(4,n) = ien(n,3)
icon(5:7,n) = ien(n,4:6)
icon(8,n) = ien(n,6)
Case(8)
icon(1:8,n) = ien(n,1:8)
End Select
enddo
c
c *** dump node connectivity data and deallocate memory
c
ierr = TECNOD(icon)
deallocate(icon)
c
c *** close the working file(s)
c
ierr = TECEND()
return
end
Now let's disect it piece by piece. Notice that in this example I
have hardwired the subroutine to use 2 variables only! But I have
allowed for a "variable number of variables" to be used. We will
see how this can be changed.
Here we go:
c
c *** create the .plt file
c
ierr = TECINI('flowfield'//NULCHAR,
& 'x y z q1 q2'//NULCHAR,
& 'output.plt'//NULCHAR,
& '.'//NULCHAR,
& debug,visdouble)
In this part I initialize the file by giving a filename, default path,
a name for the dataset and a string of 5 (five only!) variables. This
is how I hardwired the number of variables -- I have assumed that we
have 3D data (x,y,z) and q1 q2 as field variables.
c
c *** make sure your are working on the first open file
c
ierr = TECFIL(1)
Remember what I said about the "stateness" of the tecio.a library!
c
c *** Set up the zone and declare it as finite element data
c
ierr = TECZNE('Flowfield'//NULCHAR,
& nno,
& nel,
& 3, ! Brick
& 'FEBLOCK'//NULCHAR,
& NULCHAR)
Now tecio.a knows that we will be giving out unstructured data in
bricks. How? Notice the FEBLOCK statement and also notice
the "3" that is in there instead of "kmax." (Yes, I know there is
some redundancy with the FEBLOCK thing, but I can live with it and
it is not my fault.)
c
c *** dump node data sequentially, grid first, then field variables
c
ierr = TECDAT(nno,xyz(1,1),1)
ierr = TECDAT(nno,xyz(1,2),1)
ierr = TECDAT(nno,xyz(1,3),1)
do n = 1,ne
ierr = TECDAT(nno,q(1,n),1)
enddo
It does exactly what it claims; dumps streams of variables corresponding
to nodes 1,2,3,......,nno for each of the x,y,z and q1, q2
variables. Be careful here that there is a loop of the variable "n" that
can be anything that is passed to the subroutine, but tecio.a knows that
we will be giving it a maximum of 5 variables.
Here comes the tricky part. In the following fragment of FORTRAN code
we provide tecio.a with the node connectivity for bricks. WIth this example
I am illustrating how to use mixed element types and output them in the
same zone. You simply create only bricks, but you have duplicate node
numbers in the "per-element" connectivity. Notice that I have an original
connectivity array with an extra slot (the zero slot) which holds information
for the element type. An example would be something like this:
...
4 158 234 235 602 -1 -1 -1 -1
8 162 345 346 347 348 447 123 512
8 174 654 345 657 658 659 732 245
5 66 100 123 432 112 -1 -1 -1
6 266 569 246 245 123 987 -1 -1
...
In this example, the element types in the order they appear are
tetrahedron, brick, brick, pyramid, prism. The numbers that are
"-1" are obviously _invalid_ because you may not have a negative
number correspond to any node -- unless you have some specific
reason to have this in your data structure and in which case you
will have to deal with it. In this case the "-1" indicates that
the numbers in those slots of memory are meaningless.
c
c *** allocate memory for and assemble connectivity data
c
allocate(icon(8,nel))
do n = 1,nel
Select Case(ien(n,0))
Case(4)
icon(1:3,n) = ien(n,1:3)
icon(4,n) = ien(n,3)
icon(5,n) = ien(n,4)
icon(6,n) = ien(n,4)
icon(7,n) = ien(n,4)
icon(8,n) = ien(n,4)
Case(5)
icon(1:4,n) = ien(n,1:4)
icon(5,n) = ien(n,5)
icon(6,n) = ien(n,5)
icon(7,n) = ien(n,5)
icon(8,n) = ien(n,5)
Case(6)
icon(1:3,n) = ien(n,1:3)
icon(4,n) = ien(n,3)
icon(5:7,n) = ien(n,4:6)
icon(8,n) = ien(n,6)
Case(8)
icon(1:8,n) = ien(n,1:8)
End Select
enddo
c
c *** dump node connectivity data and deallocate memory
c
ierr = TECNOD(icon)
deallocate(icon)
In the above fragment of code we allocate a new array which has numbers
assigned to its memory slots based on how we want to represent the data.
We are clever in collapsing nodes on each brick that is sent to tecio.a
such that we do not run into visualization problems. Notice how the
treatment of the prism and the brick are similar; the former has two
pairs collapsed while the latter has four nodes collapsed into one. You
can think of this visually and you will realize that it makes a lot of
sense! Finally, we dump the node connectivity with a single call.
I hope this is helpful and not too confusing. Again, you will have to
do some more sophisticated programming in order to get a variable
number of field variables and the corresponding string for the variable
labels to work together in a general way, but this is beyond the scope
of this tutorial.
Your feedback and possible corrections to this tutorial are always
welcome.
 
|