#!/usr/bin/perl -w #-d
# $Header$
# usage: ugexo2ng <exo2filename> <lgmfilename> <ngfilename>
# 		<exo2filename> - exodus II file with volume meshing and nodeset/sideset 
#					 assoziativity info
#       <lgmfilename> - lgm file for which ng file will be created
#       <ngfilename> - ng file to create
#
#  Author: Stefan Lang, IWR-TS, INF 368, 69120 Heidelberg 
#          Stefan.Lang@iwr.uni-heidelberg.de
#

use tree;

#use vars qw ($HUGE $DIM $P_TREE $T_TREE $L_TREE $EPS_PROJ);
#my ($ltree,@boxes,$count);

# basic data types
$num = '[3-5]';
$int = '\d+';
#$sint = '[+-]?\d+';
$real = '[+-]?\d+(?:\.\d+)?';   # (?:\.\d+)? means: optional .nnnn part
$exp = '[+-]?\d+(?:\.\d+(?:e[+-]?\d+)?)?';
$string = '[a-z"_]\w*';

if ($#ARGV != 3)
{
	print "usage: ugexo2ng <exo2filename> <lgmfilename> <mapfilename> <ngfilename>\n";
 	exit;
}

%cdf = ();
%lgm = ();
%map = ();

$exo2 = "$ARGV[0]";
$lgm = "$ARGV[1]";
$map = "$ARGV[2]";
$ng = "$ARGV[3]";

$EPS_POINT = 10e-6;
$EPS_LOC   = 10e-6;
$DIM = 3;

$HUGE = 9999999999999.9;
$EPS_PROJ = 0.1;

$TYPE_LINE = 4;
$TYPE_SURF = 3;

###############################################################################
# read exodusII file
###############################################################################

sub	scan_nsets
{
	my $nsets = $_[0];

#	print "nsets: $nsets\n";
	$nns = 0;
	$ready = 0;
	LOOP1: while (1)
	{
		if ($nns == $nsets) { last LOOP1; }
		LOOP: while(<EXO2>)
		{
			if ( /^\s($string)/ )
			{
#				print "scan_node_sets: string $1\n";
				$record = $1;
				$i = 0;
				$found = 0;
			}
			for $_ (split)
			{
#				if ( /($exp)/  || /($exp),/ ) { printf "$record %s\n", $1; }
				if ( 
					/($exp)/ || /($exp),/   ||
					/($real)/ || /($real),/ ||
					/($int)/  || /($int),/ || 
 					/($string)/ || /($string),/
				   )
				{
#   					print "scan_node_sets: record $record $i $1\n";
					$cdf{$record}{$i} = $1; 

					$i++;
				}
 				elsif ( /=/ ) { $found++; }
				elsif ( /;/ ) { $nns++; last LOOP; }
				elsif ( /}/ ) { $ready = 1; last LOOP; }
				else { printf "error %s\n",$_;  print "ERROR while reading node_sets\n"; exit;}
			}
		}
		if ($ready == 1) { last LOOP1; }
#		print "scan_node_sets: $nns\n";
	}
}

sub scan_dimension
{
	print "EXO dimensions block\n";

	$token = 0; 
	LOOP: while(<EXO2>)
	{
		if ( /\s*num_dim\s*=\s*($int)\s*;/ ) { $cdf{num_dim} = $1; $token++;}
		if ( /\s*num_nodes\s*=\s*($int)\s*;/ ) { $cdf{num_nodes} = $1; $token++;}
		if ( /\s*num_node_sets\s*=\s*($int)\s*;/ )
		{
			$cdf{num_node_sets} = $1; 
 			scan_nsets($cdf{num_node_sets});
			$token++;
#			print "scan_dimension: $token\n";
		}
		if ( /\s*num_el_blk\s*=\s*($int)\s*;/ )
		{
			$cdf{num_el_blk} = $1; 
 			scan_nsets(2*$cdf{num_el_blk});
			$token++;
#			print "scan_dimension: $token\n";
		}
		if ( /\s*num_side_sets\s*=\s*($int)\s*;/ )
		{
			$cdf{num_side_sets} = $1; 
 			scan_nsets(2*$cdf{num_side_sets});
			$token++;
#			print "scan_dimension: $token\n";
		}
		if ($token == 5) { last LOOP; }
	}
	if ($token != 5) { printf "ERROR reading dimensions block\n"; exit; }

	return(0);
}


sub scan_variables
{
	print "EXO variables block\n";

	LOOP: while(<EXO2>)
	{
		if ( /\s*num_dim\s*=\s*($int)\s*;/ ) { $cdf{num_dim} = $1; }
		else {last LOOP; }
	}

	return(0);
}

sub scan_globalattributes
{
	print "EXO attributes block\n";

	LOOP: while(<EXO2>)
	{
		if ( /\s*num_dim\s*=\s*($int)\s*;/ ) { $cdf{num_dim} = $1; }
		else {last LOOP; }
	}

	return(0);
}

sub scan_coord
{
	$i = 0;
	$dim = 0;
	LOOP: while(<EXO2>)
	{
#		@coordline = split;
#		print "$_\n";
		for $_ (split)
		{
#			print "$cdf{num_nodes} $i\n";
			if ( /($real),/ )
			{
				$cdf{coord}{$i}{$dim} = $1; $i++;
				if ($i == $cdf{num_nodes})
				{
					$i = 0; $dim++;
				}
			}
			elsif ( /($real)/ )
			{
				$cdf{coord}{$i}{$dim} = $1; $i++; $dim++;
			}
			elsif ( /;/ ) 
			{
				if ($i != $cdf{num_nodes})
				{ print "ERROR: scaned coord=$i but num_nodes=$cdf{num_nodes}\n"; exit;}
				if ($dim != $cdf{num_dim})
				{ print "ERROR: scaned dim=$i but num_dim=$cdf{num_dim}\n"; exit;}
				last LOOP;
			} 
			else { print "ERROR while reading coord\n";}
		}
	}
}

sub scan_node_sets
{
	# skip ns_status record
	LOOP2: while(<EXO2>)
	{
		if ( /;/ ) { last LOOP2; }
	}

	# read ns_prop1 record +  n node_ns/dist_face_ns records
 	scan_nsets(2*$cdf{num_node_sets}+1);
}

sub scan_data
{
	print "EXO data block\n";

	$found = 0;
	LOOP: while(<EXO2>)
	{
		if ( /\s*coord\s*=\s*/ ) { scan_coord(); $found++}
		elsif ( /\s*ns_status\s*=\s*/ ) { scan_node_sets(); $found++}
		elsif ($found == 2) {last LOOP; }
	}

	return(0);
}

