#!/usr/bin/perl -w
################################################################################
#
# setup		bren@ucsd.edu		10/28/2002
#
# setup configures the spectral_model code to build according to
# the end users particular test specifications.
# It generates several files:
#	run
#	output/
#	output/codes_etc/
#	source/sources.mk
#
# When done running setup, the appropriate directory structure for
# the output should exist, and the program "run" should exist and
# be ready. Either run "run", or queue "run" into you're queuing 
# system.
#
# Run "./setup --help" to see the usage statement.
#
# Code written by bren of CFD/IOD/SIO/UCSD fame. It is released to the general public
# under a beer-ware license:
#
# ----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# <bren@ucsd.edu> wrote this file. As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return. 
# ----------------------------------------------------------------------------

use strict;
use Getopt::Long;

my $SIZEOF_REAL = 4;
my $SIZEOF_INTEGER = 4;
my $SIZEOF_COMPLEX = 8;
my $SIZEOF_CHARACTER = 1;

# main()
{	

	my ($np, $ndims);	
	my ($nx, $ny, $nz);
	my ($nfloats, $nsensors, $nfloatparameters);

#	command line switch variables
	my $output_dir;
	my $help;
	my $sizeof_real;
	my $project;
	my $clean;
	my $matlab;
	my $no_report;

#	Parse command line options.
	GetOptions(
			"output_dir=s" => \$output_dir,
			"project=s" => \$project,
			"help" => \$help,
			"clean" => \$clean,
			"sizeof_real=i" => \$sizeof_real,
			"no_report" => \$no_report,
			"matlab" => \$matlab
		  );


#	if help was requested, print out the usage statement and exit.
	if($help){
		usage();
	}

#	clean up after a build
	if($clean){
		clean();
		exit;
	}

#	routine added so that you can change $SIZEOF_REAL on the fly for 64 bit machines.
	if($sizeof_real){
		$SIZEOF_REAL = $sizeof_real;
		$SIZEOF_COMPLEX = 2 * $sizeof_real;
	}

#	identify the appropriate projects directory.
	if(!$project){
		$project = "trivial";
	}

	chomp(my @projects = `ls projects`);
	foreach (@projects){
		if(/$project/){
			$project = "projects/$project";
		}
	}

#	check to see if this is a fresh run. 
#	If so, setup the source dir
#	If not, check that the previous run was the same project.
#	If so, copy over only the files that have been changed since the last build.
#	If not, clean out source and setup source dir fresh.
	my @runtime_variables_h;
	if(-e "source/runtime_variables.h"){
		open(RUNTIME_VARIABLES_H_IN, "source/runtime_variables.h")
			or die "source/runtime_variables.h: $!\n";
		chomp(@runtime_variables_h = <RUNTIME_VARIABLES_H_IN>);
		close(RUNTIME_VARIABLES_H_IN);

		if($runtime_variables_h[0] eq "c $project"){
			chomp(my @filename = `ls $project`);
			foreach(@filename){
				if(/runtime_variables\.h/ || -M "$project/$_" < -M "source/$_"){
					`cp $project/$_ source/`;
				}
			}
		}else{
			clean();
			@runtime_variables_h = @{populate($project)};
		}
	}else{
		@runtime_variables_h = @{populate($project)};
	}
		
#	find np, nx, ny, nz, and some others in this file
	foreach (@runtime_variables_h){
		if(/numprocs\s*=\s*(\d+)/i){
			$np = $1;
		}
		if(/nx\s*=\s*(\d+)/i){
			$nx = $1;
		}
		if(/ny\s*=\s*(\d+)/i){
			$ny = $1;
		}
		if(/nz\s*=\s*(\d+)/i){
			$nz = $1;
		}
		if(/nfloats\s*=\s*(\d+)/i){
			$nfloats = $1;
		}
		if(/nsensors\s*=\s*(\d+)/i){
			$nsensors = $1;
		}
		if(/nfloatparameters\s*=\s*(\d+)/i){
			$nfloatparameters = $1;
		}
	}

#	These switches are required.
	if(!($np && $nx && defined($ny) && $nz)){
		usage();
	}	

#	set the default output directory to "output".
	if(!$output_dir){
		$output_dir = "output";
	}

#	$ndims must be either 2 or 3
	if(!$ny){$ndims = 2;}else{$ndims=3;}

#	$nx, $ny, and $nz must all be divisible by 2, 3, or 5
	check_powers($nx, "-nx");
	check_powers($ny, "-ny");
	check_powers($nz, "-nz");

#	Both (nx/2) and nz must be divisable by np!
	if(($nx/2) % $np){
		print STDERR "(nx/2) must by divisible by np.\n";
		exit(-1);
	}
	if($nz % $np){
		print STDERR "nz must be divisible by np.\n";
		exit(-1);
	}

#	prepare for compilation
	configure($np, $ndims, $nx, $ny, $nz, $output_dir, $nfloats, $nsensors,
			$nfloatparameters);

#	create the matlab data file, remebering one of its data members for use later.
	my %problem_params_h_variables =
		%{create_matlab_data($np, $ndims, $nx, $ny, $nz, $output_dir, $matlab)};	

#	generate float_positions.dat
#       KW  I changed the location to from input to source so I could remove input altogether
	if($nfloats){
		open(NFLOATS_OUT, ">source/float_positions.dat") or die "source/float_positions.dat: $!\n";
		for(my $i = 0; $i < $nfloats; $i++){
			print NFLOATS_OUT "".($i + 1)." ".
				int(rand()*eval($problem_params_h_variables{"Lx"}))." ".
				int(rand()*eval($problem_params_h_variables{"Ly"}))." ".
				int(rand()*eval($problem_params_h_variables{"Lz"})).
				"\n";
		}
		close(NFLOATS_OUT);
	}	

#	XXX This is set until we fix reporting based on the new netcdf output code. XXX
	$no_report = 1;

#	report the memory requirements for this build
	if(!$no_report){
		report_memory($np, $ndims, $nx, $ny, $nz, $output_dir);
	}

#	report the anticipated disk usage for this build
	my $ny_disk;
	if(!$ny){$ny_disk = 1;}else{$ny_disk = $ny;}
	if(!$no_report){
		report_disk_allocation($np, $ndims, $nx, $ny_disk, $nz, $output_dir, \%problem_params_h_variables);
	}

	code_gen($nx, $ny, $nz, \%problem_params_h_variables);

#	build binaries and finish up.
#	make();	
        system("sh kwmk.sh");
}

