3DMA Developer's Primer

Contents


Recommended reading                       Back to top
  1. New to Unix/Linux? Any book with a good review on Amazon will probably serve you well. However, I recommend using the Internet to search for help, as well as the books:

  2. ANSI standard C/C++

  3. Real stuff: +)


Searching for papers                       Back to top

Any of the papers you need to read are likely to be available online these days. Even for old publications, a lot of effort has been made to scan them and make available online.

  1. Stony Brook Library - STARS Library catalog will tell you if book/paper is on the shelves; E-journals catalog will tell you if you can access it online
  2. Interlibrary Loan - you can ask for a book/paper SBU doesn't have to be loaned to you from another library; ILL is indeed very efficient.
  3. Web of Science - the most comprehensive reference search; e.g. you can get all the papers published by a specified author
  4. Citeseer - very good source of computer science oriented papers
  5. Search for the author on the web - the papers might be posted on personal webpages.



Coding conventions                       Back to top

The C standard is VERY stable, and hence the older Kernighan-Ritchie standard as well as the ANSI standard compile everywhere, the C++ standard is continually evolving, so C++ codes are compiler sensitive. 3DMA contains one major C++ subdirectory of code, the rest is C. Keep in mind that the C++ code has been compiled with a couple of generation of RedHat g++/gcc compilers; all other C++ compilers ... who knows. Most of the time it has been ported to Linux and Sun Unix/Solaris machines; never heard of anybody porting it to Windows but it should work under Cygwin which uses a 'gcc' compiler.

Rarely are codes designed to be self explanatory; this is true with 3DMA as well. When coding: First go to the 3DMA General Users Manual to get definitions straight and try to follow these minimal commenting standards:

  1. Record the research article that describes the algorithmic approach you are following.
  2. Record down an explanation on what the algorithm supposed to do!
It will be helpful both to you and to succeeding 3DMA-coding students




Array packing convention                       Back to top

The three dimensional arrays typically processed in 3DMA provide a value for each point (voxel) in a rectangular volume. Rather than representing them as 3D arrays in C, volumetric data is packed into a one dimensional array. This vastly speeds processing of data. The convention is as follows

Given a 3D volume of

nx rows,
ny columns,   (slice size nxy = nx*ny)
nz slices,        (volume size nxyz = nx*ny*nz),

let (i,j,k) denote a point location in [0..(nx-1)] x [0..(ny-1)] x [0..(nz-1)],
and ind = k*nxy + j*nx + i.
Then the data value at the point (i,j,k) is stored in the location dat[ind], is a one dimensional array 'dat'. Note that (i,j,k) <--> ind is a one-to-one correspondence.

io/cube.h defines a structure, Cube_Info, that stores volume size information, as well as a number of macros such as "Ind_2_ijk" and "Ijk_2_ind" that switch back and forth between (i,j,k) and index 'ind'.




Example 3DMA driver                       Back to top

Below is a printout of (an outdated version of) the file src/io/seal.c. It represents an example of simple driver code in 3DMA and demonstrates coding styles adopted for the 3DMA code.
Comments explaining the file and coding style are in red. I am assuming you have access to the code so you can look up the files referenced in the commentary.

//The copyright declaration is a mandatory header of each file.
/*
*     Copyrighted, Research Foundation of SUNY, 2003
*/

//Comment on file's purpose.
/*
*    Artificially sealing the segmented data: 1-voxel layer of brick volume
*    on all 6 sides is converted to phase 1(grain).
*/

#include <stdio.h>     //This is C header.
#include "dat_types.h" //dat_types.h is in the src/ directory and contains typical
                       //shortcuts used in memory allocation like UCSZ used below

#include "io.h"
#include "filenames.h"
#include "cube.h"

#define MAT0 0
#define MAT1 1