sub scan_modeldata ()
{
	$nunits = 0;
	$cdf{unitsrev}{0} = 0;

	LOOP: while(<EXO2>)
	{
		if ( /Volume\sEntity\s*\(Id\s=\s($int)\)/ )
		{
#			printf "unit %d material%d\n",$1,$1;
			$cdf{units}{$nunits} = $1;
			$cdf{unitsrev}{$1} = $nunits+1;
			$nunits++;
		}
		if ( /Surface\sEntity\s*\(Id\s=\s($int)\)/ )
		{
			$surface = $1;
			$nl = 0;
			LOOP1: while(<EXO2>)
			{
				$surfacename = sprintf "surface%d",$surface; 
				if ( /In\sVolume\s*($int)\./ )
				{
# 					printf "surface %d left=0 right=%d\n",$surface,$1;
					$cdf{$surfacename}{left} = 0;
					$cdf{$surfacename}{right} = $1;
					last LOOP1;
				}
				elsif ( /In\sVolume\s*($int),\s*Volume\s*($int)\./ )
				{
# 					printf "surface %d left=%d right=%d\n",$surface,$1,$2;
					$cdf{$surfacename}{left} = $1;
					$cdf{$surfacename}{right} = $2;
					last LOOP1;
				}
				elsif ( /\s*Curve\s*($int)\s*($int)/ )
				{
					$cdf{$surfacename}{lines}{$nl} = $2;
					if ( $1 != $2 ) { printf "ERROR curve name $1 differs from its id $2\n"; }
					$nl++;
				}
				else { printf "ERROR: left/right or curve information missing for surface %d\n",$surface; exit;}
			}
			$cdf{$surfacename}{lines}{nlines} = $nl;
		}
	}

	$cdf{nunits} = $nunits;

}

# read ng mesh in exodus II format
sub read_exo2_ng()
{
	print "processing $exo2\n";
	open(EXO2, "<$exo2") || die "can't open $exo2\n";

	BEGIN: while(<EXO2>)
	{
		last BEGIN if ( /^netcdf\s\w+\s\{/ ); 
		print "$exo2 has no valid netcdf file format!\n";
		exit;
	}

	$end = 0;
	END: while(<EXO2>)
	{
		if ( /dimensions:/ ) { scan_dimension(); }
		if ( /variables:/ ) { scan_variables(); }
		if ( /global\sattributes:/ ) { scan_globalattributes(); }
#		if ( /data:/) { scan_data(); }
 		if ( /data:/) { print "EXO data block\n"; scan_nsets(-1); }
		if ( /\}/ ) { $end = 1; last END; }
	}

	if ($end == 0) { print "file $exo2 not complete!!\n"} 

	close(EXO2);

	return(0);
}


###############################################################################
# read lgm domain file
###############################################################################