################################################################################
# usage()
#
# Print the usage information and exit.
################################################################################
sub usage{
	print STDERR "usage: setup.pl  [-output_dir OUTPUT_DIR]\n";
	print STDERR "                 [-project PROJECT_NAME]\n";
	print STDERR "                 [-no_report]\n";
	print STDERR "                 [-matlab]\n";
	print STDERR "                 [-clean]\n";
	print STDERR "                 [-help]\n";
	print STDERR "  OUPUT_DIR -> The name of the directory to use for output.\n";
	print STDERR "  PROJECT_NAME -> The name of the directory where the current project is located.\n";
	print STDERR "  All switches are optional.\n";
	print STDERR "\n";
	exit(-1);
}


################################################################################
# configure()
#
# Do the general configuration. 
# setup the output directory structure, backup the data 
# peritnant to this run, generate the appropriate source files
################################################################################
sub configure{
	my $np = $_[0];
	my $ndims = $_[1];
	my $nx = $_[2];
	my $ny = $_[3];
	my $nz = $_[4];
	my $output_dir = $_[5];
	my $nfloats = $_[6];
	my $nsensors = $_[7];
	my $nfloatparameters = $_[8];

#	configure the output directory structures.
	if(-e "output" && -l "output"){
		`rm -f $ENV{PWD}/output`;
	}	
	if(-e "output" && $output_dir ne "output"){
		print STDERR "Cannot link $output_dir to output/. Remove output/ and try again.";
		exit(-1);
	}
		print STDERR "OUTPUT DIRECTORY ". $output_dir ." \n"; 
	if(! -e "$output_dir"){
		#mkdir "$output_dir";
		system("mkdir $output_dir");
	}
	if(! -e "$output_dir/codes_etc"){
		#mkdir "$output_dir/codes_etc";
		system("mkdir $output_dir/codes_etc");
	}
	if(! -e "$output_dir/3D"){
		#mkdir "$output_dir/3D";
		system("mkdir $output_dir/3D");
	}
	if(! -e "$output_dir/2D"){
		#mkdir "$output_dir/2D";
		system("mkdir $output_dir/2D");
	}
	if(! -e "$output_dir/1D"){
		#mkdir "$output_dir/1D";
		system("mkdir $output_dir/1D");
	}
	if(! -e "$output_dir/0D"){
		#mkdir "$output_dir/0D";
		system("mkdir $output_dir/0D");
	}
	if($output_dir ne "output"){
		`ln -s $output_dir $ENV{PWD}/output`;
	}

	for(my $i = 0; $i <= 3; $i++){
		my $dirname = $i."D";
		opendir(OUTPUT_DIR, "$output_dir/$dirname");
		my @dir_contents = readdir(OUTPUT_DIR);

		foreach (@dir_contents){
			if(! (/^\.$/ || /^\.\.$/)){
				print STDERR "$output_dir/$dirname is not empty!\n";
				exit(-1);
			}
		}
		closedir(OUTPUT_DIR);
	}

#	archive this run's setup info.
	`cp -f source/*.f $output_dir/codes_etc/ 2>/dev/null`;
	`cp -f source/*.h $output_dir/codes_etc/ 2>/dev/null`;
	`cp system.conf $output_dir/codes_etc/ 2>/dev/null`;

#	build sources.mk for the Makefile from definitions in system.conf
	open(SYSTEM_CONF_IN, "system.conf") or die "system.conf: $!\n";
	chomp(my @system_conf = <SYSTEM_CONF_IN>);
	close(SYSTEM_CONF_IN);

	my $fflags;
	my $fc;
	my $flibs;
	my $run_command;

	my $previous_line = "";
	foreach (@system_conf){
		chomp;
		s/(.*)\#.*/$1/;
		if(/\\$/){
			s/\\$//;
			$previous_line = $_;
			next;
		}
		else{
			$_ = $previous_line.$_;
			$previous_line = "";
		}
		s/^\s+//;
		s/\s+$//;
		s/OUTPUT_DIR/$output_dir/g;
		s/NUMPROCS/$np/g;

		if($_){
			if(/^FFLAGS/){$fflags = $_;}
			elsif(/^FLIBS/){$flibs = $_;}
			elsif(/^FC/){$fc = $_;}
			elsif(/^RUN_COMMAND/){
				$run_command = $_;
				if(/machfile/){
					if($np > 32){
						print STDERR "source/machfile_0-15 does not contain enough entries."
							." You must create your own machfile!\n";}
					else{
						open(MACHFILE_IN, "source/machfile_0-15") or die "source/machfile_0-15: $!\n";
						chomp(my @machfile = <MACHFILE_IN>);
						close(MACHFILE_IN);
						open(MACHFILE_OUT, ">machfile") or die "machfile: $!\n";
						for(my $i = 0; $i < $np; $i++){
							print MACHFILE_OUT "$machfile[$i]\n";
						}
						close(MACHFILE_OUT);
						`cp machfile $output_dir/codes_etc/`;
					}
				}
			}
		}
	}

#	Tell the makefile which files are where.
	open SYSTEM_MK_OUT, ">source/system.mk" or die "source/system.mk: $!\n";

	print SYSTEM_MK_OUT "$fc\n";
	print SYSTEM_MK_OUT "$fflags\n";
	print SYSTEM_MK_OUT "$flibs\n";

	close(SYSTEM_MK_OUT);

#	make the run script
	$run_command =~ s/^RUN_COMMAND\s*=\s*//;
	open(RUN_OUT, ">run") or die "run: $!\n";
	print RUN_OUT "#!/bin/sh\n$run_command\n";
	close(RUN_OUT);

	`cp run $output_dir/codes_etc/ 2>/dev/null`;
	`chmod 750 run`;
}