void    seal_driver(void)
{

          //The list of all the functions used in this function
          //is a mandatory part of each function.
    extern     void     get_filenames(char *,char **,char **,int *,int *,char *);
    extern     void     set_vol_size(int *,int *,int *,int *,int *,int *,int *);
    extern     void     set_mat_from_segfl_vol(unsigned char,File_Info *,
                        Cube_Info *,unsigned char *,unsigned char);
    extern     void     output_mat_segfl_vol(unsigned char,File_Info *,
                        unsigned char *, Cube_Info *,unsigned char);

          //the following three functions are part of 3DMA memory checking routines
    extern     int     MEMCHECK(void *,char *,int);
    extern     void    FREE(void *,int);
    extern     void    clean_up(int);

    File_Info     segfl, segfl_new;
    Cube_Info     cube;
    unsigned char *dat;
    char          *msg;
    int           i, j, k, ind, n, cnt;

          //get_filenames is a standard routine that will import names of files
          //needed by the function as well as information on compression etc.
    get_filenames("segmented volume",&segfl.base, &segfl.name,&segfl.len,
                            &segfl.cmprss,"");
    set_vol_size(&cube.zs,&cube.ze,&cube.nx, &cube.ny,&cube.nz,&cube.nxy,
                                &cube.nxyz);
    get_filenames("sealed segmented",&segfl_new.base, &segfl_new.name,
                    &segfl_new.len,&segfl_new.cmprss,"");

          //dynamic memory allocation with typical use of MEMCHECK
    dat = (unsigned char *)calloc(cube.nxyz,UCSZ);
    msg = "dat in seal_driver()";   //this is an error message that will be displayed
                                    //if memory cannot be allocated for some reason
          //MEMCHECK will simply add the allocated number of bytes to a global
          //counter. It is important to have that counter both for debugging and
          //for finding the size of the volume that can be processed in memory
          //consuming cases.
    if( MEMCHECK((char *)dat,msg,cube.nxyz*UCSZ) ) clean_up(0);

          //read in segmented data from a volume file into allocated memory
    set_mat_from_segfl_vol(MAT1,&segfl,&cube,dat,MAT1);

          //count number of phase 0 voxels
    cnt = 0;
    for( i = 0;  i < cube.nxyz;  i++ ) { if( dat[i] == MAT0 ) cnt++; }

    printf("\nBefore sealing");
    printf("\n%d phase 0 voxels, %d phase 1 voxels, %d total voxels",
                     cnt,cube.nxyz - cnt,cube.nxyz);
    printf("\nporosity %f",(float)cnt/cube.nxyz);

           //set n=1 outside layer(s) of voxels  to phase 1
    ind = 0;
    n = 1;        //number of layers
    for( k = 0;  k < cube.nz;  k++ )
    {
        for( j = 0;  j < cube.ny;  j++ )
        {
             for( i = 0;  i < cube.nx;  i++, ind++ )
             {
                if(    (k < n) || (k >= cube.nz - n)
                    || (j < n) || (j >= cube.ny - n)
                    || (i < n) || (i >= cube.nx - n)    ) dat[ind] = 1;
            }
        }
    }

          //count number of phase 0 voxels
    cnt = 0;
    for( i = 0;  i < cube.nxyz;  i++ ) { if( dat[i] == 0 ) cnt++; }

    printf("\nAfter sealing");
    printf("\n%d phase 0 voxels, %d phase 1 voxels, %d total voxels",
                    cnt,cube.nxyz - cnt,cube.nxyz);
    printf("\nporosity %f",(float)cnt/cube.nxyz);

          //outputs new segmented volume data file
    output_mat_segfl_vol(MAT1,&segfl_new,dat,&cube,1);

          //FREE frees the pointer and subtracts memory(in bytes)
          //from the global memory counter

    FREE(dat,cube.nxyz*UCSZ);
}


Debugging with GDB                       Back to top
  1. Uncomment the option DBX = -g in the makefile (src/makefile).
  2. Recompile the desired part of the code.
  3. Prepare a "stripped" input file using stripcomm:
        stripcomm_linux -b < caseM.N.in > gdb.in
  4. Start gdb with the full path to your executable as argument, e.g.
        gdb ~/3dma/bin/3dma_linux
  5. In the gdb environment, run the code by typing
        run < gdb.in (or run < gdb.in > out
    should you wish to redirect output).
  6. The main gdb commands you need to know are
        run, where, break, next, cont, print and quit.
    To learn more about gdb, follow this link


Time profiling - finding out which function is taking the most CPU time                       Back to top

There is a utility that can show you what percentage of time you spend in every function of your code. This is very useful if you need to figure out which parts of the program are taking too much time and need to be redesigned. The basic three steps of time profiling are

  1. Compile and link your program with the '-pg' option. e.g.
         compiling: gcc -g -c myprog.c utils.c -pg
         linking: gcc -o myprog myprog.o utils.o -pg

    Note that in 3DMA this is equivalent to setting the option
        "DBX = -g -pg"
    in src/makefile and then recompiling all the files of the code containing subroutines you wish to profile.

  2. Execute your program, i.e.
        % myprog
    eg. in 3DMA, run the case you want,
        % runcase linux caseM.N
    This will create the file gmon.out in the working directory.

  3. Run gprof,
        % gprof myprog > gprof.ouput
    eg. for the 3DMA code type
        % gprof ~/3dma/bin/3dma_linux > gprof.out
    (where ~/3dma/bin/3dma_linux should be substituted with the full path of 3dma_linux executable you are using) and take a look at gprof.output.
The above three steps are just a short reminder, you should read this excellent summary on the gprof utility to get more details.



The memory debug compilation option in 3DMA                       Back to top

Every time MEMCHECK() is used to increment global memory counter (after dynamic memory allocations with malloc and realloc) or memory is freed using FREE(), you can get a printout on pointer and memory amount allocated (as well as any mismatches). Furthermore, at the end of the output file a list (MEM_LIST) of pointers that were not freed can be printed (File src/mem.c has more details).
To turn the above memort debugging option on, recompile the desired part of the code AND src/mem.c  with the option
    DBX =  -DMEM_DBG