sub scan_lines
{
	$nline = 0;
	LOOP: while (<LGM>)
	{
#		printf "scanlines";
		if (/line\s*($int)/)
		{
 			$line = sprintf("line%d",$1);
#  			printf "%s\n",$line;
			$nline++;
			$points = 0;
		}
		for $_ (split)
		{
#  			printf "$_\n";
			if (/points:/)
			{
# 				printf "points\n";
				$points = 1;
				$point = 0;
			}
			elsif ( /($int);/ || /;/ ) 
			{
# 				printf "last\n";
				if ($points == 1) 
				{
# 					printf "%d\n",$1;
					$lgm{$line}{points}{$point} = $1; 
					$point++;
				}
				$lgm{$line}{npoints} = $point; 
				$points = 0; 
# 				printf " %d\n",$lgm{$line}{npoints};
				next LOOP;
			}
			elsif (/($int)/)
			{ 
				if ($points == 1) 
				{
# 					printf "%d\n",$1;
					$lgm{$line}{points}{$point} = $1; 
					$point++;
				}
			}
		}
		if ( /#\s*Surface/ )
		{
			$lgm{nlines} = $nline;
			printf ("LGM nlines=%d\n",$nline);
			last LOOP;
		}
	}
}

sub scan_surfaces
{
	$nsurface = 0;
	LOOP: while(<LGM>)
	{
		if (/surface\s*($int):/)
		{
 			$surface = sprintf("surface%d",$1);
#			printf "%s\n",$surface;
			$nsurface++;
			$points = 0;
			$lines = 0;
			$triangles = 0;
		}
		for $_ (split)
		{
#			printf "$_\n";
			if (/points:/)
			{
# 				printf "points\n";
				$points = 1;
				$point = 0;
			}
			if (/lines:/)
			{
# 				printf "lines\n";
				$lines = 1;
				$line = 0;
			}
			if (/triangles:/)
			{
# 				printf "triangles\n";
				$triangles = 1;
				$triangle = 0;
			}
			elsif ( /;/ )
			{
				$points = 0;
				$lines = 0;
# 				$triangles = 0;
			}
			if ( /($int);/ ) 
			{
#				printf "last\n";
				if ($points == 1) 
				{
					$lgm{$surface}{points}{$point} = $1; $point++;
					$lgm{$surface}{npoints} = $point; 
					$points = 0; 
# 					printf " %d\n",$lgm{$surface}{npoints};
				}
				if ($lines == 1) 
				{
					$lgm{$surface}{lines}{$line} = $1; $line++;
					$lgm{$surface}{nlines} = $line; 
					$lines = 0; 
#					printf " %d\n",$lgm{$surface}{nlines};
				}
				if ($triangles == 1) 
				{
					$lgm{$surface}{triangles}{$triangle} = $1; $triangle++;
					$lgm{$surface}{ntriangles} = $triangle; 
#  					$triangles = 0; 
#  					printf "ntri=%d tri=%d\n",$lgm{$surface}{ntriangles}, $lgm{$surface}{triangles}{$triangle-1};
				}
			}
			elsif (/($int)/)
			{ 
#				printf "int\n";
				if ($points == 1) 
				{
					$lgm{$surface}{points}{$point} = $1; $point++;
				}
				if ($lines == 1) 
				{
					$lgm{$surface}{line}{$line} = $1; $line++;
				}
				if ($triangles == 1) 
				{
					$lgm{$surface}{triangles}{$triangle} = $1; $triangle++;
				}
			}
		}
		if ( /#\s*Point/ )
		{
			$lgm{nsurface} = $nsurface; 
			printf "LGM nsurface=%d\n",$lgm{nsurface};
			last LOOP;
		}
	}
}

sub scan_points()
{
	$npoints = 0;
	while(<LGM>)
	{
		if ( /($exp)\s+($exp)\s+($exp);/ )
		{
#  			printf " %d %f %f %f\n",$npoints,$1,$2,$3;

			$lgm{point}{$npoints}{0} = $1;
			$lgm{point}{$npoints}{1} = $2;
			$lgm{point}{$npoints}{2} = $3;
			$npoints++;
		}
	}
	$lgm{npoints} = $npoints;
 	printf "LGM npoints=%d\n",$npoints;
}


sub read_lgm
{
	print "processing $lgm\n";
	open(LGM, "<$lgm") || die "can't open $lgm\n";

	$found = 0;

	while(<LGM>)
	{
		if ( /#\s*Line/ )
		{
#			printf "LGM scan_lines\n";
			scan_lines(); $found++;
		}

		if ( /#\s*Surface/ )
		{
#			printf "LGM scan_surfaces\n";
			scan_surfaces(); $found++;
		}

		if ( /#\s*Point/ )
		{
#			printf "LGM scan_points\n"; 
			scan_points(); $found++;
		}
	}
# 	printf "found=%d\n",$found; 
 	if ($found != 3) { print "file $lgm not complete!!\n"; exit; } 

	close(LGM);

	return(0);
}


###############################################################################
# read map file
###############################################################################

sub scan_linemap
{
	$entries = 0;
	LOOP: while(<MAP>)
	{
		if ( /($int)\s+($int)/)
		{
# 			printf "linemap %d %d\n",$1,$2;
			$map{linemap}{$1} = $2;
			$entries++;
		}
		if ( /Surface/ ) { last LOOP; }
	}
	printf ("MAP nlinemap=%d\n",$entries);
}

sub scan_surfacemap
{
	$entries = 0;
	LOOP: while(<MAP>)
	{
		if ( /($int)\s+($int)/)
		{
# 			printf "surfacemap %d %d\n",$1,$2;
			$map{surfacemap}{$1} = $2;
			$entries++;
		}
	}
	printf ("MAP nsurfacemap=%d\n",$entries);
}

sub read_map
{
	print "processing $map\n";
	open(MAP, "<$map") || die "can't open $map\n";

	$found = 0;

	while(<MAP>)
	{
		if ( /Line/ )
		{
#			printf "MAP scan_linemap\n";
			scan_linemap(); $found++;
		}

		if ( /Surface/ )
		{
#			printf "Map scan_surfacemap\n";
			scan_surfacemap(); $found++;
		}

	}
# 	printf "found=%d\n",$found; 
 	if ($found != 2) { print "file $map not complete!!\n"; exit; } 

	close(MAP);

	return(0);
}


###############################################################################
# bulid bound box tree
###############################################################################

sub Mod
{
	my (@a) = @_;
	return(sqrt($a[0]*$a[0]+$a[1]*$a[1]+$a[2]*$a[2]));
}

sub min
{
	my ($a,$b) = @_;
	if ($a < $b) {return $a;}
	else		 {return $b;}
}

sub max
{
	my ($a,$b) = @_;
	if ($a > $b) {return $a;}
	else		 {return $b;}
}

#my (@keys,$k,$np,$j,$i,$count,@boxes,@objects,@points,@d);

sub bbb_tree
{
	# build boxes and box trees for lgm lines
   	for ($j=0; $j<$lgm{nlines}; $j++)
	{
   		$count = 0;
 		$line = sprintf("line%d",$j);
# 		printf("ltree %s\n",$line);
		for ($n=0; $n<$lgm{$line}{npoints}-1; $n++)
		{
			my(@ur,@ll,%obj,@p0,@p1);

			$point0 = $lgm{$line}{points}{$n};
			$point1 = $lgm{$line}{points}{$n+1};
			for($i=0; $i<$DIM; $i++)
			{
				$p0[$i] = $lgm{point}{$point0}{$i};
				$p1[$i] = $lgm{point}{$point1}{$i};
#				printf("point0 %d=%lf\n",$i,$p0[$i]);
# 				printf("point1 %d=%lf\n",$i,$p1[$i]);
				$ll[$i] = $ur[$i] = $p0[$i];
				$ll[$i] = min($ll[$i],$p1[$i]);
				$ur[$i] = max($ur[$i],$p1[$i]);
				$d[$i] = $ur[$i] - $ll[$i];
#				printf("dim=%d ll=%lf ur=%lf d=%lf\n",$i,$ll[$i],$ur[$i],$d[$i]); 
			}
#			printf("p0 %lf %lf %lf\n",$p0[0],$p0[1],$p0[2]);
#			printf("p1 %lf %lf %lf\n",$p1[0],$p1[1],$p1[2]);
			if (Mod(@d) < $EPS_POINT)
			{
				die "Length of a segment is smaller than EPS_POINT $!\n";
			}
#			$obj{p1} = \$lgm{point}{$point0};
#			$obj{p2} = \$lgm{point}{$point1};
			$obj{p1} = \@p0;
			$obj{p2} = \@p1;
			$obj{seg} = $n;
			$obj{alpha} = 2.0;
			$lgm{lobjects}[$j][$count] = \%obj;
			$lgm{lboxes}[$j][$count] = BBT_NewBBox($DIM,\@ll,\@ur,$lgm{lobjects}[$j][$count]);
#			print "newbox $lgm{lboxes}[$j][$count]\n";
			$count++;
		}
  		$ltree = sprintf("ltree%d",$j);
# 		printf("NewTree=%s count=%d\n",$ltree,$count);
 		$lgm{$ltree} = BBT_NewTree(\@{$lgm{lboxes}[$j]},$count,$DIM);
	}

	
	# build boxes and box trees for lgm surface triangles
   	for ($j=0; $j<$lgm{nsurface}; $j++)
	{
   		$count = 0;
 		$surface = sprintf("surface%d",$j);
# 		printf("stree %s tris=%d\n",$surface,$lgm{$surface}{ntriangles});
		for ($n=0; $n<$lgm{$surface}{ntriangles}/3; $n++)
		{
			my (@p0,@p1,@p2,@ur,@ll,%obj);

			$point0 = $lgm{$surface}{triangles}{3*$n};
			$point1 = $lgm{$surface}{triangles}{3*$n+1};
			$point2 = $lgm{$surface}{triangles}{3*$n+2};
#			printf "tri%d p0=%d p1=%d p2=%d\n",$n,$point0,$point1,$point2;
			for($i=0; $i<$DIM; $i++)
			{
				$p0[$i] = $lgm{point}{$point0}{$i};
				$p1[$i] = $lgm{point}{$point1}{$i};
				$p2[$i] = $lgm{point}{$point2}{$i};
#				printf("point0 %d=%lf\n",$i,$p0[$i]);
# 				printf("point1 %d=%lf\n",$i,$p1[$i]);
				$ll[$i] = $ur[$i] = $p0[$i];
				$ll[$i] = min($ll[$i],$p1[$i]);
				$ll[$i] = min($ll[$i],$p2[$i]);
				$ur[$i] = max($ur[$i],$p1[$i]);
				$ur[$i] = max($ur[$i],$p2[$i]);
				$d[$i] = $ur[$i] - $ll[$i];
# 				printf("dim=%d ll=%lf ur=%lf d=%lf\n",$i,$ll[$i],$ur[$i],$d[$i]); 
			}
#			printf("p0 %lf %lf %lf\n",$p0[0],$p0[1],$p0[2]);
#			printf("p1 %lf %lf %lf\n",$p1[0],$p1[1],$p1[2]);
#			printf("p2 %lf %lf %lf\n",$p2[0],$p2[1],$p2[2]);
			if (Mod(@d) < $EPS_POINT)
			{
				die "Length of a triangle is smaller than EPS_POINT $!\n";
			}
#			$obj{p1} = \$lgm{point}{$point0};
#			$obj{p2} = \$lgm{point}{$point1};
			$obj{p0} = \@p0;
			$obj{p1} = \@p1;
			$obj{p2} = \@p2;
			$obj{tri} = $n;
 			$obj{loc0} = 2.0;
 			$obj{loc1} = 2.0;
 			$obj{test} = 2.0;
			$lgm{tobjects}[$j][$count] = \%obj;
			$lgm{tboxes}[$j][$count] = BBT_NewBBox($DIM,\@ll,\@ur,$lgm{tobjects}[$j][$count]);
#			print "newbox $lgm{tboxes}[$j][$count]\n";
			$count++;
		}
  		$ttree = sprintf("ttree%d",$j);
# 		printf("NewTree=%s count=%d\n",$ttree,$count);
 		$lgm{$ttree} = BBT_NewTree(\@{$lgm{tboxes}[$j]},$count,$DIM);
	}
}


###############################################################################
# compute boundary data
###############################################################################

sub Mod2
{
	my (@a)=@_;
	return($a[0]*$a[0]+$a[1]*$a[1]+$a[2]*$a[2]);
}


## maximum of several values ##
sub mmax
{
	my ($max,$i);

	$max=-1000000;
	for ($i=0; $i<@_; $i++){
		if(defined($_[$i])){
			if ($max<$_[$i]){
				$max=$_[$i];
			}
		}
	}
	return ($max);
}

sub DotProduct
{
	my ($a,$b)=@_;
	return($a->[0]*$b->[0]+$a->[1]*$b->[1]+$a->[2]*$b->[2]);
}

sub CrossProduct
{
    my ($a,$b)=@_;
    my @cp;

    $cp[0]=$a->[1]*$b->[2]-$a->[2]*$b->[1];
    $cp[1]=$a->[2]*$b->[0]-$a->[0]*$b->[2];
    $cp[2]=$a->[0]*$b->[1]-$a->[1]*$b->[0];
    return @cp;
}

sub GetLocalCoordinates
{
	my($P0,$P1,$P2,$P,$xi,$eta)=@_;
	my (@a,@b,@c,@p,@q,@sp,$sum,@lambda,$mcp,$i,@cp,@n);

	#print"GetLocalCoordinates\n";
	for($i=0;$i<3;$i++){
		#print "P->[$i]=$P->[$i] P0->[$i]=$P0->[$i] P1->[$i]=$P1->[$i] P2->[$i]=$P2->[$i]\n";
	}

	## Determine vectors ##
	for($i=0;$i<3;$i++){
		$a[$i]=$P1->[$i]-$P0->[$i];
		$b[$i]=$P2->[$i]-$P0->[$i];
		$c[$i]=$P2->[$i]-$P1->[$i];
		$p[$i]=$P->[$i]-$P0->[$i];
		$q[$i]=$P->[$i]-$P1->[$i];
	}

	#print"\@a=@a \@b=@b \@c=@c \@p=@p \@q=@q\n";

	## double area of triangle ##
	@cp=CrossProduct(\@a,\@b);
	$mcp=Mod(@cp);
	for($i=0;$i<3;$i++){
		$n[$i]=$cp[$i]/$mcp;
		#print "n[$i]=$n[$i]\n";
	}

	## Get the spat ##
	$sp[0]=Spat(\@a,\@p,\@n);
	$sp[1]=Spat(\@p,\@b,\@n);
	$sp[2]=Spat(\@c,\@q,\@n);
	#print "\@sp=@sp\n";

	## TODO: not needed
	## check, whether point lies inside triangle ##
	 for($i=0;$i<3;$i++){
	 	if( ($sp[$i]/$mcp) < (0-$EPS_LOC) ){
#			printf "outside triangle\n";
#	 		return 0;
	 	}
	 }
	
	## Get relationship vol of one triangle/vol of whole triangle ##
	$sum=0;
	for($i=0;$i<3;$i++){
		$lambda[$i]=$sp[$i]/$mcp;
		$sum+=$lambda[$i];
#		print"lambda[$i]=$lambda[$i]\n";
	}

	## check sum params must be 1 ##
	if(($sum<= (1-$EPS_LOC)) || ($sum>=1+$EPS_LOC) ){
		printf "Sum of parameters must be 1 ! $!\n";
#		return 0;
 		 die "Sum of parameters must be 1 ! $!\n";
	}
#	printf "sum=$sum\n";
		
	## last check ##	
	for($i=0;$i<3;$i++){
		if( ($lambda[$i] > (1.0+$EPS_LOC)) || ($lambda[$i] < (0-$EPS_LOC)) ){
#  			print "lambda[$i]=$lambda[$i] <0 || > 1 $!\n";
 			return 0;	
	   }
	}

	$$xi = $lambda[0];
	$$eta = $lambda[1];

	return 1;
}

sub Spat
{
	my ($a,$b,$c)=@_;
	return((($a->[1]*$b->[2] - $a->[2]*$b->[1])*$c->[0])+
		(($a->[2]*$b->[0] - $a->[0]*$b->[2])*$c->[1])+
		(($a->[0]*$b->[1] - $a->[1]*$b->[0])*$c->[2]))
}

# TODO (s.l.): $obj{loc1} is overwritten after Tri_Callback has been executed 
my $local0=2.0;
my $local1=2.0;

sub Tri_Callback
{
	my($x,$obj)=@_;
	my(@p0,@p1,@p2,@a,@b,@c,@p,$i,$ma,$mb,$mc,@tmp,@cp,$mcp,@n,$d,$md,@dist);
	my($tmp,$xi,$eta);

	$tmp = $obj->{p0}; @p0 = @$tmp;
	$tmp = $obj->{p1}; @p1 = @$tmp;
	$tmp = $obj->{p2}; @p2 = @$tmp;

if (0)
{
	printf ("x %lf %lf %lf\n",$x->[0],$x->[1],$x->[2]);
	printf ("	t0 %lf %lf %lf\n",$p0[0],$p0[1],$p0[2]);
	printf ("	t1 %lf %lf %lf\n",$p1[0],$p1[1],$p1[2]);
	printf ("	t2 %lf %lf %lf\n",$p2[0],$p2[1],$p2[2]);
}

	## point lies not inside extruded triangle area
	if(!(GetLocalCoordinates(\@p0,\@p1,\@p2,$x,\$xi,\$eta))){
#  		printf ("error1 in GetLocalCoordinates()\n");
		return $HUGE;
	}
	
	for($i=0; $i<$DIM; $i++){
		$a[$i]=$x->[$i]-$p0[$i];
		$b[$i]=$x->[$i]-$p1[$i];
		$c[$i]=$x->[$i]-$p2[$i];
	}
	#print"Vor Vertauschung: \@a=@a \@b=@b \@c=@c \n";

	#print"S_TRI{$obj->{n}}=$S_TRI{$obj->{n}}\n";
	#print"Vor Vertauschung: \@p0=@p0 \@p1=@p1 \@p2=@p2\n";

	## norms ##
	$ma=Mod(@a);
	$mb=Mod(@b);
	$mc=Mod(@c);
	#print"ma=$ma mb=$mb mc=$mc\n";

	## Find closest Point of triangle P0,P1,P2 to x -> Pref=P0 ##
	if( ($mc<$ma) && ($mc<$mb) ){
		@tmp = @p0;
		@p0  = @p2;
		@p2  = @tmp;
	}elsif( ($mb<$ma) && ($mb<$mc) ){
		@tmp = @p0;
		@p0  = @p1;	
		@p1  = @tmp;
	}
	#print"Nach Vertauschung: x=$x->[0] $x->[1] $x->[2] \@p0=@p0 \@p1=@p1 \@p2=@p2\n";

	## vectors ##
	@a=@b=@c=();
	for($i=0;$i<$DIM;$i++){
		$a[$i]=$p1[$i]-$p0[$i];
		$b[$i]=$p2[$i]-$p0[$i];
		$c[$i]=$p2[$i]-$p1[$i];
		$p[$i]=$x->[$i]-$p0[$i];
	}
	#print"Nach Vertauschung: \@a=@a \@b=@b \@c=@c \@p=@p\n";

	## normal vector ##
	@cp=CrossProduct(\@a,\@b);
	$mcp=Mod(@cp);
	for($i=0;$i<$DIM;$i++){
		$n[$i]=$cp[$i]/$mcp;
		#print "n[$i]=$n[$i] ";
	}
	#print"\n";
	
	## distance from P normal to plane axb ##
	#print"\@p=@p \@n=@n\n";
	$d=DotProduct(\@p,\@n);
	#printf("d=%.20f\n",$d);

	## point is too far from plane axb ##
	$md=sqrt($d*$d);
	if($md>$EPS_PROJ){
		printf ("error: md=%lf greater EPS_PROJ=%lf\n",$md,$EPS_PROJ);
		return $HUGE;
	}
	
	## Projected point = reference point P0 + dist ##
	#print"$obj->{tri}\n";
	for($i=0;$i<$DIM;$i++){
		$dist[$i]=(-$d)*$n[$i];	
		$obj->{p_proj}->[$i] = $x->[$i] + $dist[$i];
# 		printf "dist[$i]=%.16e\n",$dist[$i] ;
#		printf"Triangle_Callback: $obj->{p_proj}->[$i]\n";
	}

	# compute local coordinates
#	$local0 = $xi;
#	$local1 = $eta;
 	$obj->{loc0} = $xi;
 	$obj->{loc1} = $eta;

if (0)
{
	printf ("	l %lf %lf\n",$obj->{loc0},$obj->{loc1});
	printf ("	d %lf\n",$md);
}

#	if ($md > 0.0) {die "md invalid $md\n";}

#	$obj->{test} = $loc1;
#	printf ("Tri_Callback loc0=%lf loc1=%lf\n",$loc0,$loc1);
#	printf ("Tri_Callback loc0=%lf loc1=%lf\n",$obj->{loc0},$obj->{loc1});
	
	#print"\n";
	#print"md=$md\n";
	#print"Mod(dist)=",Mod(@dist),"\n";
#	return(Mod(@dist));
 	return($md);
}


sub Line_Callback
{
	my($x, $obj) = @_;
	my(@a,@pv,$i,$dp,$ma2,@pa,@d,$md);
	my(@alpha,$alpha,$diff,$found,$tmp);

	## vector of p1-p2 and p1-p ##
	for($i=0; $i<$DIM; $i++){
		#print"obj->{p1}->[$i]=$obj->{p1}->[$i]\n";
		#print"obj->{p2}->[$i]=$obj->{p2}->[$i]\n";
#		printf "p1=%lf p2=%lf\n",$obj->{p1}->[$i],$obj->{p2}->[$i]; 
 		$a[$i]= $obj->{p2}->[$i]-$obj->{p1}->[$i];
		$pv[$i]=$x->[$i]-$obj->{p1}->[$i];
	}
#	printf "p1=%lf %lf %lf\n",$obj->{p1}->[0],$obj->{p1}->[1],$obj->{p1}->[2]; 
#	printf "p2=%lf %lf %lf\n",$obj->{p2}->[0],$obj->{p2}->[1],$obj->{p2}->[2]; 
	#print"a=@a pv=@pv\n";

	if(Mod(@a) < $EPS_POINT){
		die"EPS_POINT is too large. Length of segment between p1-p2 is smaller than EPS_POINT.\n";
	}

	$dp = DotProduct(\@a,\@pv);
	$ma2 = Mod2(@a);
	
	## pa means p projected on a ##
	$found=0;
	for($i=0; $i<3; $i++){

		## projected point ##
		$pa[$i]=$dp/$ma2*$a[$i];

#		printf "a*a/ma2=%lf\n",($a[$i]*$a[$i]/$ma2);
		if(($a[$i]*$a[$i]/$ma2) > $EPS_LOC*$EPS_LOC){
			$alpha[$i] = $pa[$i]/$a[$i]	;
			$found=1;
		}

		## check parameter ##
		$tmp=$alpha[$i];
		if(defined($tmp)){
			if( ($alpha[$i]<-$EPS_LOC) || ($alpha[$i]>1.0+$EPS_LOC) ){
#				printf"Line_Callback: alpha=%e return Huge\n",$alpha[$i];
				return $HUGE;}
		}
	}
	if(!$found){die}
	
	$alpha=mmax(@alpha);

#	print"alpha=$alpha\n";
	$obj->{alpha} = $alpha;

	for($i=0; $i<3; $i++){
		
		## new projected point lying on line exactly ##
		$obj->{p_proj}->[$i] = $obj->{p1}->[$i] + $alpha*$a[$i]; 
		#printf("obj->{p1}->[%d]=%.16e\n",$i,$obj->{p1}->[$i]);
		#printf("alpha*a[%d]=%.16e\n",$i,$alpha*$a[$i]);
		#printf("obj->{p_proj->[%d]=%.16e\n",$i,$obj->{p1}->[$i]);

		## distance between point p and projected point ##
		$d[$i] = $pv[$i] - ($obj->{p_proj}->[$i]-$obj->{p1}->[$i]);
		#print"d[$i]=$d[$i]\n";
	}
	
	$md=Mod(@d);
	#print"dist_line=$md\n";
	#print"\@alpha=@alpha\n"; 

	return($md);
}


sub modify_node_coord
{
	my($node,%obj) = @_;

	if (!defined($cdf{bndcoord}{$node}{mod_coord}))
	{
#		printf("node=%d lprojection %.16e %.16e %.16e %d %d alpha=%lf\n",
#			$node,$obj{p_proj}->[0],$obj{p_proj}->[1],$obj{p_proj}->[2],
#			$newid,$obj{seg},$obj{alpha});
		for ($i=0; $i<$DIM; $i++)
		{
			$cdf{coord}{$node+$i*$cdf{num_nodes}} = $obj{p_proj}->[$i];
		}

#		printf ("new coords node %d\n",$node);
		$cdf{bndcoord}{$node}{mod_coord} = 0;
	}
	else
	{
        for ($i=0; $i<$DIM; $i++)
        {
            if ($cdf{coord}{$node+$i*$cdf{num_nodes}} - $obj{p_proj}->[$i] > $EPS_PROJ)
			{
				die ("modify_node_coord(): error node %d coord%d=%s proj_coord=%s\n",
					$node,$i,$cdf{coord}{$node+$i*$cdf{num_nodes}},$obj{p_proj}->[$i]);
			}
        }
	}

	return;
}


sub proj_node_onto_surf
{
	my($node,$newid) = @_;
	my ($robj,%obj);


# 					printf("projecting node=%d onto lgm-surf %d\n",$node,$newid);
	for ($i=0; $i<$DIM; $i++)
	{
		$p[$i] = $cdf{coord}{$node+$i*$cdf{num_nodes}};
	}
# 					printf("node=%d coord %lf %lf %lf\n",$node,$p[0],$p[1],$p[2]);
	$ttree = sprintf("ttree%d",$newid);
#  					printf("ttree=%s ttree=%s count=%d huge=$HUGE\n",$lgm{$ttree},$ttree,$count,$HUGE);

	$local0 = $local1 = 2.0;
	$d = BBT_TreePointDistance($lgm{$ttree},\@p,\$robj,\&Tri_Callback,$HUGE);

#  					printf ("node=%d surfdist=%lf\n",$node,$d);
	if ($d < $HUGE)
	{
		%obj = %$robj;
#						printf("node=%d sprojection %.16e %.16e %.16e %d %d loc0=%e loc1=%e test=%e\n",
#							$node,$obj{p_proj}->[0],$obj{p_proj}->[1],$obj{p_proj}->[2],
#							$newid,$obj{tri},$obj{loc0}),$obj{loc1},$obj{test};
#						printf ("local0=%e local1=%e\n",$local0,$local1);
		# modify node coordinates
		if ($d > 0.0)
		{
			modify_node_coord($node,%obj);
		}
	}
	else
	{
# 						printf "Surf projection failed node=$node surf=$newid\n";
		die "Surf projection failed node=$node surf=$newid\n";
	}
#					printf("\n");

	$cdf{bndcoord}{$node}{tri}
		{$cdf{bndcoord}{$node}{nsurf}} = $obj{tri};

	$cdf{bndcoord}{$node}{loc0}
		{$cdf{bndcoord}{$node}{nsurf}} = $obj{loc0};
	$cdf{bndcoord}{$node}{loc1}
		{$cdf{bndcoord}{$node}{nsurf}} = $obj{loc1};

	$cdf{bndcoord}{$node}{sdist}
		{$cdf{bndcoord}{$node}{nsurf}} = $d;

	$cdf{bndcoord}{$node}{nsurf}++;
}


sub proj_node_onto_line
{
	my($node,$newid) = @_;
	my ($robj,%obj);

#	printf("projecting node=%d onto lgm-line %d\n",$node,$newid);
	for ($i=0; $i<$DIM; $i++)
	{
		$p[$i] = $cdf{coord}{$node+$i*$cdf{num_nodes}};
	}
#	printf("node=%d coord %lf %lf %lf\n",$node,$p[0],$p[1],$p[2]);
	$ltree = sprintf("ltree%d",$newid);
# 	printf("ltree=%s ltree=%s count=%d huge=$HUGE\n",$lgm{$ltree},$ltree,$count,$HUGE);

	$d = BBT_TreePointDistance($lgm{$ltree},\@p,\$robj,\&Line_Callback,$HUGE);

# 	printf ("node=%d linedist=%lf\n",$node,$d);
	if ($d < $HUGE)
	{
		%obj = %$robj;
#		printf("node=%d lprojection %.16e %.16e %.16e %d %d alpha=%lf\n",
#			$node,$obj{p_proj}->[0],$obj{p_proj}->[1],$obj{p_proj}->[2],
#			$newid,$obj{seg},$obj{alpha});

		# modify node coordinates
		if ($d > 0.0)
		{
			modify_node_coord($node,%obj);
		}
	}
	else
	{
# 		printf "Line projection failed node=$node line=$newid\n";
		die "Line projection failed node=$node line=$newid\n";
	}
#					printf("\n");
	$cdf{bndcoord}{$node}{seg}
		{$cdf{bndcoord}{$node}{nline}} = $obj{seg};
	$cdf{bndcoord}{$node}{alpha}
		{$cdf{bndcoord}{$node}{nline}} = $obj{alpha};
	$cdf{bndcoord}{$node}{ldist}
		{$cdf{bndcoord}{$node}{nline}} = $d;
	
	$cdf{bndcoord}{$node}{nline}++;
}


sub	bnd_node
{
	my($do_type) = @_;

	LOOP: foreach $nns (1 .. $cdf{num_node_sets})
	{
#		print "$cdf{ns_prop1}{$nns}\n";
		if ( $cdf{ns_prop1}{$nns} =~/\A($num)($int)/ )
		{
			$type = $1;
			if ($type != $do_type)
			{
				next LOOP;
			}
			$id = sprintf "%d",$2;
#			printf "%d\n", $cdf{ns_prop1}{$nns};
			$ns = sprintf "node_ns%d",$nns; 
			$numns = sprintf "num_nod_ns%d",$nns; 
# 			print "$ns $numns $cdf{$numns}{1}\n";

# 			print " bndcoord:";
			$numnp = sprintf "node_ns%d",$nns; 
LOOP:		foreach $np (1 .. $cdf{$numns}{1})
			{
				$node = $cdf{$numnp}{$np}; 
				$cdf{bndcoord}{$node}{bnd} = 1;

				# node on surface
				if ($type == $TYPE_SURF)
				{
					if (!defined($map{surfacemap}{$id}))
					{
						die "no newid for surface id $id\n";
					}
					$newid = $map{surfacemap}{$id}; 

					if (!defined($cdf{bndcoord}{$node}{nsurf}))
					{
						$cdf{bndcoord}{$node}{nsurf} = 0;
					}
					$cdf{bndcoord}{$node}{surf}
						{$cdf{bndcoord}{$node}{nsurf}} = $newid;

					proj_node_onto_surf($node,$newid);
				}

				# node on line
				if ($type == $TYPE_LINE)
				{
					$newid = $map{linemap}{$id}; 

					if (!defined($cdf{bndcoord}{$node}{nline}))
					{
						$cdf{bndcoord}{$node}{nline} = 0;
					}
					$cdf{bndcoord}{$node}{line}
						{$cdf{bndcoord}{$node}{nline}} = $newid;

					proj_node_onto_line($node,$newid);
				}
			}
		}
	}	
}


sub bnd_elem
{
	foreach $nns (1 .. $cdf{num_side_sets})
	{
#		print "$cdf{ss_prop1}{$nns}\n";
		if ( $cdf{ss_prop1}{$nns} =~/\A3($int)/ )
		{
#			printf "%d\n", $cdf{ss_prop1}{$nns};
			$ess = sprintf "elem_ss%d",$nns; 
			$sss = sprintf "side_ss%d",$nns; 
			$numss = sprintf "num_side_ss%d",$nns; 
# 			print "$ess $sss $cdf{$numss}{1}\n";

			foreach $ns (1 .. $cdf{$numss}{1})
			{
				$elem = $cdf{$ess}{$ns};
				if (!defined($cdf{bnd_elem_nside}{$elem}))
				{
					$cdf{bnd_elem_nside}{$elem} = 0;
				}
				$nside = $cdf{bnd_elem_nside}{$elem};
				$cdf{bnd_elem_side}{$elem}{$nside} = $cdf{$sss}{$ns};
				$cdf{bnd_elem_nside}{$elem}++;
			}
		}
	}	
}

sub comp_bnddata
{
	# project points associated with bnd lines
	bnd_node($TYPE_LINE);

	# project points associated with bnd surfaces
	bnd_node($TYPE_SURF);

	# select element sides associated with bnd sides
	bnd_elem();
}


###############################################################################
# write ng file
###############################################################################

sub write_nodes
{
	print NG "# Points\n";
#	printf "%d\n",$cdf{num_nodes};
#	printf "%s %s\n", $cdf{coord}{0},$cdf{coord}{1};
#	printf "%s\n", $cdf{coord};

#   write boundary nodes
	$nid = 0;
	foreach $nc (1 .. $cdf{num_nodes})
	{
#    		printf "%lf, %lf, %lf\n",$cdf{coord}{$nc-1},$cdf{coord}{2*$nc-1},$cdf{coord}{3*$nc-1};
			if (defined($cdf{bndcoord}{$nc}{bnd}))
			{
				printf NG "# %d %d\n",$nid,$nc;
    			printf NG "B %s %s %s\n",
					$cdf{coord}{$nc},$cdf{coord}{$nc+$cdf{num_nodes}},$cdf{coord}{$nc+2*$cdf{num_nodes}}; 

				if (defined($cdf{bndcoord}{$nc}{nline}))
				{
					foreach $nl (0 .. $cdf{bndcoord}{$nc}{nline}-1)
					{
#						printf NG " L %d %.16lf # d=%.16lf",
 						printf NG " L %d %s # d=%s",
							$cdf{bndcoord}{$nc}{line}{$nl},$cdf{bndcoord}{$nc}{seg}{$nl}+
							$cdf{bndcoord}{$nc}{alpha}{$nl},$cdf{bndcoord}{$nc}{ldist}{$nl};
    					printf NG "\n";
					}
				}
				if (defined($cdf{bndcoord}{$nc}{nsurf}))
				{
					foreach $ns (0 .. $cdf{bndcoord}{$nc}{nsurf}-1)
					{
#  						printf NG " S %d %d %.16lf %.16lf # d=%.16lf",
# 						printf NG " S %d %d 0 0 # d=%e",
  						printf NG " S %d %d %lf %lf # d=%lf",
							$cdf{bndcoord}{$nc}{surf}{$ns},$cdf{bndcoord}{$nc}{tri}{$ns},
							$cdf{bndcoord}{$nc}{loc1}{$ns},$cdf{bndcoord}{$nc}{loc0}{$ns},
							$cdf{bndcoord}{$nc}{sdist}{$ns};
    					printf NG "\n";
					}
				}
    			printf NG ";\n";
				$cdf{nodemap}{$nc} = $nid;
				$nid++;
			}
	}	
#   write inner nodes
	foreach $nc (1 .. $cdf{num_nodes})
	{
#    		printf "%lf, %lf, %lf\n",$cdf{coord}{$nc-1},$cdf{coord}{2*$nc-1},$cdf{coord}{3*$nc-1};
			if (!defined($cdf{bndcoord}{$nc}{bnd}))
			{
				printf NG "# %d %d\n",$nid,$nc;
    			printf NG "I %s %s %s;\n",
					$cdf{coord}{$nc},$cdf{coord}{$nc+$cdf{num_nodes}},$cdf{coord}{$nc+2*$cdf{num_nodes}}; 
				$cdf{nodemap}{$nc} = $nid;
				$nid++;
			}
	}	
	print NG "\n";

	if ($nid != $cdf{num_nodes})
	{
		printf "nid=%d and num_nodes=%d\n",$nid,$cdf{num_nodes}-1;
		die;
	}
}

sub element_offsets()
{
	# print the connect blocks with volume elements
	$offset = 0;
	LOOP: foreach $nconnect (1 .. $cdf{num_el_blk})
	{
		if ( $cdf{eb_prop1}{$nconnect} =~/\A2($int)/ )
		{
			print "# elementoffset of volume $nconnect is $offset\n";

			# number of elements in block $ncon
			$numne = sprintf "num_el_in_blk%d",$nconnect; 

			$cdf{elem_offset}{$nconnect} = $offset;
			$offset += $cdf{$numne}{1};
		}
	}

	return;
}

sub nmap
{
	my $nc = $_[0];

	$nid = $cdf{nodemap}{$nc};

	return ($nid);
}

sub write_bnd_side
{
	my $connect = $_[0];
	my $ne = $_[1];
	my $nn = $_[2];
	my $neid = $_[3];
	my $ns = $_[4];

#	printf NG " F %d", $cdf{bnd_elem_side}{$neid}{$ns};
	printf NG " F";
	$side = $cdf{bnd_elem_side}{$neid}{$ns};

	# tetrahedral case
	if ($nn == 4)
	{
		if ($side == 1)
		{
				$n0 = nmap($cdf{$connect}{4*$ne-3});
				$n1 = nmap($cdf{$connect}{4*$ne-2});
				$n2 = nmap($cdf{$connect}{4*$ne-0});
		}
		elsif ($side == 2)
		{
				$n0 = nmap($cdf{$connect}{4*$ne-2});
				$n1 = nmap($cdf{$connect}{4*$ne-1});
				$n2 = nmap($cdf{$connect}{4*$ne-0});
		}
		elsif ($side == 3)
		{
				$n0 = nmap($cdf{$connect}{4*$ne-3});
				$n1 = nmap($cdf{$connect}{4*$ne-1});
				$n2 = nmap($cdf{$connect}{4*$ne-0});
		}
		elsif ($side == 4)
		{
				$n0 = nmap($cdf{$connect}{4*$ne-3});
				$n1 = nmap($cdf{$connect}{4*$ne-1});
				$n2 = nmap($cdf{$connect}{4*$ne-2});
		}
		else
		{
			printf "ERROR no valid side of tet\n";
			exit;
		}
		printf NG " %d %d %d", $n0, $n1, $n2;
	}
	# hexahedral case
	elsif ($nn == 8)
	{
		if ($side == 1)
		{
				$n0 = nmap($cdf{$connect}{get_con_id($nn,$ne,1)});
				$n1 = nmap($cdf{$connect}{get_con_id($nn,$ne,2)});
				$n2 = nmap($cdf{$connect}{get_con_id($nn,$ne,6)});
				$n3 = nmap($cdf{$connect}{get_con_id($nn,$ne,5)});
		}
		elsif ($side == 2)
		{
				$n0 = nmap($cdf{$connect}{get_con_id($nn,$ne,2)});
				$n1 = nmap($cdf{$connect}{get_con_id($nn,$ne,3)});
				$n2 = nmap($cdf{$connect}{get_con_id($nn,$ne,7)});
				$n3 = nmap($cdf{$connect}{get_con_id($nn,$ne,6)});
		}
		elsif ($side == 3)
		{
				$n0 = nmap($cdf{$connect}{get_con_id($nn,$ne,3)});
				$n1 = nmap($cdf{$connect}{get_con_id($nn,$ne,4)});
				$n2 = nmap($cdf{$connect}{get_con_id($nn,$ne,8)});
				$n3 = nmap($cdf{$connect}{get_con_id($nn,$ne,7)});
		}
		elsif ($side == 4)
		{
				$n0 = nmap($cdf{$connect}{get_con_id($nn,$ne,4)});
				$n1 = nmap($cdf{$connect}{get_con_id($nn,$ne,1)});
				$n2 = nmap($cdf{$connect}{get_con_id($nn,$ne,5)});
				$n3 = nmap($cdf{$connect}{get_con_id($nn,$ne,8)});
		}
		elsif ($side == 5)
		{
				$n0 = nmap($cdf{$connect}{get_con_id($nn,$ne,1)});
				$n1 = nmap($cdf{$connect}{get_con_id($nn,$ne,4)});
				$n2 = nmap($cdf{$connect}{get_con_id($nn,$ne,3)});
				$n3 = nmap($cdf{$connect}{get_con_id($nn,$ne,2)});
		}
		elsif ($side == 6)
		{
				$n0 = nmap($cdf{$connect}{get_con_id($nn,$ne,5)});
				$n1 = nmap($cdf{$connect}{get_con_id($nn,$ne,6)});
				$n2 = nmap($cdf{$connect}{get_con_id($nn,$ne,7)});
				$n3 = nmap($cdf{$connect}{get_con_id($nn,$ne,8)});
		}
		else
		{
			printf "ERROR no valid side of tet\n";
			exit;
		}
		printf NG " %d %d %d %d", $n0, $n1, $n2, $n3;
	}
	else
	{
		printf "ERROR no valid element node number=%d e=%d\n",$nn,$ne;
		exit;
	}
}


sub get_con_id
{
	my ($nodpe,$ne,$nod) = @_;
	my $con_id = -1;
	
	$con_id = $nodpe*$ne-($nodpe-$nod);

	return($con_id);
}


sub write_elements
{
	print NG "# Elements\n";

	element_offsets();

	# print the connect blocks with volume elements
	$ncon = 0;
	$volume = 1;
	$neid = 1;
	LOOP: foreach $nconnect (1 .. $cdf{num_el_blk})
	{
		if ( $cdf{eb_prop1}{$nconnect} =~/\A2($int)/ )
		{
			print NG "# elements of volume $volume ($1)\n";

			# number of elements in block $ncon
			$numne = sprintf "num_el_in_blk%d",$nconnect; 
			# number of nodes per element in block $ncon
			$npe = sprintf "num_nod_per_el%d",$nconnect; 
			$nodpe = $cdf{$npe}{1};
			# hash of elements
			$connect = sprintf "connect%d",$nconnect; 
			printf("nconnect=$nconnect numne=$cdf{$numne}{1} connect=$connect\n");
			foreach $ne (1 .. $cdf{$numne}{1})
			{
				# compute global element number
				$neid = $ne + $cdf{elem_offset}{$nconnect};

				# print element node ids
				# TODO: implement other element types
				printf NG "# %d\n",$neid-1;
				printf NG "E %d",$volume;
				foreach $nod (1 .. $nodpe)
				{
					$con_id = get_con_id($nodpe,$ne,$nod);
					printf NG " %d", nmap($cdf{$connect}{$con_id});
				}
#					printf NG " %d", nmap($cdf{$connect}{4*$ne-3});
#					printf NG " %d", nmap($cdf{$connect}{4*$ne-2});
#					printf NG " %d", nmap($cdf{$connect}{4*$ne-1});
#					printf NG " %d", nmap($cdf{$connect}{4*$ne-0});

				# print boundary side
#				$nn = 4;
				if (defined($cdf{bnd_elem_nside}{$neid}))
				{
					foreach $ns (0 .. $cdf{bnd_elem_nside}{$neid}-1)
					{
  						write_bnd_side($connect,$ne,$nodpe,$neid,$ns)
					}
				}
				print NG ";\n";
			}
			print NG "\n";
			$volume++;
		}
		$ncon++;
	}
}

# write ng

sub write_ng()
{
	my $s = localtime;

	print "writing ng geometry file $ng\n";
	open(NG, ">$ng") || die "can't open $ng\n";
	printf NG "# created at %s\n",$s;

  	write_nodes();
  	write_elements();

	close(NG);
}

sub main()
{
	read_exo2_ng();
	read_lgm();
	read_map();
	bbb_tree();
 	comp_bnddata();
   	write_ng();
}

main();

exit;