################################################################################
# code_gen()
################################################################################
sub code_gen{
	my $nx = $_[0];
	my $ny = $_[1];
	my $nz = $_[2];
	my %problem_params_h = %{$_[3]};

	my $init_write_netcdf_h = 
"c      Output u,v,w,s1 and s2 on full grid to netcdf files cfd3D_xxxxxx\n".
"c      where xxxxxx corresponds to the value of istep.\n".
"       if( mod(istep,i_all) .eq. 0 ) then\n".
"        call MPI_BARRIER(comm,ierr)   \n".
"        pressure_flag='no_pressure'    ! pressure not yet computed, don't write it out\n".
"        call init_netcdf(start3D,count3D,scalars,nx,ny,nyplanes,nz,locnz,istep,\n".
"     &                   Lx,Ly,Lz,DGRAD,bc_flag,vort_flag,\n".
"     &                   time,work_cplx(1,1,1,8),myid,numprocs,rootname)\n".
"        call write_netcdf(start3D,count3D,work(1,1,1,4),work(1,1,1,5),work(1,1,1,6),\n".
"     &                            work(1,1,1,7),work(1,1,1,9),work(1,1,1,8),\n".
"     &                            work(1,1,1,1),work(1,1,1,2),work(1,1,1,3),\n".
"     &                            Lx,Ly,Lz,nx,ny,nyplanes,nz,locnx,locnz,myid,numprocs,istep,\n".
"     &                            U0,s1_scale,s2_scale,rho_0,bc_flag,\n".
"     &                            vort_flag,pressure_flag,rootname)\n".
"       endif\n";


	my $trans_write_netcdf_h = 
"c      At output steps only, inverse transform pressure and add\n".
"c      the physical space values to the current netcdf file.\n".
"       if( mod(istep,i_all) .eq. 0 ) then\n".
"c        inverse transform work_cplx(:,:,:,8) to get spatial representation\n".
"c        store physical space result in work(:,:,:,1)\n".
"        if( bc_flag .eq. 'zperiodic' ) then\n".
"         tswitch='f'\n".
"        elseif( bc_flag .eq. 'zslip' ) then\n".
"         tswitch='c'\n".
"        endif\n".
"        iswitch=-1\n".
"        itrig=0\n".
"        call transform3d(work(1,1,1,1),work_cplx(1,1,1,8),nx,ny,nz,\n".
"     &                   iswitch,tswitch,itrig,\n".
"     &                   trigx,trigy,trigz,wn,ifax,ifay,ifaz,\n".
"     &                   num_dims,work(1,1,1,2),work(1,1,1,4),xt,myid,\n".
"     &                   numprocs,locnx,locnz,\n".
"     &                   comm,twoslice,subslice)\n".
"        pressure_flag='do_pressure_only'\n".
"         call write_netcdf(start3D,count3D,work(1,1,1,1),work(1,1,1,8),work(1,1,1,8),\n".
"     &                            work(1,1,1,8),work(1,1,1,8),work(1,1,1,8),\n".
"     &                            work(1,1,1,8),work(1,1,1,8),work(1,1,1,8),\n".
"     &                            Lx,Ly,Lz,nx,ny,nyplanes,nz,locnx,locnz,myid,numprocs,istep,\n".
"     &                            U0,s1_scale,s2_scale,rho_0,bc_flag,\n".
"     &                            vort_flag,pressure_flag,rootname)\n".
"       endif  ! end of block for transforming/writing pressure\n";


	open(OUTPUT_CONTROL_IN, "source/output_control") or die "source/output_control: $!\n";
	chomp(my @output_control = <OUTPUT_CONTROL_IN>);
	close(OUTPUT_CONTROL_IN);


	open(INIT_WRITE_NETCDF_H_OUT, ">source/init_write_netcdf.h") or die "source/init_write_netcdf.h: $!\n";
	open(TRANS_WRITE_NETCDF_H_OUT, ">source/trans_write_netcdf.h") or die "source/trans_write_netcdf.h: $!\n";
	
	print TRANS_WRITE_NETCDF_H_OUT "c XXX  This code is auto-generated by the setup script! XXX\n";
	print TRANS_WRITE_NETCDF_H_OUT "c XXX  Any changes by you will be overwriten the next time you run setup!! XXX\n";
	print INIT_WRITE_NETCDF_H_OUT "c XXX  This code is auto-generated by the setup script! XXX\n";
	print INIT_WRITE_NETCDF_H_OUT "c XXX  Any changes by you will be overwriten the next time you run setup!! XXX\n";

	my $counter = 1;
	foreach (@output_control){
		s/#.*//;
		s/\s//g;
		s/\(//g;
		s/\)//g;
		if($_){
			my @tmp = split /,/;

			# nx
			if($tmp[0] eq "*"){ 
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(1) = 1\n";	
				print TRANS_WRITE_NETCDF_H_OUT "        count3D(1) = $nx\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(1) = 1\n";	
				print INIT_WRITE_NETCDF_H_OUT "        count3D(1) = $nx\n";	
			}elsif($tmp[0] =~ /\:/){
				my @tmp2 = split /\:/, $tmp[0];
				if($tmp2[1] > $nx){
					print STDERR "output_control line $counter: $tmp2[1] greater than nx\n";
					exit(-1);
				}	
				my $count = $tmp2[1] - $tmp2[0] + 1;
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(1) = $tmp2[0]\n";	
				print TRANS_WRITE_NETCDF_H_OUT "        count3D(1) = $count\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(1) = $tmp2[0]\n";	
				print INIT_WRITE_NETCDF_H_OUT "        count3D(1) = $count\n";	
			}else{
				if($tmp[0] > $nx){
					print STDERR "output_control line $counter: $tmp[0] greater than nx\n";
					exit(-1);
				}	
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(1) = $tmp[0]\n";	
				print TRANS_WRITE_NETCDF_H_OUT "        count3D(1) = 1\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(1) = $tmp[0]\n";	
				print INIT_WRITE_NETCDF_H_OUT "        count3D(1) = 1\n";	
			}

			# ny
			if($tmp[1] eq "*"){ 
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(2) = 1\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(2) = 1\n";	
				if($ny == 0){
					print TRANS_WRITE_NETCDF_H_OUT "        count3D(2) = 1\n";
					print INIT_WRITE_NETCDF_H_OUT "        count3D(2) = 1\n";
				}else{
					print TRANS_WRITE_NETCDF_H_OUT "        count3D(2) = $ny\n";
					print INIT_WRITE_NETCDF_H_OUT "        count3D(2) = $ny\n";
				}
			}elsif($tmp[1] =~ /\:/){
				my @tmp2 = split /\:/, $tmp[1];
				if($ny != 0 && $tmp2[1] > $ny){
					print STDERR "output_control line $counter: $tmp2[1] greater than ny\n";
					exit(-1);
				}
				my $count = $tmp2[1] - $tmp2[0] + 1;
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(2) = $tmp2[0]\n";	
				print TRANS_WRITE_NETCDF_H_OUT "        count3D(2) = $count\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(2) = $tmp2[0]\n";	
				print INIT_WRITE_NETCDF_H_OUT "        count3D(2) = $count\n";	
			}else{
				if($ny != 0 && $tmp[1] > $ny){
					print STDERR "output_control line $counter: $tmp[1] greater than ny\n";
					exit(-1);
				}	
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(2) = $tmp[1]\n";	
				print TRANS_WRITE_NETCDF_H_OUT "        count3D(2) = 1\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(2) = $tmp[1]\n";	
				print INIT_WRITE_NETCDF_H_OUT "        count3D(2) = 1\n";	
			}

# nz  SOMETIMES COUNT=NZ , if bc_flag='zslip' there is good data in the nz+1 plane
			if($tmp[2] eq "*"){ 
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(3) = 1\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(3) = 1\n";	
				if($problem_params_h{"bc_flag"} eq "\'zslip\'"){
					print TRANS_WRITE_NETCDF_H_OUT "        count3D(3) = $nz + 1\n";	
					print INIT_WRITE_NETCDF_H_OUT "        count3D(3) = $nz + 1\n";	
				}else{
					print TRANS_WRITE_NETCDF_H_OUT "        count3D(3) = $nz\n";	
					print INIT_WRITE_NETCDF_H_OUT "        count3D(3) = $nz\n";	
				}
			}elsif($tmp[2] =~ /\:/){
				my @tmp2 = split /\:/, $tmp[2];
				if($tmp2[1] > $nz){
					print STDERR "output_control line $counter: $tmp2[1] greater than nz\n";
					exit(-1);
				}	
				my $count = $tmp2[1] - $tmp2[0] + 1;
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(3) = $tmp2[0]\n";
				print TRANS_WRITE_NETCDF_H_OUT "        count3D(3) = $count\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(3) = $tmp2[0]\n";
				print INIT_WRITE_NETCDF_H_OUT "        count3D(3) = $count\n";	
			}else{
				if($tmp[2] > $nz+1){
					print STDERR "output_control line $counter: $tmp[2] greater than nz+1\n";
					exit(-1);
				}	
				print TRANS_WRITE_NETCDF_H_OUT "        start3D(3) = $tmp[2]\n";	
				print TRANS_WRITE_NETCDF_H_OUT "        count3D(3) = 1\n";	
				print INIT_WRITE_NETCDF_H_OUT "        start3D(3) = $tmp[2]\n";	
				print INIT_WRITE_NETCDF_H_OUT "        count3D(3) = 1\n";	
			}

			print TRANS_WRITE_NETCDF_H_OUT "        rootname = '$tmp[4]'\n";
			print INIT_WRITE_NETCDF_H_OUT "        rootname = '$tmp[4]'\n";

			print TRANS_WRITE_NETCDF_H_OUT "        i_all = $tmp[3]/dt\n";
			print INIT_WRITE_NETCDF_H_OUT "        i_all = $tmp[3]/dt\n";

			print TRANS_WRITE_NETCDF_H_OUT "$trans_write_netcdf_h";
			print INIT_WRITE_NETCDF_H_OUT "$init_write_netcdf_h";

			print TRANS_WRITE_NETCDF_H_OUT "\n\n";
			print INIT_WRITE_NETCDF_H_OUT "\n\n";
		}
		$counter++;
	}	
}	