in src/makefile, and then run the code as usual.




Workstations in the Lindquist lab                       Back to top

Workstations: berea, bubbles, fiber, jungle, medax, neuron, mukluk, petra, woof
File server: totem
Current operating system: Redhat Linux 9.0
   
Software Tools: xpdf  -   pdf viewer
  ggv, gs - ps viewer
  xv, gimp - 2d image viewing/manipulation
 
Geomview (all machines),
Inventor (only on medax)
- 3D image viewing/manipulation

All data that resides on the workstations (file systems /nfs/user[1, 2, 3, 4, 5, 6, 7, 8, 9, 17, 18, 19, 20, 21, 22, 23, 24, 25]) is backed up on a weekly basis. See this section to learn how one exports file systems on a unix network.

Current backup schedule:

  backup of time
berea bubbles:/user24 Sat, 2am
bubbles
neuron:/user7,8,9
berea:/user17,18,19
Sat, 10pm
fiber N/A  
medax petra:/user22 Fri, 11pm
mukluk berea:/user21
medax:/user3,4,5,6
Sun, 1am
neuron N/A  
petra medax:/user3,4,5,6 Sun, 11pm
woof fiber:/user1,2
mukluk:/user20,23
Sat, 4am

How to know which hardware components are installed on your system? Here are the basic commands:
    /sbin/lspci  --> list of all video, sound cards, etc.
    uname -a --> name of the system, name of the machine, Linux version and basic hardware type are listed, among other things
    dmesg --> gives everything the kernel saw as it loaded, in order (and in much more detailed than you want to know)

If you're experiencing a problem, Linuxquestions.org most probably has an answer.



Exporting file systems on a Unix network                       Back to top

Machine A has directory /userA that machine B needs to access. It is to be mounted as /nfs/userA on machine B via the network file system. Here is how to arrange exporting of /userA from A and access to it on B. You need to be logged in as superuser (root) to do steps 1-4.

  1. Edit /etc/exports on machine A to contain the line
         /userA/  B(rw,sync)

  2. Execute the command
         % exportfs -r (or /usr/sbin/exportfs -r)

  3. On machine B, edit the file /etc/auto.fs  and add the following line
         userA   -exec,dev,suid,rw  A:/userA

  4. Execute the command
        % /etc/init.d/autofs reload
That's it.



Basic rpm installation instructions                       Back to top

'rpm' is a Linux utility that will install/uninstall precompiled software (from a file in .rpm format). Most of the software on any linux system is installed this way.
    rpm -qa
will list all the software installed on the system via rpm; to search for a specific one you can pipe it to 'grep'. For instance, to check whether the package "xv" is already installed, type
    rpm -qa | grep xv

So, let's try to install 'xv' which is free software for viewing/manipulating images.

  1. Use the internet to find an appropriate rpm that you need. (Note that xv is not bundled with the RedHat 9 distribution CDs.) The following link is an rpm repository that contains most of the existing rpms:
        http://rpmfind.net/linux/RPM/index.html

    Search for "xv" in the above link - you will get plenty of options that are associated with various Linux distributions. Try to find one that you think is closest to your version of Linux (because of the different compilers, some don't work, so you might need to try a number of them). Let's say you picked and downloaded 'xv-3.10a-blah.rpm' to the /user/downloads/  directory.

  2. To install the software you need to have 'root' access to the machine. As root, do the following (in any directory)
        rpm -ivh /user/downloads/xv-3.10a-blah.rpm
    You will be notified about the success of the installation as well as if there is any missing software/library. If there is missing software, The RPMfind package, (available on the rpmfild.net site)is a great resource for locating the missing piece - just go back to the xv-3.10a-blah.rpm page and follow the appropriate link in the "requires" section.

  3. rpm --erase xv-3.10a-blah  will remove an installation.

  4. man rpm  will give you more information about the 'rpm' utility.


Very useful Linux commands                       Back to top
  1. find performs a search of any directory tree (from '/' it can search the entire disk, or at least the directories you have access to) for certain files. The command syntax is
        find path_directory -name filename
    e.g.
        % find . -name "*neuron*"
    searches for all files containing the string "neuron" in their name starting in the current directory.

  2. grep "expression" file(s)   searches the contents of file(s) for the given expression. e.g.
        % grep "neuron" *.c
    searches for the word "neuron" in all files with the .c extension in the current directory)

  3. top lists the current jobs (as well as who's running them) on the machine. Might help you answer why the machine seems very slow and who's responsible :+).

  4. diff  See man diff

  5. Type
        % import > file.ps
    in a terminal window, use mouse movement and click-hold-release on the left mouse button to define a rectangular region on the screen. The contents of the rectangle will be saved as a postscript image in 'file.ps'. The file extension "ps", "tif", "jpg", "pdf", etc. in fact determines the format of the image file. Very useful for grabbing screen content for inserting into a presentation.


    Back to top

    Created by Masa Prodanovic, January 2005