################################################################################
# make()
# 
# Build the binaries.
################################################################################
sub make{

        print " coucou make\n"; 
	if(! system("cd source && make flow_solve.x")){
		print "Finished building. Type \"./run\" to begin.\n";
	}	
}

################################################################################
# report_memory()
#
# Reports the memory requirements of this particular build.
################################################################################
sub report_memory{	

	my $number_of_integers = 0;
	my $number_of_reals = 0;
	my $number_of_complexs = 0;
	my $number_of_characters = 0;

#       integer numprocs,num_dims,nx,ny,nz,locnx,locnz,nwork
	$number_of_integers += 8;

#       character*80 netcdf_file
	$number_of_characters += 80;

#       parameter(numprocs=2)         ! must match specification (-np) in run.sh 
#       parameter(num_dims=3)         ! num_dims and ny must be consistent
#       parameter(nx=32,ny=32,nz=32)  ! ny=0 for 2d problems
#       parameter(locnz=nz/numprocs,locnx=(nx/2)/numprocs)
#       parameter(nwork=2*(nx)*(nz+1))   ! max of [nx*(nz+1),nx*ny]
	my $numprocs = $_[0];
	my $ndims = $_[1];
	my $nx = $_[2];
	my $ny = $_[3];
	my $nz = $_[4];
	my $locnz = $nz / $numprocs;
	my $locnx = ($nx/2) / $numprocs;
	my $nwork = 2 * $nx * ($nz + 1);

#       integer nfloats,nsensors,nfloatparameters
#       parameter(nfloats=0,nsensors=7,nfloatparameters=4)
	$number_of_integers += 3;
	my $nfloats = 0; 
	my $nsensors = 7;
	my $nfloatparameters = 4;

#       real rho_bar(locnz+1),ddz(locnz+1),d2dz2(locnz+1)
	$number_of_reals += 3 * ($locnz + 1);

#       real u(nx+2,ny+1,locnz+1),v(nx+2,ny+1,locnz+1)
#       real w(nx+2,ny+1,locnz+1),pd(nx+2,ny+1,locnz+1)
#       real ps1(nx+2,ny+1,locnz+1)
	$number_of_reals += 5 * (($nx + 2) * ($ny + 1) * ($locnz + 1));

#       complex u_cplx(locnx,ny+1,nz+1,2),v_cplx(locnx,ny+1,nz+1,2)
#       complex w_cplx(locnx,ny+1,nz+1,2),pd_cplx(locnx,ny+1,nz+1,2)
#       ps1_cplx(locnx,ny+1,nz+1,2)
	$number_of_complexs += 5 * ($locnx * ($ny + 1) * ($nz + 1) * 2);

#       complex Tr1(locnx,ny+1,nz+1,3),Tr2(locnx,ny+1,nz+1,3)
#       complex Tr3(locnx,ny+1,nz+1,3),Tr4(locnx,ny+1,nz+1,3)
#       complex Tr5(locnx,ny+1,nz+1,3)
	$number_of_complexs += 5 * ($locnx * ($ny + 1) * ($nz + 1) * 3);

#       real work(nx+2,ny+1,locnz+1,13)
#       complex work_cplx(locnx,ny+1,nz+1,13)
#       real scratch(nwork)
	$number_of_reals += ($nx + 2) * ($ny + 1) * ($locnz + 1) * 13;
	$number_of_complexs += $locnx * ($ny + 1) * ($nz + 1) * 13;
	$number_of_reals += $nwork;

#       real wnx(locnx),wny(ny+1),wnz(nz+1)
#       real amp(locnx,ny+1,nz+1,2)
	$number_of_reals += $locnx + ($ny + 1) + ($nz + 1);
	$number_of_reals += $locnx * ($ny + 1) * ($nz + 1) * 2;

#       real trigx(3*(nx+2)/2+1),trigy(2*(ny+1)),trigz(2*nz)
#       complex wn(nz/4+1),xt(nx+2, nz/2+1)
#       integer ifax(13),ifay(13),ifaz(13),itrig
#       character tswitch
	$number_of_reals += (3 * ($nx + 2) / 2 + 1) + (2 * ($ny + 1)) + (2 * $nz);
	$number_of_complexs += ($nz / 4 + 1) + (($nx + 2) * ($nz / 2 + 1));
	$number_of_integers += 13 + 13 + 13 + 1;
	$number_of_characters += 1;

#       real float_variables(6,nfloats)
	$number_of_reals += 6 * $nfloats;

#       real sensor_data(nsensors,nfloats)
	$number_of_reals += $nsensors * $nfloats;

#       real float_parameters(nfloatparameters,nfloats)
#       real z_close,z_offset,delta_w
	$number_of_reals += $nfloatparameters * $nfloats;
	$number_of_reals += 3;

#       real k_rk4(5,nfloats,4)
	$number_of_reals += 5 * $nfloats * 4;

#       real Rot,Ri,Re,Pr
	$number_of_reals += 4;

#       real Lx,Ly,Lz,dx,dy,dz,U0,DGRAD,rho_0,f,g,kappa,nu,kmax
#       real pi,xlat,xOmega,bfreq,dt,t_start,t_end,t_write_all
#       real t_write_selective,t_stat,t_floats_on
#       real time,time_scale,delta_u,delta_rho,z_0,h_0,k_0,a,b,c
#       real x0,y0,z0,interp_val,efactor,T_diss
#	real S1GRAD,delta_s1
	$number_of_reals += 14 + 8 + 3 + 10 + 6 + 2;

#       integer i,j,k,istep,nyplanes,istart_floats,N,NM1,M,MM1,MM2
#       integer istart,iend,i_all,i_selective,i_stat,tmp,j_float_time
#       integer subslice,twoslice,comm,status,myid,iproc,nprocs,ierr,p
	$number_of_integers += 11 + 7 + 9;

#       character*80 bc_flag,float_switch,step_flag,prelim_flag,fcs_flag
#       character*80 diss_flag
#       character*3  vort_flag,force_flag	
	$number_of_characters += 80 * (6) + 3 * 2;

	my $memory = ((($number_of_integers * $SIZEOF_INTEGER)
				+ ($number_of_reals * $SIZEOF_REAL)
				+ ($number_of_complexs * $SIZEOF_COMPLEX)
				+ ($number_of_characters * $SIZEOF_CHARACTER))
			/ 1000000);

	print "\nMemory requirements: \n";
	print "   Memory required (total) -> $memory MB's\n";
	print "   Memory required (per processor) -> ".($memory/$numprocs)." MB's\n";

	print "\nMemory statistics for local machine: \n";
	system("free -mto");
	print "\n";
}

################################################################################
# report_disk_allocation() 
#
# report the anticipated disk usage for this build.
################################################################################
sub report_disk_allocation{

	my $numprocs = $_[0];
	my $ndims = $_[1];
	my $nx = $_[2];
	my $ny = $_[3];
	my $nz = $_[4];
	my $output_dir = $_[5];
	my $locnz = $nz / $numprocs;

	my %problem_params_h_variables = %{$_[6]};

	my $t_start = eval($problem_params_h_variables{"t_start"});
	my $t_end = eval($problem_params_h_variables{"t_end"});
	my $t_write_all = eval($problem_params_h_variables{"t_write_all"});
	my $dt = eval($problem_params_h_variables{"dt"});
	my $vort_flag = eval($problem_params_h_variables{"vort_flag"});

	my $num_outputs = int(($t_end - $t_start)/$t_write_all) + 1;

	my $num_variables = 6;
	if($vort_flag =~ /yes/i){
		$num_variables = 8;
	}

	my $base_case = ($numprocs * $num_outputs) - $num_outputs;

	my $result1 = $base_case * $num_variables * $SIZEOF_REAL * $nx * $ny * $locnz;
	my $result2 = $num_outputs * $num_variables * $SIZEOF_REAL * $nx * $ny * ($locnz + 1);
	my $result = ($result1 + $result2) / 1000000;

	print "\nApproximate disk space allocation anticipated: $result MB's\n";

	print "\nDisk statistics for $ENV{\"PWD\"}/$output_dir: \n";
	system("df -m $output_dir");
	print "\n";
}

################################################################################
# create_matlab_data
#
# creates the output/codes_etc/data.m file.
################################################################################
sub create_matlab_data{
	my $np = $_[0];
	my $ndims = $_[1];
	my $nx = $_[2];
	my $ny = $_[3];
	my $nz = $_[4];
	my $output_dir = $_[5];
	my $matlab = $_[6];

	my %problem_params_h_variables;

	open(IN_FILE_IN, "source/problem_params.h") or die "source/problem_params.h: $!\n";
	chomp(my @in_file = <IN_FILE_IN>);
	close(IN_FILE_IN);

	for(my $i = 0; $i <= $#in_file; $i++){
#		deal with the case where long lines of fortran are continued on
#		the next line.
LINE_WRAP:	if($i != $#in_file && $in_file[$i+1] =~ /^\s{5}\S/){
			$in_file[$i+1] =~ s/^\s{5}\S//;
			$in_file[$i] .= $in_file[$i+1];
			splice(@in_file, $i+1, 1);
			$i--;
			goto LINE_WRAP;
		}

		$_ = $in_file[$i];
		s/^c.*//;
		s/\s//g;
		s/(.*)\!.*/$1/;

#		if there is still data on this line, then grab any variables and
#		store thier value in the resultant hash.
		if(/\=/){
			my @tmp = split /=/;
			my $tmp = $tmp[1];

#			grab any tokens and see if they contain valid data in any of
#			the existing hash's.
			while($tmp =~ /([a-zA-Z]\w*)/){
				my $foo = $1;
				if(exists($problem_params_h_variables{$foo})){
					$tmp[1] =~ s/$foo/$problem_params_h_variables{$foo}/;
				}
				$tmp =~ s/$foo//;
			}
			$tmp[1] =~ s/\*\*/^/g;
			$problem_params_h_variables{$tmp[0]} = $tmp[1];
		}	
	}

	if($matlab){
		open(DATA_M_OUT, ">output/codes_etc/data.m") or die "output/codes_etc/data.m: $!\n";
		print DATA_M_OUT "nx=$nx;\n";
		print DATA_M_OUT "ny=$ny;\n";
		print DATA_M_OUT "nz=$nz;\n";
		print DATA_M_OUT "Lx=$problem_params_h_variables{\"Lx\"};\n";
		print DATA_M_OUT "Ly=$problem_params_h_variables{\"Ly\"};\n";
		print DATA_M_OUT "Lz=$problem_params_h_variables{\"Lz\"};\n";
		print DATA_M_OUT "f=$problem_params_h_variables{\"f\"};\n";
		print DATA_M_OUT "g=$problem_params_h_variables{\"g\"};\n";
		print DATA_M_OUT "rho_0=$problem_params_h_variables{\"rho_0\"};\n";
		print DATA_M_OUT "bfreq=$problem_params_h_variables{\"bfreq\"};\n";

		print DATA_M_OUT "force_flag=$problem_params_h_variables{\"force_flag\"};\n";
	}

#	save this to use again later.
	\%problem_params_h_variables;
}

################################################################################
# check_powers()
#
# checks the input to make sure it is only divisible by 2, 3, or 5.
################################################################################
sub check_powers{
	my $number = $_[0];
	if(!$number){return;}

	while($number != 1){
		if(!($number % 2)){$number /= 2;}
		elsif(!($number % 3)){$number /= 3;}
		elsif(!($number % 5)){$number /= 5;}
		else{
			print STDERR "$_[1] can only be divisible by 2, 3, or 5.\n";
			exit(-1);
		}
	}
}

########################################################################
# atan()
#
# Wraper for perls native atan2 so perl can eval fortran atan calls.
########################################################################
sub atan{
	atan2($_[0],1);
}

########################################################################
# clean()
#
# a couple of lines that clean out the source tree. 
########################################################################
sub clean{
	`cd source && rm * 2>/dev/null`;
	`rm flow_solve.x 2>/dev/null`;
	`rm run 2>/dev/null`;
}

########################################################################
# populate()
#
# Populates the source tree and properly mangles runtime_variables.h
########################################################################
sub populate{
	my $project = $_[0];
	my @runtime_variables_h;

	`cp projects/trivial/\* source/ 2>/dev/null`;
	`cp $project/\* source/ 2>/dev/null`;

	open(RUNTIME_VARIABLES_H_IN, "source/runtime_variables.h")
		or die "source/runtime_variables.h: $!\n";
	chomp(@runtime_variables_h = <RUNTIME_VARIABLES_H_IN>);
	close(RUNTIME_VARIABLES_H_IN);

	open(RUNTIME_VARIABLES_H_OUT, ">source/runtime_variables.h")
		or die "source/runtime_variables.h: $!\n";
	splice(@runtime_variables_h, 0, 0, "c $project");
	foreach(@runtime_variables_h){
		print RUNTIME_VARIABLES_H_OUT "$_\n";
	} 
	close(RUNTIME_VARIABLES_H_OUT);

	\@runtime_variables_h;
}
