https://github.com/geodynamics/citcoms
Revision 45be8f2e32748cd56c3154bbdcd61da3fddda4bd authored by Eh Tan on 09 March 2007, 00:25:03 UTC, committed by Eh Tan on 09 March 2007, 00:25:03 UTC
* Seperating composition codes from tracer codes, several struct members in E->trace are moved to E->composition
* Add chemical buoyancy term
* Renamed thermal_buoyancy() to get_buoyancy() to reflect the fact that this function computes buoyancy according to temperature/composition/phase
* Fix and optimize the fix_*() functions
* Remove some static variables, or converte them to const
* Change the type of E->SinCos from float to double, and replace E->trace.DSinCos
* Provide default values for "required" input parameters
* Renamed tracer_restart to tracer_ic_method, also remapped the options
* Removed option for Cartesian tracer input and trace.iwrite_tracers_every
* Replaced trace.itracer_type by composition.ichemical_buoyancy
* Optionally output tracers and composition fields

1 parent 068a4d5
Raw File
Tip revision: 45be8f2e32748cd56c3154bbdcd61da3fddda4bd authored by Eh Tan on 09 March 2007, 00:25:03 UTC
A big patch for tracers and composition:
Tip revision: 45be8f2
Full_tracer_advection.c
/*
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *<LicenseText>
 *
 * CitcomS by Louis Moresi, Shijie Zhong, Lijie Han, Eh Tan,
 * Clint Conrad, Michael Gurnis, and Eun-seo Choi.
 * Copyright (C) 1994-2005, California Institute of Technology.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *</LicenseText>
 *
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <math.h>
#include "element_definitions.h"
#include "global_defs.h"
#include "parsing.h"
#include "parallel_related.h"
#include "composition_related.h"

static void get_neighboring_caps(struct All_variables *E);
static void pdebug(struct All_variables *E, int i);

static void fix_radius(struct All_variables *E,
                       double *radius, double *theta, double *phi,
                       double *x, double *y, double *z);
static void fix_theta_phi(double *theta, double *phi);
static void fix_phi(double *phi);
static void predict_tracers(struct All_variables *E);
static void correct_tracers(struct All_variables *E);
static int isum_tracers(struct All_variables *E);



/******* FULL TRACER INPUT *********************/

void full_tracer_input(E)
     struct All_variables *E;

{
    int m = E->parallel.me;

    /* Initial condition, this option is ignored if E->control.restart is 1,
    *  ie. restarted from a previous run */
    /* tracer_ic_method=0 (random generated array) */
    /* tracer_ic_method=1 (all proc read the same file) */
    /* tracer_ic_method=2 (each proc reads its restart file) */
    if(E->control.restart)
        E->trace.ic_method = 2;
    else {
        input_int("tracer_ic_method",&(E->trace.ic_method),"0,0,nomax",m);

        if (E->trace.ic_method==0)
	    input_int("tracers_per_element",&(E->trace.itperel),"10,0,nomax",m);
        else if (E->trace.ic_method==1)
	    input_string("tracer_file",E->trace.tracer_file,"tracer.dat",m);
        else if (E->trace.ic_method==2) {
        }
        else {
	    fprintf(stderr,"Sorry, tracer_ic_method only 0, 1 and 2 available\n");
	    fflush(stderr);
	    parallel_process_termination();
        }
    }

    /* Advection Scheme */

    /* itracer_advection_scheme=1 (simple predictor corrector -uses only V(to)) */
    /* itracer_advection_scheme=2 (predictor-corrector - uses V(to) and V(to+dt)) */

    E->trace.itracer_advection_scheme=2;
    input_int("tracer_advection_scheme",&(E->trace.itracer_advection_scheme),
	      "2,0,nomax",m);

    if (E->trace.itracer_advection_scheme==1)
	{
	}
    else if (E->trace.itracer_advection_scheme==2)
	{
	}
    else
	{
	    fprintf(stderr,"Sorry, only advection scheme 1 and 2 available (%d)\n",E->trace.itracer_advection_scheme);
	    fflush(stderr);
	    parallel_process_termination();
	}


    /* Interpolation Scheme */
    /* itracer_interpolation_scheme=1 (gnometric projection) */
    /* itracer_interpolation_scheme=2 (simple average) */

    E->trace.itracer_interpolation_scheme=1;
    input_int("tracer_interpolation_scheme",&(E->trace.itracer_interpolation_scheme),
	      "1,0,nomax",m);
    if (E->trace.itracer_interpolation_scheme<1 || E->trace.itracer_interpolation_scheme>2)
	{
	    fprintf(stderr,"Sorry, only interpolation scheme 1 and 2 available\n");
	    fflush(stderr);
	    parallel_process_termination();
	}

    /* Regular grid parameters */
    /* (first fill uniform del[0] value) */
    /* (later, in make_regular_grid, will adjust and distribute to caps */

    E->trace.deltheta[0]=1.0;
    E->trace.delphi[0]=1.0;
    input_double("regular_grid_deltheta",&(E->trace.deltheta[0]),"1.0",m);
    input_double("regular_grid_delphi",&(E->trace.delphi[0]),"1.0",m);


    /* Analytical Test Function */

    E->trace.ianalytical_tracer_test=0;
    input_int("analytical_tracer_test",&(E->trace.ianalytical_tracer_test),
	      "0,0,nomax",m);


    composition_input(E);
    return;
}

/***** FULL TRACER SETUP ************************/

void full_tracer_setup(E)
     struct All_variables *E;
{

    char output_file[200];
    int m;
    void write_trace_instructions();
    void viscosity_checks();
    void make_tracer_array();
    void initialize_old_composition();
    void find_tracers();
    void make_regular_grid();
    void initialize_tracer_elements();
    void define_uv_space();
    void determine_shape_coefficients();
    void check_sum();
    void read_tracer_file();
    void analytical_test();
    void tracer_post_processing();
    void restart_tracers();

    m=E->parallel.me;
    E->trace.itracing=1;

    /* some obscure initial parameters */
    /* This parameter specifies how close a tracer can get to the boundary */
    E->trace.box_cushion=0.00001;

    /* AKMA turn this back on after debugging */
    E->trace.itracer_warnings=1;

    /* Determine number of tracer quantities */

    /* advection_quantites - those needed for advection */
    // TODO: generalize it
    if (E->trace.itracer_advection_scheme==1) E->trace.number_of_advection_quantities=12;
    if (E->trace.itracer_advection_scheme==2) E->trace.number_of_advection_quantities=12;

    /* extra_quantities - used for composition, etc.    */
    /* (can be increased for additional science i.e. tracing chemistry */

    // TODO: how to move this part to Composition_related.c?
    if (E->composition.ichemical_buoyancy==1) E->trace.number_of_extra_quantities=1;
    else E->trace.number_of_extra_quantities=0;

    E->trace.number_of_tracer_quantities=E->trace.number_of_advection_quantities +
	E->trace.number_of_extra_quantities;



    /* Fixed positions in tracer array */
    /* Comp is always in etrac position 0  */
    /* Current coordinates are always kept in rtrac positions 0-5 */
    /* Other positions may be used depending on advection scheme and/or science being done */

    /* open tracing output file */

    sprintf(output_file,"%s.tracer_log.%d",E->control.data_file,m);
    E->trace.fpt=fopen(output_file,"w");


    /* reset statistical counters */

    E->trace.istat_isend=0;
    E->trace.istat_iempty=0;
    E->trace.istat_elements_checked=0;
    E->trace.istat1=0;

    /* Some error control regarding size of pointer arrays */

    if (E->trace.number_of_advection_quantities>99)
	{
	    fprintf(E->trace.fpt,"ERROR(initialize_trace)-increase 2nd position size of rtrac in global.h\n");
	    fflush(E->trace.fpt);
	    parallel_process_termination();
	}
    if (E->trace.number_of_extra_quantities>99)
	{
	    fprintf(E->trace.fpt,"ERROR(initialize_trace)-increase 2nd position size of etrac in global.h\n");
	    fflush(E->trace.fpt);
	    parallel_process_termination();
	}
    if (E->trace.number_of_tracer_quantities>99)
	{
	    fprintf(E->trace.fpt,"ERROR(initialize_trace)-increase 2nd position size of rlater in global.h\n");
	    fflush(E->trace.fpt);
	    parallel_process_termination();
	}

    write_trace_instructions(E);

    /* Some error control */

    if (E->sphere.caps_per_proc>1)
	{
	    fprintf(E->trace.fpt,"This code does not work for multiple caps per processor!\n");
	    fflush(E->trace.fpt);
	    parallel_process_termination();
	}

    get_neighboring_caps(E);

    if (E->trace.ic_method==0) {
        make_tracer_array(E);

        if (E->composition.ichemical_buoyancy==1)
            init_tracer_composition(E);
    }
    else if (E->trace.ic_method==1) {
        read_tracer_file(E);

        if (E->composition.ichemical_buoyancy==1)
            init_tracer_composition(E);
    }
    else if (E->trace.ic_method==2) restart_tracers(E);
    else
	{
	    fprintf(E->trace.fpt,"Not ready for other inputs yet\n");
	    fflush(E->trace.fpt);
	    exit(10);
	}

    /* flush and wait for not real reason but it can't hurt */
    fflush(E->trace.fpt);
    parallel_process_sync(E);


    if (E->trace.itracer_interpolation_scheme==1)
	{
	    define_uv_space(E);
	    determine_shape_coefficients(E);
	}


    /* flush and wait for not real reason but it can't hurt */
    fflush(E->trace.fpt);
    parallel_process_sync(E);

    make_regular_grid(E);

    /* flush and wait for not real reason but it can't hurt */
    fflush(E->trace.fpt);
    parallel_process_sync(E);


    /* find elements */

    find_tracers(E);

    /* total number of tracers  */

    E->trace.ilast_tracer_count = isum_tracers(E);
    fprintf(E->trace.fpt, "Sum of Tracers: %d\n", E->trace.ilast_tracer_count);

    if (E->trace.ianalytical_tracer_test==1)
	{
	    //TODO: walk into this code...
	    analytical_test(E);
	    parallel_process_termination();
	}

    composition_setup(E);

    tracer_post_processing(E);

    return;
}


/******* TRACING *************************************************************/
/*                                                                           */
/* This function is the primary tracing routine called from Citcom.c         */
/* In this code, unlike the original 3D cartesian code, force is filled      */
/* during Stokes solution. No need to call thermal_buoyancy() after tracing. */


void full_tracer_advection(E)
     struct All_variables *E;
{

    void check_sum();
    void tracer_post_processing();
    void write_tracers();


    fprintf(E->trace.fpt,"STEP %d\n",E->monitor.solution_cycles);
    fflush(E->trace.fpt);

    /* presave last timesteps tracers as preback */
    //TODO: use system("mv oldfile oldfile.bak\n") instead
    if  ( (E->monitor.solution_cycles % E->control.record_every)==0) {
        //TODO: migrate to output_tracer
	//write_tracers(E,3);
    }

    /* advect tracers */

    predict_tracers(E);
    correct_tracers(E);

    check_sum(E);

    //TODO: move
    if (E->composition.ichemical_buoyancy==1) {
	fill_composition(E);
    }

    tracer_post_processing(E);

    return;
}



/********* TRACER POST PROCESSING ****************************************/

void tracer_post_processing(E)
     struct All_variables *E;

{

    char output_file[200];

    double convection_time,tracer_time;
    double trace_fraction,total_time;

    void get_bulk_composition();
    void write_tracers();

    static int been_here=0;

    //TODO: fix this function
    //if (E->composition.ichemical_buoyancy==1) get_bulk_composition(E);

    if ( ((E->monitor.solution_cycles % E->control.record_every)==0) || (been_here==0))
	{
            //TODO: migrate to output_tracer
	    //write_tracers(E,1);
	}

    //TODO: move to Output.c
    if ( ((E->monitor.solution_cycles!=E->control.restart)&&(E->monitor.solution_cycles!=0) ))
	{
            //TODO: migrate to output_tracer
	    //write_tracers(E,2);
	}


    fprintf(E->trace.fpt,"Number of times for all element search  %d\n",E->trace.istat1);
    if (been_here!=0)
	{
	    fprintf(E->trace.fpt,"Number of tracers sent to other processors: %d\n",E->trace.istat_isend);
	    fprintf(E->trace.fpt,"Number of times element columns are checked: %d \n",E->trace.istat_elements_checked);
	    if (E->composition.ichemical_buoyancy==1)
		{
		    fprintf(E->trace.fpt,"Empty elements filled with old compositional values: %d (%f percent)\n",
			    E->trace.istat_iempty,(100.0*E->trace.istat_iempty/E->lmesh.nel));
		}
	}

/*     fprintf(E->trace.fpt,"Error fraction: %f  (composition: %f)\n",E->trace.error_fraction,E->trace.bulk_composition); */



    /* reset statistical counters */

    E->trace.istat_isend=0;
    E->trace.istat_iempty=0;
    E->trace.istat_elements_checked=0;
    E->trace.istat1=0;

    /* compositional and error fraction data files */
    //TODO: move
    if (E->parallel.me==0)
	{
	    if (been_here==0)
		{
		    if (E->composition.ichemical_buoyancy==1)
			{
			    sprintf(output_file,"%s.error_fraction.data",E->control.data_file);
			    E->trace.fp_error_fraction=fopen(output_file,"w");

			    sprintf(output_file,"%s.composition.data",E->control.data_file);
			    E->trace.fp_composition=fopen(output_file,"w");
			}
		}

	    if (E->composition.ichemical_buoyancy==1)
		{
                    //TODO: to be init'd
		    fprintf(E->trace.fp_error_fraction,"%e %e\n",E->monitor.elapsed_time,E->trace.error_fraction);
		    fprintf(E->trace.fp_composition,"%e %e\n",E->monitor.elapsed_time,E->trace.bulk_composition);

		    fflush(E->trace.fp_error_fraction);
		    fflush(E->trace.fp_composition);
		}

	}



    fflush(E->trace.fpt);

    if (been_here==0) been_here++;

    return;
}

/*********** WRITE TRACERS **********************************************/
/*                                                      */
/* if iflag=1, rewrite over last save array             */
/* if iflag=2, write periodic save file and gzip        */



void write_tracers(E,iflag)
     struct All_variables *E;
     int iflag;

{

    char output_file[200];
    /* char doit_string[200]; */
    FILE *fpcomp;

    int kk,j;
    double comp;
    double theta,phi,rad;

    if (iflag==1)
	{
	    sprintf(output_file,"%s.tracers.%d",E->control.data_file,
		    E->parallel.me);
	}
    if (iflag==2)
	{
	    sprintf(output_file,"%s.tracers.%d.%d",E->control.data_file,
		    E->parallel.me, E->monitor.solution_cycles);
	}
    if (iflag==3)
	{
	    sprintf(output_file,"%s.tracers.%d.preback",E->control.data_file,
		    E->parallel.me);
	}
    fpcomp=fopen(output_file,"w");

    /* This may be slightly incompatible with previous version */

    for(j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    //TODO: move
/* 	    fprintf(fpcomp,"%6d %6d %.5e %.5e %.5e %d\n",E->trace.itrac[j][0], */
/* 		    E->monitor.solution_cycles,E->monitor.elapsed_time, */
/* 		    E->trace.bulk_composition, */
/* 		    E->trace.initial_bulk_composition,j); */

	    comp=0.0;
	    for (kk=1;kk<=E->trace.itrac[j][0];kk++)
		{
		    theta=E->trace.rtrac[j][0][kk];
		    phi=E->trace.rtrac[j][1][kk];
		    rad=E->trace.rtrac[j][2][kk];

		    if (E->composition.ichemical_buoyancy==1)
			{
			    comp=E->trace.etrac[j][0][kk];
			    fprintf(fpcomp,"%.12e %.12e %.12e %.12e \n",theta,phi,rad,comp);
			    fflush(fpcomp);
			}
		    else if (E->composition.ichemical_buoyancy==0)
			{
			    fprintf(fpcomp,"%.12e %.12e %.12e \n",theta,phi,rad);
			    fflush(fpcomp);
			}
		}
	    fclose(fpcomp);

	    /* maybe some problem with zipping on the fly? */
	    /*
	      if (iflag==2)
	      {
	      sprintf(doit_string,"gzip %s",output_file);
	      system(doit_string);
	      }
	    */

	}

    return;
}


/*********** PREDICT TRACERS **********************************************/
/*                                                                        */
/* This function predicts tracers performing an euler step                */
/*                                                                        */
/*                                                                        */
/* Note positions used in tracer array                                    */
/* [positions 0-5 are always fixed with current coordinates               */
/*  regardless of advection scheme].                                      */
/*  Positions 6-8 contain original Cartesian coordinates.                 */
/*  Positions 9-11 contain original Cartesian velocities.                 */
/*                                                                        */


static void predict_tracers(struct All_variables *E)
{

    int numtracers;
    int j;
    int kk;
    int nelem;

    double dt;
    double theta0,phi0,rad0;
    double x0,y0,z0;
    double theta_pred,phi_pred,rad_pred;
    double x_pred,y_pred,z_pred;
    double velocity_vector[4];

    void get_velocity();
    void get_cartesian_velocity_field();
    void cart_to_sphere();
    void keep_in_sphere();
    void find_tracers();

    static int been_here=0;

    dt=E->advection.timestep;

    /* if advection scheme is 2, don't have to calculate cartesian velocity again */
    /* (already did after last stokes calculation, unless this is first step)     */

    if ((been_here==0) && (E->trace.itracer_advection_scheme==2))
	{
	    get_cartesian_velocity_field(E);
	    been_here++;
	}

    if (E->trace.itracer_advection_scheme==1) get_cartesian_velocity_field(E);


    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    numtracers=E->trace.itrac[j][0];

	    for (kk=1;kk<=numtracers;kk++)
		{

		    theta0=E->trace.rtrac[j][0][kk];
		    phi0=E->trace.rtrac[j][1][kk];
		    rad0=E->trace.rtrac[j][2][kk];
		    x0=E->trace.rtrac[j][3][kk];
		    y0=E->trace.rtrac[j][4][kk];
		    z0=E->trace.rtrac[j][5][kk];

		    nelem=E->trace.itrac[j][kk];
		    get_velocity(E,j,nelem,theta0,phi0,rad0,velocity_vector);

		    x_pred=x0+velocity_vector[1]*dt;
		    y_pred=y0+velocity_vector[2]*dt;
		    z_pred=z0+velocity_vector[3]*dt;


		    /* keep in box */

		    cart_to_sphere(E,x_pred,y_pred,z_pred,&theta_pred,&phi_pred,&rad_pred);
		    keep_in_sphere(E,&x_pred,&y_pred,&z_pred,&theta_pred,&phi_pred,&rad_pred);

		    /* Current Coordinates are always kept in positions 0-5. */

		    E->trace.rtrac[j][0][kk]=theta_pred;
		    E->trace.rtrac[j][1][kk]=phi_pred;
		    E->trace.rtrac[j][2][kk]=rad_pred;
		    E->trace.rtrac[j][3][kk]=x_pred;
		    E->trace.rtrac[j][4][kk]=y_pred;
		    E->trace.rtrac[j][5][kk]=z_pred;

		    /* Fill in original coords (positions 6-8) */

		    E->trace.rtrac[j][6][kk]=x0;
		    E->trace.rtrac[j][7][kk]=y0;
		    E->trace.rtrac[j][8][kk]=z0;

		    /* Fill in original velocities (positions 9-11) */

		    E->trace.rtrac[j][9][kk]=velocity_vector[1];  /* Vx */
		    E->trace.rtrac[j][10][kk]=velocity_vector[2];  /* Vy */
		    E->trace.rtrac[j][11][kk]=velocity_vector[3];  /* Vz */


		} /* end kk, predicting tracers */
	} /* end caps */

    /* find new tracer elements and caps */

    find_tracers(E);

    return;

}

/*********** CORRECT TRACERS **********************************************/
/*                                                                        */
/* This function corrects tracers using both initial and                  */
/* predicted velocities                                                   */
/*                                                                        */
/*                                                                        */
/* Note positions used in tracer array                                    */
/* [positions 0-5 are always fixed with current coordinates               */
/*  regardless of advection scheme].                                      */
/*  Positions 6-8 contain original Cartesian coordinates.                 */
/*  Positions 9-11 contain original Cartesian velocities.                 */
/*                                                                        */


static void correct_tracers(struct All_variables *E)
{

    int j;
    int kk;
    int nelem;


    double dt;
    double x0,y0,z0;
    double theta_pred,phi_pred,rad_pred;
    double x_pred,y_pred,z_pred;
    double theta_cor,phi_cor,rad_cor;
    double x_cor,y_cor,z_cor;
    double velocity_vector[4];
    double Vx0,Vy0,Vz0;
    double Vx_pred,Vy_pred,Vz_pred;

    void get_velocity();
    void get_cartesian_velocity_field();
    void cart_to_sphere();
    void keep_in_sphere();
    void find_tracers();


    dt=E->advection.timestep;

    if ((E->parallel.me==0) && (E->trace.itracer_advection_scheme==2) )
	{
	    fprintf(stderr,"AA:Correcting Tracers\n");
	    fflush(stderr);
	}


    /* If scheme==1, use same velocity (t=0)      */
    /* Else if scheme==2, use new velocity (t=dt) */

    if (E->trace.itracer_advection_scheme==2)
	{
	    get_cartesian_velocity_field(E);
	}


    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    for (kk=1;kk<=E->trace.itrac[j][0];kk++)
		{

		    theta_pred=E->trace.rtrac[j][0][kk];
		    phi_pred=E->trace.rtrac[j][1][kk];
		    rad_pred=E->trace.rtrac[j][2][kk];
		    x_pred=E->trace.rtrac[j][3][kk];
		    y_pred=E->trace.rtrac[j][4][kk];
		    z_pred=E->trace.rtrac[j][5][kk];

		    x0=E->trace.rtrac[j][6][kk];
		    y0=E->trace.rtrac[j][7][kk];
		    z0=E->trace.rtrac[j][8][kk];

		    Vx0=E->trace.rtrac[j][9][kk];
		    Vy0=E->trace.rtrac[j][10][kk];
		    Vz0=E->trace.rtrac[j][11][kk];

		    nelem=E->trace.itrac[j][kk];

		    get_velocity(E,j,nelem,theta_pred,phi_pred,rad_pred,velocity_vector);

		    Vx_pred=velocity_vector[1];
		    Vy_pred=velocity_vector[2];
		    Vz_pred=velocity_vector[3];

		    x_cor=x0 + dt * 0.5*(Vx0+Vx_pred);
		    y_cor=y0 + dt * 0.5*(Vy0+Vy_pred);
		    z_cor=z0 + dt * 0.5*(Vz0+Vz_pred);

		    cart_to_sphere(E,x_cor,y_cor,z_cor,&theta_cor,&phi_cor,&rad_cor);
		    keep_in_sphere(E,&x_cor,&y_cor,&z_cor,&theta_cor,&phi_cor,&rad_cor);

		    /* Fill in Current Positions (other positions are no longer important) */

		    E->trace.rtrac[j][0][kk]=theta_cor;
		    E->trace.rtrac[j][1][kk]=phi_cor;
		    E->trace.rtrac[j][2][kk]=rad_cor;
		    E->trace.rtrac[j][3][kk]=x_cor;
		    E->trace.rtrac[j][4][kk]=y_cor;
		    E->trace.rtrac[j][5][kk]=z_cor;

		} /* end kk, correcting tracers */
	} /* end caps */
    /* find new tracer elements and caps */

    find_tracers(E);

    return;
}

/******** GET VELOCITY ***************************************/

void get_velocity(E,j,nelem,theta,phi,rad,velocity_vector)
     struct All_variables *E;
     int j,nelem;
     double theta,phi,rad;
     double *velocity_vector;
{

    void gnomonic_interpolation();

    /* gnomonic projection */

    if (E->trace.itracer_interpolation_scheme==1)
	{
	    gnomonic_interpolation(E,j,nelem,theta,phi,rad,velocity_vector);
	}
    else if (E->trace.itracer_interpolation_scheme==2)
	{
	    fprintf(E->trace.fpt,"Error(get velocity)-not ready for simple average interpolation scheme\n");
	    fflush(E->trace.fpt);
	    exit(10);
	}
    else
	{
	    fprintf(E->trace.fpt,"Error(get velocity)-not ready for other interpolation schemes\n");
	    fflush(E->trace.fpt);
	    exit(10);
	}

    return;
}

/********************** GNOMONIC INTERPOLATION *********************************/
/*                                                                             */
/* This function interpolates tracer velocity using gnominic interpolation.    */
/* Real theta,phi,rad space is transformed into u,v space. This transformation */
/* maps great circles into straight lines. Here, elements boundaries are       */
/* assumed to be great circle planes (not entirely true, it is actually only   */
/* the nodal arrangement that lies upon great circles).  Element boundaries    */
/* are then mapped into planes.  The element is then divided into 2 wedges     */
/* in which standard shape functions are used to interpolate velocity.         */
/* This transformation was found on the internet (refs were difficult to       */
/* to obtain). It was tested that nodal configuration is indeed transformed    */
/* into straight lines.                                                        */
/* The transformations require a reference point along each cap. Here, the     */
/* midpoint is used.                                                           */
/* Radial and azimuthal shape functions are decoupled. First find the shape    */
/* functions associated with the 2D surface plane, then apply radial shape     */
/* functions.                                                                  */
/*                                                                             */
/* Wedge information:                                                          */
/*                                                                             */
/*        Wedge 1                  Wedge 2                                     */
/*        _______                  _______                                     */
/*                                                                             */
/*    wedge_node  real_node      wedge_node  real_node                         */
/*    ----------  ---------      ----------  ---------                         */
/*                                                                             */
/*         1        1               1            1                             */
/*         2        2               2            3                             */
/*         3        3               3            4                             */
/*         4        5               4            5                             */
/*         5        6               5            7                             */
/*         6        7               6            8                             */


void gnomonic_interpolation(E,j,nelem,theta,phi,rad,velocity_vector)
     struct All_variables *E;
     int j,nelem;
     double theta,phi,rad;
     double *velocity_vector;
{

    int iwedge,inum;
    int kk;
    int ival;
    int itry;

    double u,v;
    double shape2d[4];
    double shaperad[3];
    double shape[7];
    double vx[7],vy[7],vz[7];
    double x,y,z;


    int maxlevel=E->mesh.levmax;

    const double eps=-1e-4;

    void get_radial_shape();
    void sphere_to_cart();
    void spherical_to_uv();
    void get_2dshape();
    int iget_element();
    int icheck_element();
    int icheck_column_neighbors();


    /* find u and v using spherical coordinates */

    spherical_to_uv(E,j,theta,phi,&u,&v);

    inum=0;
    itry=1;

 try_again:

    /* Check first wedge (1 of 2) */

    iwedge=1;

 next_wedge:

    /* determine shape functions of wedge */
    /* There are 3 shape functions for the triangular wedge */

    get_2dshape(E,j,nelem,u,v,iwedge,shape2d);

    /* if any shape functions are negative, goto next wedge */

    if (shape2d[1]<eps||shape2d[2]<eps||shape2d[3]<eps)
	{
	    inum=inum+1;
	    /* AKMA clean this up */
	    if (inum>3)
		{
		    fprintf(E->trace.fpt,"ERROR(gnomonic_interpolation)-inum>3!\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}
	    if (inum>1 && itry==1)
		{
		    fprintf(E->trace.fpt,"ERROR(gnomonic_interpolation)-inum>1\n");
		    fprintf(E->trace.fpt,"shape %f %f %f\n",shape2d[1],shape2d[2],shape2d[3]);
		    fprintf(E->trace.fpt,"u %f v %f element: %d \n",u,v, nelem);
		    fprintf(E->trace.fpt,"Element uv boundaries: \n");
		    for(kk=1;kk<=4;kk++)
			fprintf(E->trace.fpt,"%d: U: %f V:%f\n",kk,E->trace.UV[j][1][E->ien[j][nelem].node[kk]],E->trace.UV[j][2][E->ien[j][nelem].node[kk]]);
		    fprintf(E->trace.fpt,"theta: %f phi: %f rad: %f\n",theta,phi,rad);
		    fprintf(E->trace.fpt,"Element theta-phi boundaries: \n");
		    for(kk=1;kk<=4;kk++)
			fprintf(E->trace.fpt,"%d: Theta: %f Phi:%f\n",kk,E->sx[j][1][E->ien[j][nelem].node[kk]],E->sx[j][2][E->ien[j][nelem].node[kk]]);
		    sphere_to_cart(E,theta,phi,rad,&x,&y,&z);
		    ival=icheck_element(E,j,nelem,x,y,z,rad);
		    fprintf(E->trace.fpt,"ICHECK?: %d\n",ival);
		    ival=iget_element(E,j,-99,x,y,z,theta,phi,rad);
		    fprintf(E->trace.fpt,"New Element?: %d\n",ival);
		    ival=icheck_column_neighbors(E,j,nelem,x,y,z,rad);
		    fprintf(E->trace.fpt,"New Element (neighs)?: %d\n",ival);
		    nelem=ival;
		    ival=icheck_element(E,j,nelem,x,y,z,rad);
		    fprintf(E->trace.fpt,"ICHECK?: %d\n",ival);
		    itry++;
		    if (ival>0) goto try_again;
		    fprintf(E->trace.fpt,"NO LUCK\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}

	    iwedge=2;
	    goto next_wedge;
	}

    /* Determine radial shape functions */
    /* There are 2 shape functions radially */

    get_radial_shape(E,j,nelem,rad,shaperad);

    /* There are 6 nodes to the solid wedge.             */
    /* The 6 shape functions assocated with the 6 nodes  */
    /* are products of radial and wedge shape functions. */

    /* Sum of shape functions is 1                       */

    shape[1]=shaperad[1]*shape2d[1];
    shape[2]=shaperad[1]*shape2d[2];
    shape[3]=shaperad[1]*shape2d[3];
    shape[4]=shaperad[2]*shape2d[1];
    shape[5]=shaperad[2]*shape2d[2];
    shape[6]=shaperad[2]*shape2d[3];

    /* depending on wedge, set up velocity points */

    if (iwedge==1)
	{
	    vx[1]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[1]];
	    vx[1]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[1]];
	    vx[2]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[2]];
	    vx[3]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[3]];
	    vx[4]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[5]];
	    vx[5]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[6]];
	    vx[6]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[7]];
	    vy[1]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[1]];
	    vy[2]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[2]];
	    vy[3]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[3]];
	    vy[4]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[5]];
	    vy[5]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[6]];
	    vy[6]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[7]];
	    vz[1]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[1]];
	    vz[2]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[2]];
	    vz[3]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[3]];
	    vz[4]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[5]];
	    vz[5]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[6]];
	    vz[6]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[7]];
	}
    if (iwedge==2)
	{
	    vx[1]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[1]];
	    vx[2]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[3]];
	    vx[3]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[4]];
	    vx[4]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[5]];
	    vx[5]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[7]];
	    vx[6]=E->trace.V0_cart[j][1][E->IEN[maxlevel][j][nelem].node[8]];
	    vy[1]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[1]];
	    vy[2]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[3]];
	    vy[3]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[4]];
	    vy[4]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[5]];
	    vy[5]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[7]];
	    vy[6]=E->trace.V0_cart[j][2][E->IEN[maxlevel][j][nelem].node[8]];
	    vz[1]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[1]];
	    vz[2]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[3]];
	    vz[3]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[4]];
	    vz[4]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[5]];
	    vz[5]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[7]];
	    vz[6]=E->trace.V0_cart[j][3][E->IEN[maxlevel][j][nelem].node[8]];
	}

    velocity_vector[1]=vx[1]*shape[1]+vx[2]*shape[2]+shape[3]*vx[3]+
	vx[4]*shape[4]+vx[5]*shape[5]+shape[6]*vx[6];
    velocity_vector[2]=vy[1]*shape[1]+vy[2]*shape[2]+shape[3]*vy[3]+
	vy[4]*shape[4]+vy[5]*shape[5]+shape[6]*vy[6];
    velocity_vector[3]=vz[1]*shape[1]+vz[2]*shape[2]+shape[3]*vz[3]+
	vz[4]*shape[4]+vz[5]*shape[5]+shape[6]*vz[6];



    return;
}

/***************************************************************/
/* GET 2DSHAPE                                                 */
/*                                                             */
/* This function determines shape functions at u,v             */
/* This method uses standard linear shape functions of         */
/* triangular elements. (See Cuvelier, Segal, and              */
/* van Steenhoven, 1986).                                      */


void get_2dshape(E,j,nelem,u,v,iwedge,shape2d)
     struct All_variables *E;
     int j,nelem,iwedge;
     double u,v;
     double * shape2d;

{

    double a0,a1,a2;

    /* shape function 1 */

    a0=E->trace.shape_coefs[j][iwedge][1][nelem];
    a1=E->trace.shape_coefs[j][iwedge][2][nelem];
    a2=E->trace.shape_coefs[j][iwedge][3][nelem];

    shape2d[1]=a0+a1*u+a2*v;

    /* shape function 2 */

    a0=E->trace.shape_coefs[j][iwedge][4][nelem];
    a1=E->trace.shape_coefs[j][iwedge][5][nelem];
    a2=E->trace.shape_coefs[j][iwedge][6][nelem];

    shape2d[2]=a0+a1*u+a2*v;

    /* shape function 3 */

    a0=E->trace.shape_coefs[j][iwedge][7][nelem];
    a1=E->trace.shape_coefs[j][iwedge][8][nelem];
    a2=E->trace.shape_coefs[j][iwedge][9][nelem];

    shape2d[3]=a0+a1*u+a2*v;

    return;
}

/***************************************************************/
/* GET RADIAL SHAPE                                            */
/*                                                             */
/* This function determines radial shape functions at rad      */

void get_radial_shape(E,j,nelem,rad,shaperad)
     struct All_variables *E;
     int j,nelem;
     double rad;
     double * shaperad;

{

    int node1,node5;
    double rad1,rad5,f1,f2,delrad;

    const double eps=1e-6;
    double top_bound=1.0+eps;
    double bottom_bound=0.0-eps;

    node1=E->IEN[E->mesh.levmax][j][nelem].node[1];
    node5=E->IEN[E->mesh.levmax][j][nelem].node[5];

    rad1=E->sx[j][3][node1];
    rad5=E->sx[j][3][node5];

    delrad=rad5-rad1;

    f1=(rad-rad1)/delrad;
    f2=(rad5-rad)/delrad;

    /* Save a small amount of computation here   */
    /* because f1+f2=1, shapes can be switched   */
    /*
      shaperad[1]=1.0-f1=1.0-(1.0-f2)=f2;
      shaperad[2]=1.0-f2=1.0-(10-f1)=f1;
    */

    shaperad[1]=f2;
    shaperad[2]=f1;

    /* Some error control */

    if (shaperad[1]>(top_bound)||shaperad[1]<(bottom_bound)||
	shaperad[2]>(top_bound)||shaperad[2]<(bottom_bound))
	{
	    fprintf(E->trace.fpt,"ERROR(get_radial_shape)\n");
	    fprintf(E->trace.fpt,"shaperad[1]: %f \n",shaperad[1]);
	    fprintf(E->trace.fpt,"shaperad[2]: %f \n",shaperad[2]);
	    fflush(E->trace.fpt);
	    exit(10);
	}

    return;
}





/**************************************************************/
/* SPHERICAL TO UV                                               */
/*                                                            */
/* This function transforms theta and phi to new coords       */
/* u and v using gnomonic projection.                          */

void spherical_to_uv(E,j,theta,phi,u,v)
     struct All_variables *E;
     int j;
     double theta,phi;
     double *u;
     double *v;

{

    double theta_f;
    double phi_f;
    double cosc;
    double cos_theta_f,sin_theta_f;
    double cost,sint,cosp2,sinp2;

    /* theta_f and phi_f are the reference points at the midpoint of the cap */

    theta_f=E->trace.UV[j][1][0];
    phi_f=E->trace.UV[j][2][0];

    cos_theta_f=E->trace.cos_theta_f;
    sin_theta_f=E->trace.sin_theta_f;

    cost=cos(theta);
    /*
      sint=sin(theta);
    */
    sint=sqrt(1.0-cost*cost);

    cosp2=cos(phi-phi_f);
    sinp2=sin(phi-phi_f);

    cosc=cos_theta_f*cost+sin_theta_f*sint*cosp2;
    cosc=1.0/cosc;

    *u=sint*sinp2*cosc;
    *v=(sin_theta_f*cost-cos_theta_f*sint*cosp2)*cosc;

    return;
}


/**************** INITIALIZE TRACER ARRAYS ************************************/
/*                                                                            */
/* This function allocates memories to tracer arrays.                         */

void initialize_tracer_arrays(E,j,number_of_tracers)
     struct All_variables *E;
     int j, number_of_tracers;
{

    int kk;

    /* itracsize is physical size of tracer array */
    /* (initially make it 25% larger than required */

    E->trace.itracsize[j]=number_of_tracers+number_of_tracers/4;

    /* make tracer arrays */

    if ((E->trace.itrac[j]=(int *) malloc(E->trace.itracsize[j]*sizeof(int)))==NULL)
	{
	    fprintf(E->trace.fpt,"ERROR(make tracer array)-no memory 1a\n");
	    fflush(E->trace.fpt);
	    exit(10);
	}
    E->trace.itrac[j][0]=0;

    for (kk=0;kk<=((E->trace.number_of_advection_quantities)-1);kk++)
	{
	    if ((E->trace.rtrac[j][kk]=(double *)malloc(E->trace.itracsize[j]*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(initialize tracer arrays)-no memory 1b.%d\n",kk);
		    fflush(E->trace.fpt);
		    exit(10);
		}
	}

    for (kk=0;kk<=((E->trace.number_of_extra_quantities)-1);kk++)
	{
	    if ((E->trace.etrac[j][kk]=(double *)malloc(E->trace.itracsize[j]*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(initialize tracer arrays)-no memory 1c.%d\n",kk);
		    fflush(E->trace.fpt);
		    exit(10);
		}
	}

    fprintf(E->trace.fpt,"Physical size of tracer arrays (itracsize): %d\n",
	    E->trace.itracsize[j]);
    fflush(E->trace.fpt);

    return;
}



/************ FIND TRACERS *************************************/
/*                                                             */
/* This function finds tracer elements and moves tracers to    */
/* other processor domains if necessary.                       */
/* Array itrac is filled with elemental values.                */

void find_tracers(E)
     struct All_variables *E;

{

    int iel;
    int kk;
    int j;
    int it;
    int iprevious_element;
    int num_tracers;

    double theta,phi,rad;
    double x,y,z;
    double time_stat1;
    double time_stat2;

    int iget_element();
    void put_away_later();
    void eject_tracer();
    void reduce_tracer_arrays();
    void lost_souls();
    void sphere_to_cart();

    static int been_here=0;

    time_stat1=CPU_time0();


    if (been_here==0)
	{
	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (kk=1;kk<=E->trace.itrac[j][0];kk++)
			{
			    E->trace.itrac[j][kk]=-99;
			}
		}
	    been_here++;
	}



    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{


	    /* initialize arrays and statistical counters */

	    E->trace.ilater[j]=0;

	    E->trace.istat1=0;
	    for (kk=0;kk<=4;kk++)
		{
		    E->trace.istat_ichoice[j][kk]=0;
		}

	    //TODO: use while-loop instead of for-loop
	    /* important to index by it, not kk */

	    it=0;
	    num_tracers=E->trace.itrac[j][0];

	    for (kk=1;kk<=num_tracers;kk++)
		{

		    it++;

		    theta=E->trace.rtrac[j][0][it];
		    phi=E->trace.rtrac[j][1][it];
		    rad=E->trace.rtrac[j][2][it];
		    x=E->trace.rtrac[j][3][it];
		    y=E->trace.rtrac[j][4][it];
		    z=E->trace.rtrac[j][5][it];

		    iprevious_element=E->trace.itrac[j][it];

		    /* AKMA REMOVE */
		    /*
		      fprintf(E->trace.fpt,"BB. kk %d %d %d %f %f %f %f %f %f\n",kk,j,iprevious_element,x,y,z,theta,phi,rad);
		      fflush(E->trace.fpt);
		    */
		    iel=iget_element(E,j,iprevious_element,x,y,z,theta,phi,rad);

		    E->trace.itrac[j][it]=iel;

		    if (iel<0)
			{
			    put_away_later(E,j,it);
			    eject_tracer(E,j,it);
			    it--;
			}

		} /* end tracers */

	} /* end j */


    /* fprintf(E->trace.fpt,"tracer# old:%d new:%d\n", */
    /*         num_tracers, E->trace.itrac[1][0]); */

    /* Now take care of tracers that exited cap */

    /* REMOVE */
    /*
      parallel_process_termination();
    */

    lost_souls(E);

    /* Free later arrays */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    if (E->trace.ilater[j]>0)
		{
		    for (kk=0;kk<=((E->trace.number_of_tracer_quantities)-1);kk++)
			{
			    free(E->trace.rlater[j][kk]);
			}
		}
	} /* end j */


    /* Adjust Array Sizes */

    reduce_tracer_arrays(E);

    time_stat2=CPU_time0();

    fprintf(E->trace.fpt,"AA: time for find tracers: %f\n", time_stat2-time_stat1);

    return;
}

/************** LOST SOULS ****************************************************/
/*                                                                            */
/* This function is used to transport tracers to proper processor domains.    */
/* (MPI parallel)                                                             */
/*  All of the tracers that were sent to rlater arrays are destined to another */
/*  cap and sent there. Then they are raised up or down for multiple z procs.  */
/*  isend[j][n]=number of tracers this processor cap is sending to cap n        */
/*  ireceive[j][n]=number of tracers this processor cap is receiving from cap n */


void lost_souls(E)
     struct All_variables *E;
{
    int ithiscap;
    int ithatcap=1;
    int isend[13][13];
    int ireceive[13][13];
    int isize[13];
    int kk,pp,j;
    int mm;
    int numtracers;
    int icheck=0;
    int isend_position;
    int ipos,ipos2,ipos3;
    int idb;
    int idestination_proc=0;
    int isource_proc;
    int isend_z[13][3];
    int ireceive_z[13][3];
    int isum[13];
    int irad;
    int ival;
    int ithat_processor;
    int ireceive_position;
    int ihorizontal_neighbor;
    int ivertical_neighbor;
    int ilast_receiver_position;
    int it;
    int irec[13];
    int irec_position;
    int iel;
    int num_tracers;
    int isize_send;
    int isize_receive;
    int itemp_size;
    int itracers_subject_to_vertical_transport[13];

    double x,y,z;
    double theta,phi,rad;
    double *send[13][13];
    double *receive[13][13];
    double *send_z[13][3];
    double *receive_z[13][3];
    double *REC[13];

    int iget_element();
    int icheck_cap();
    void expand_tracer_arrays();

    int number_of_caps=12;
    int lev=E->mesh.levmax;
    int num_ngb;

    /* Note, if for some reason, the number of neighbors exceeds */
    /* 50, which is unlikely, the MPI arrays must be increased.  */
    MPI_Status status[200];
    MPI_Request request[200];
    MPI_Status status1;
    MPI_Status status2;
    static int itag=1;


    parallel_process_sync(E);
    fprintf(E->trace.fpt, "Entering lost_souls()\n");


    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    E->trace.istat_isend=E->trace.ilater[j];
	}

    for (j=1;j<=E->sphere.caps_per_proc;j++) {
	for (kk=1; kk<=E->trace.istat_isend; kk++) {
	    fprintf(E->trace.fpt, "tracer#=%d xx=(%g,%g,%g)\n", kk,
		    E->trace.rlater[j][0][kk],
		    E->trace.rlater[j][1][kk],
		    E->trace.rlater[j][2][kk]);
	}
	fflush(E->trace.fpt);
    }

    /* initialize isend and ireceive */
    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
            /* # of neighbors in the horizontal plane */
            num_ngb = E->parallel.TNUM_PASS[lev][j];
	    isize[j]=E->trace.ilater[j]*E->trace.number_of_tracer_quantities;
	    for (kk=1;kk<=num_ngb;kk++) isend[j][kk]=0;
	    for (kk=1;kk<=num_ngb;kk++) ireceive[j][kk]=0;
	}

    /* Allocate Maximum Memory to Send Arrays */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    ithiscap=0;

	    itemp_size=max(isize[j],1);

	    if ((send[j][ithiscap]=(double *)malloc(itemp_size*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"Error(lost souls)-no memory (u389)\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}

            num_ngb = E->parallel.TNUM_PASS[lev][j];
	    for (kk=1;kk<=num_ngb;kk++)
                {
                    if ((send[j][kk]=(double *)malloc(itemp_size*sizeof(double)))==NULL)
                        {
                            fprintf(E->trace.fpt,"Error(lost souls)-no memory (u389)\n");
                            fflush(E->trace.fpt);
                            exit(10);
                        }
                }
	}


    /* For testing, remove this */
    /*
      for (j=1;j<=E->sphere.caps_per_proc;j++)
      {
      ithiscap=E->sphere.capid[j];
      for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
      {
	  ithatcap=E->parallel.PROCESSOR[lev][j].pass[kk]+1;
	  fprintf(E->trace.fpt,"cap: %d proc %d TNUM: %d ithatcap: %d\n",
		  ithiscap,E->parallel.me,kk,ithatcap);

      }
      fflush(E->trace.fpt);
      }
    */


    /* Pre communication */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    /* transfer tracers from rlater to send */

	    numtracers=E->trace.ilater[j];

	    for (kk=1;kk<=numtracers;kk++)
		{
		    rad=E->trace.rlater[j][2][kk];
		    x=E->trace.rlater[j][3][kk];
		    y=E->trace.rlater[j][4][kk];
		    z=E->trace.rlater[j][5][kk];

		    /* first check same cap if nprocz>1 */

		    if (E->parallel.nprocz>1)
			{
			    ithatcap=0;
			    icheck=icheck_cap(E,ithatcap,x,y,z,rad);
			    if (icheck==1) goto foundit;

			}

		    /* check neighboring caps */

		    for (pp=1;pp<=E->parallel.TNUM_PASS[lev][j];pp++)
			{
			    ithatcap=pp;
			    icheck=icheck_cap(E,ithatcap,x,y,z,rad);
			    if (icheck==1) goto foundit;
			}


		    /* should not be here */
		    if (icheck!=1)
			{
			    fprintf(E->trace.fpt,"Error(lost souls)-should not be here\n");
			    fprintf(E->trace.fpt,"x: %f y: %f z: %f rad: %f\n",x,y,z,rad);
			    icheck=icheck_cap(E,0,x,y,z,rad);
			    if (icheck==1) fprintf(E->trace.fpt," icheck here!\n");
			    else fprintf(E->trace.fpt,"icheck not here!\n");
			    fflush(E->trace.fpt);
			    exit(10);
			}

		foundit:

		    isend[j][ithatcap]++;

		    /* assign tracer to send */

		    isend_position=(isend[j][ithatcap]-1)*E->trace.number_of_tracer_quantities;

		    for (pp=0;pp<=(E->trace.number_of_tracer_quantities-1);pp++)
			{
			    ipos=isend_position+pp;
			    send[j][ithatcap][ipos]=E->trace.rlater[j][pp][kk];
			}

		} /* end kk, assigning tracers */

	} /* end j */


    /* Send info to other processors regarding number of send tracers */

    /* idb is the request array index variable */
    /* Each send and receive has a request variable */

    idb=0;
    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    ithiscap=0;

	    /* if tracer is in same cap (nprocz>1) */

	    if (E->parallel.nprocz>1)
		{
		    ireceive[j][ithiscap]=isend[j][ithiscap];
		}

	    for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
		{
		    ithatcap=kk;

		    /* if neighbor cap is in another processor, send information via MPI */

		    idestination_proc=E->parallel.PROCESSOR[lev][j].pass[kk];

                    idb++;
                    MPI_Isend(&isend[j][ithatcap],1,MPI_INT,idestination_proc,
                              11,E->parallel.world,
                              &request[idb-1]);

		} /* end kk, number of neighbors */

	} /* end j, caps per proc */

    /* Receive tracer count info */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
		{
		    ithatcap=kk;

		    /* if neighbor cap is in another processor, receive information via MPI */

		    isource_proc=E->parallel.PROCESSOR[lev][j].pass[kk];

		    if (idestination_proc!=E->parallel.me)
			{

			    idb++;
			    MPI_Irecv(&ireceive[j][ithatcap],1,MPI_INT,isource_proc,
				      11,E->parallel.world,
				      &request[idb-1]);

			} /* end if */

		} /* end kk, number of neighbors */
	} /* end j, caps per proc */

    /* Wait for non-blocking calls to complete */

    MPI_Waitall(idb,request,status);

    /* Testing, should remove */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
      {
      for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
      {
          isource_proc=E->parallel.PROCESSOR[lev][j].pass[kk];
          fprintf(E->trace.fpt,"j: %d send %d to cap %d\n",j,isend[j][kk],isource_proc);
          fprintf(E->trace.fpt,"j: %d rec  %d from cap %d\n",j,ireceive[j][kk],isource_proc);
      }
      }


    /* Allocate memory in receive arrays */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
            num_ngb = E->parallel.TNUM_PASS[lev][j];
	    for (ithatcap=1;ithatcap<=num_ngb;ithatcap++)
		{
		    isize[j]=ireceive[j][ithatcap]*E->trace.number_of_tracer_quantities;

		    itemp_size=max(1,isize[j]);

		    if ((receive[j][ithatcap]=(double *)malloc(itemp_size*sizeof(double)))==NULL)
			{
			    fprintf(E->trace.fpt,"Error(lost souls)-no memory (c721)\n");
			    fflush(E->trace.fpt);
			    exit(10);
			}
		}
	} /* end j */

    /* Now, send the tracers to proper caps */

    idb=0;
    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    ithiscap=0;

	    /* same cap */

	    if (E->parallel.nprocz>1)
		{

		    ithatcap=ithiscap;
		    isize[j]=isend[j][ithatcap]*E->trace.number_of_tracer_quantities;
		    for (mm=0;mm<=(isize[j]-1);mm++)
			{
			    receive[j][ithatcap][mm]=send[j][ithatcap][mm];
			}

		}

	    /* neighbor caps */

	    for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
		{
		    ithatcap=kk;

		    idestination_proc=E->parallel.PROCESSOR[lev][j].pass[kk];

		    isize[j]=isend[j][ithatcap]*E->trace.number_of_tracer_quantities;

                    idb++;

                    MPI_Isend(send[j][ithatcap],isize[j],MPI_DOUBLE,idestination_proc,
                              11,E->parallel.world,
                              &request[idb-1]);

		} /* end kk, number of neighbors */

	} /* end j, caps per proc */


    /* Receive tracers */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    ithiscap=0;
	    for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
		{
		    ithatcap=kk;

		    isource_proc=E->parallel.PROCESSOR[lev][j].pass[kk];

                    idb++;

                    isize[j]=ireceive[j][ithatcap]*E->trace.number_of_tracer_quantities;

                    MPI_Irecv(receive[j][ithatcap],isize[j],MPI_DOUBLE,isource_proc,
                              11,E->parallel.world,
                              &request[idb-1]);

		} /* end kk, number of neighbors */

	} /* end j, caps per proc */

    /* Wait for non-blocking calls to complete */

    MPI_Waitall(idb,request,status);


    /* Put all received tracers in array REC[j] */
    /* This makes things more convenient.       */

    /* Sum up size of receive arrays (all tracers sent to this processor) */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    isum[j]=0;

	    ithiscap=0;

	    for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
		{
		    ithatcap=kk;
		    isum[j]=isum[j]+ireceive[j][ithatcap];
		}
	    if (E->parallel.nprocz>1) isum[j]=isum[j]+ireceive[j][ithiscap];

	    itracers_subject_to_vertical_transport[j]=isum[j];
	}

    /* Allocate Memory for REC array */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    isize[j]=isum[j]*E->trace.number_of_tracer_quantities;
	    isize[j]=max(isize[j],1);
	    if ((REC[j]=(double *)malloc(isize[j]*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"Error(lost souls)-no memory (g323)\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}
	    REC[j][0]=0.0;
	}

    /* Put Received tracers in REC */


    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    irec[j]=0;

	    irec_position=0;

	    ithiscap=0;

	    /* horizontal neighbors */

	    for (ihorizontal_neighbor=1;ihorizontal_neighbor<=E->parallel.TNUM_PASS[lev][j];ihorizontal_neighbor++)
		{

		    ithatcap=ihorizontal_neighbor;

		    for (pp=1;pp<=ireceive[j][ithatcap];pp++)
			{
			    irec[j]++;
			    ipos=(pp-1)*E->trace.number_of_tracer_quantities;

			    for (mm=0;mm<=(E->trace.number_of_tracer_quantities-1);mm++)
				{
				    ipos2=ipos+mm;
				    REC[j][irec_position]=receive[j][ithatcap][ipos2];

				    irec_position++;

				} /* end mm (cycling tracer quantities) */
			} /* end pp (cycling tracers) */
		} /* end ihorizontal_neighbors (cycling neighbors) */

	    /* for tracers in the same cap (nprocz>1) */

	    if (E->parallel.nprocz>1)
		{
		    ithatcap=ithiscap;
		    for (pp=1;pp<=ireceive[j][ithatcap];pp++)
			{
			    irec[j]++;
			    ipos=(pp-1)*E->trace.number_of_tracer_quantities;

			    for (mm=0;mm<=(E->trace.number_of_tracer_quantities-1);mm++)
				{
				    ipos2=ipos+mm;

				    REC[j][irec_position]=receive[j][ithatcap][ipos2];

				    irec_position++;

				} /* end mm (cycling tracer quantities) */

			} /* end pp (cycling tracers) */

		} /* endif nproc>1 */

	} /* end j (cycling caps) */

    /* Done filling REC */



    /* VERTICAL COMMUNICATION */

    /* Note: For generality, I assume that both multiple */
    /* caps per processor as well as multiple processors */
    /* in the radial direction. These are probably       */
    /* inconsistent parameter choices for the regular    */
    /* CitcomS code.                                     */

    if (E->parallel.nprocz>1)
	{

	    /* Allocate memory for send_z */
	    /* Make send_z the size of receive array (max size) */
	    /* (No dynamic reallocation of send_z necessary)    */

	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (kk=1;kk<=E->parallel.TNUM_PASSz[lev];kk++)
			{
			    isize[j]=itracers_subject_to_vertical_transport[j]*E->trace.number_of_tracer_quantities;
			    isize[j]=max(isize[j],1);

			    if ((send_z[j][kk]=(double *)malloc(isize[j]*sizeof(double)))==NULL)
				{
				    fprintf(E->trace.fpt,"Error(lost souls)-no memory (c721)\n");
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}
		} /* end j */


	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{

		    for (ivertical_neighbor=1;ivertical_neighbor<=E->parallel.TNUM_PASSz[lev];ivertical_neighbor++)
			{

			    ithat_processor=E->parallel.PROCESSORz[lev].pass[ivertical_neighbor];

			    /* initialize isend_z and ireceive_z array */

			    isend_z[j][ivertical_neighbor]=0;
			    ireceive_z[j][ivertical_neighbor]=0;

			    /* sort through receive array and check radius */

			    it=0;
			    num_tracers=irec[j];
			    for (kk=1;kk<=num_tracers;kk++)
				{

				    it++;

				    ireceive_position=((it-1)*E->trace.number_of_tracer_quantities);

				    irad=ireceive_position+2;

				    rad=REC[j][irad];

				    ival=icheck_that_processor_shell(E,j,ithat_processor,rad);


				    /* if tracer is in other shell, take out of receive array and give to send_z*/

				    if (ival==1)
					{

					    isend_z[j][ivertical_neighbor]++;

					    isend_position=(isend_z[j][ivertical_neighbor]-1)*E->trace.number_of_tracer_quantities;

					    ilast_receiver_position=(irec[j]-1)*E->trace.number_of_tracer_quantities;

					    for (mm=0;mm<=(E->trace.number_of_tracer_quantities-1);mm++)
						{
						    ipos=ireceive_position+mm;
						    ipos2=isend_position+mm;

						    send_z[j][ivertical_neighbor][ipos2]=REC[j][ipos];


						    /* eject tracer info from REC array, and replace with last tracer in array */

						    ipos3=ilast_receiver_position+mm;
						    REC[j][ipos]=REC[j][ipos3];

						}

					    it--;
					    irec[j]--;

					} /* end if ival===1 */

				    /* Otherwise, leave tracer */

				} /* end kk (cycling through tracers) */

			} /* end ivertical_neighbor */

		} /* end j */


	    /* Send arrays are now filled.                         */
	    /* Now send send information to vertical processor neighbor */

	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (ivertical_neighbor=1;ivertical_neighbor<=E->parallel.TNUM_PASSz[lev];ivertical_neighbor++)
			{

			    MPI_Sendrecv(&isend_z[j][ivertical_neighbor],1,MPI_INT,
					 E->parallel.PROCESSORz[lev].pass[ivertical_neighbor],itag,
					 &ireceive_z[j][ivertical_neighbor],1,MPI_INT,
					 E->parallel.PROCESSORz[lev].pass[ivertical_neighbor],
					 itag,E->parallel.world,&status1);

			    /* for testing - remove */
			    /*
			      fprintf(E->trace.fpt,"PROC: %d IVN: %d (P: %d) SEND: %d REC: %d\n",
			      E->parallel.me,ivertical_neighbor,E->parallel.PROCESSORz[lev].pass[ivertical_neighbor],
			      isend_z[j][ivertical_neighbor],ireceive_z[j][ivertical_neighbor]);
			      fflush(E->trace.fpt);
			    */

			} /* end ivertical_neighbor */

		} /* end j */


	    /* Allocate memory to receive_z arrays */


	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (kk=1;kk<=E->parallel.TNUM_PASSz[lev];kk++)
			{
			    isize[j]=ireceive_z[j][kk]*E->trace.number_of_tracer_quantities;
			    isize[j]=max(isize[j],1);

			    if ((receive_z[j][kk]=(double *)malloc(isize[j]*sizeof(double)))==NULL)
				{
				    fprintf(E->trace.fpt,"Error(lost souls)-no memory (t590)\n");
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}
		} /* end j */

	    /* Send Tracers */

	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (ivertical_neighbor=1;ivertical_neighbor<=E->parallel.TNUM_PASSz[lev];ivertical_neighbor++)
			{
			    isize_send=isend_z[j][ivertical_neighbor]*E->trace.number_of_tracer_quantities;
			    isize_receive=ireceive_z[j][ivertical_neighbor]*E->trace.number_of_tracer_quantities;

			    MPI_Sendrecv(send_z[j][ivertical_neighbor],isize_send,
					 MPI_DOUBLE,
					 E->parallel.PROCESSORz[lev].pass[ivertical_neighbor],itag+1,
					 receive_z[j][ivertical_neighbor],isize_receive,
					 MPI_DOUBLE,
					 E->parallel.PROCESSORz[lev].pass[ivertical_neighbor],
					 itag+1,E->parallel.world,&status2);

			}
		}

	    /* Put tracers into REC array */

	    /* First, reallocate memory to REC */

	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    isum[j]=0;
		    for (ivertical_neighbor=1;ivertical_neighbor<=E->parallel.TNUM_PASSz[lev];ivertical_neighbor++)
			{
			    isum[j]=isum[j]+ireceive_z[j][ivertical_neighbor];
			}

		    isum[j]=isum[j]+irec[j];

		    isize[j]=isum[j]*E->trace.number_of_tracer_quantities;

		    if (isize[j]>0)
			{
			    if ((REC[j]=(double *)realloc(REC[j],isize[j]*sizeof(double)))==NULL)
				{
				    fprintf(E->trace.fpt,"Error(lost souls)-no memory (i981)\n");
				    fprintf(E->trace.fpt,"isize: %d\n",isize[j]);
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}
		}


	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (ivertical_neighbor=1;ivertical_neighbor<=E->parallel.TNUM_PASSz[lev];ivertical_neighbor++)
			{

			    for (kk=1;kk<=ireceive_z[j][ivertical_neighbor];kk++)
				{
				    irec[j]++;

				    irec_position=(irec[j]-1)*E->trace.number_of_tracer_quantities;
				    ireceive_position=(kk-1)*E->trace.number_of_tracer_quantities;

				    for (mm=0;mm<=(E->trace.number_of_tracer_quantities-1);mm++)
					{
					    REC[j][irec_position+mm]=receive_z[j][ivertical_neighbor][ireceive_position+mm];
					}
				}

			}
		}

	    /* Free Vertical Arrays */

	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (ivertical_neighbor=1;ivertical_neighbor<=E->parallel.TNUM_PASSz[lev];ivertical_neighbor++)
			{
			    free(send_z[j][ivertical_neighbor]);
			    free(receive_z[j][ivertical_neighbor]);
			}
		}

	} /* endif nprocz>1 */

    /* END OF VERTICAL TRANSPORT */

    /* Put away tracers */


    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    for (kk=1;kk<=irec[j];kk++)
		{
		    E->trace.itrac[j][0]++;

		    if (E->trace.itrac[j][0]>(E->trace.itracsize[j]-5)) expand_tracer_arrays(E,j);

		    ireceive_position=(kk-1)*E->trace.number_of_tracer_quantities;

		    for (mm=0;mm<=(E->trace.number_of_advection_quantities-1);mm++)
			{
			    ipos=ireceive_position+mm;

			    E->trace.rtrac[j][mm][E->trace.itrac[j][0]]=REC[j][ipos];
			}
		    for (mm=0;mm<=(E->trace.number_of_extra_quantities-1);mm++)
			{
			    ipos=ireceive_position+E->trace.number_of_advection_quantities+mm;

			    E->trace.etrac[j][mm][E->trace.itrac[j][0]]=REC[j][ipos];
			}

		    theta=E->trace.rtrac[j][0][E->trace.itrac[j][0]];
		    phi=E->trace.rtrac[j][1][E->trace.itrac[j][0]];
		    rad=E->trace.rtrac[j][2][E->trace.itrac[j][0]];
		    x=E->trace.rtrac[j][3][E->trace.itrac[j][0]];
		    y=E->trace.rtrac[j][4][E->trace.itrac[j][0]];
		    z=E->trace.rtrac[j][5][E->trace.itrac[j][0]];


		    iel=iget_element(E,j,-99,x,y,z,theta,phi,rad);

		    if (iel<1)
			{
			    fprintf(E->trace.fpt,"Error(lost souls) - element not here?\n");
			    fprintf(E->trace.fpt,"x,y,z-theta,phi,rad: %f %f %f - %f %f %f\n",x,y,z,theta,phi,rad);
			    fflush(E->trace.fpt);
			    exit(10);
			}

		    E->trace.itrac[j][E->trace.itrac[j][0]]=iel;

		}
	}

    fprintf(E->trace.fpt,"Freeing memory in lost_souls()\n");
    fflush(E->trace.fpt);
    parallel_process_sync(E);

    /* Free Arrays */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    ithiscap=0;

	    free(REC[j]);

	    free(send[j][ithiscap]);

	    for (kk=1;kk<=E->parallel.TNUM_PASS[lev][j];kk++)
		{
		    ithatcap=kk;

		    free(send[j][ithatcap]);
		    free(receive[j][ithatcap]);

		}

	}
    fprintf(E->trace.fpt,"Leaving lost_souls()\n");
    fflush(E->trace.fpt);

    return;
}


/****** REDUCE  TRACER ARRAYS *****************************************/

void reduce_tracer_arrays(E)
     struct All_variables *E;

{

    int inewsize;
    int kk;
    int iempty_space;
    int j;

    int icushion=100;

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{


	    /* if physical size is double tracer size, reduce it */

	    iempty_space=(E->trace.itracsize[j]-E->trace.itrac[j][0]);

	    if (iempty_space>(E->trace.itrac[j][0]+icushion))
		{


		    inewsize=E->trace.itrac[j][0]+E->trace.itrac[j][0]/4+icushion;

		    if (inewsize<1)
			{
			    fprintf(E->trace.fpt,"Error(reduce tracer arrays)-something up (hdf3)\n");
			    fflush(E->trace.fpt);
			    exit(10);
			}


		    if ((E->trace.itrac[j]=(int *)realloc(E->trace.itrac[j],inewsize*sizeof(int)))==NULL)
			{
			    fprintf(E->trace.fpt,"ERROR(reduce tracer arrays )-no memory (itracer)\n");
			    fflush(E->trace.fpt);
			    exit(10);
			}


		    for (kk=0;kk<=((E->trace.number_of_advection_quantities)-1);kk++)
			{
			    if ((E->trace.rtrac[j][kk]=(double *)realloc(E->trace.rtrac[j][kk],inewsize*sizeof(double)))==NULL)
				{
				    fprintf(E->trace.fpt,"AKM(reduce tracer arrays )-no memory (%d)\n",kk);
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}

		    for (kk=0;kk<=((E->trace.number_of_extra_quantities)-1);kk++)
			{
			    if ((E->trace.etrac[j][kk]=(double *)realloc(E->trace.etrac[j][kk],inewsize*sizeof(double)))==NULL)
				{
				    fprintf(E->trace.fpt,"AKM(reduce tracer arrays )-no memory 783 (%d)\n",kk);
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}


		    fprintf(E->trace.fpt,"Reducing physical memory of itrac, rtrac, and etrac to %d from %d\n",
			    E->trace.itracsize[j],inewsize);

		    E->trace.itracsize[j]=inewsize;

		} /* end if */

	} /* end j */

    return;
}

/********** PUT AWAY LATER ************************************/
/*                                             */
/* rlater has a similar structure to rtrac     */
/* ilatersize is the physical memory and       */
/* ilater is the number of tracers             */


void put_away_later(E,j,it)
     struct All_variables *E;
     int it,j;

{


    int kk;


    void expand_later_array();


    /* The first tracer in initiates memory allocation. */
    /* Memory is freed after parallel communications    */

    if (E->trace.ilater[j]==0)
	{

	    E->trace.ilatersize[j]=E->trace.itracsize[j]/5;

	    for (kk=0;kk<=((E->trace.number_of_tracer_quantities)-1);kk++)
		{
		    if ((E->trace.rlater[j][kk]=(double *)malloc(E->trace.ilatersize[j]*sizeof(double)))==NULL)
			{
			    fprintf(E->trace.fpt,"AKM(put_away_later)-no memory (%d)\n",kk);
			    fflush(E->trace.fpt);
			    exit(10);
			}
		}
	} /* end first particle initiating memory allocation */


    /* Put tracer in later array */

    E->trace.ilater[j]++;

    if (E->trace.ilater[j] >= (E->trace.ilatersize[j]-5)) expand_later_array(E,j);

    /* stack advection and extra quantities together (advection first) */

    for (kk=0;kk<=((E->trace.number_of_advection_quantities)-1);kk++)
	{
	    E->trace.rlater[j][kk][E->trace.ilater[j]]=E->trace.rtrac[j][kk][it];
	}
    for (kk=0;kk<=((E->trace.number_of_extra_quantities)-1);kk++)
	{
	    E->trace.rlater[j][E->trace.number_of_advection_quantities+kk][E->trace.ilater[j]]=E->trace.etrac[j][kk][it];
	}

    return;
}

/****** EXPAND LATER ARRAY *****************************************/

void expand_later_array(E,j)
     struct All_variables *E;
     int j;
{

    int inewsize;
    int kk;
    int icushion;

    /* expand rlater by 20% */

    icushion=100;

    inewsize=E->trace.ilatersize[j]+E->trace.ilatersize[j]/5+icushion;

    for (kk=0;kk<=((E->trace.number_of_tracer_quantities)-1);kk++)
	{
	    if ((E->trace.rlater[j][kk]=(double *)realloc(E->trace.rlater[j][kk],inewsize*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"AKM(expand later array )-no memory (%d)\n",kk);
		    fflush(E->trace.fpt);
		    exit(10);
		}
	}


    fprintf(E->trace.fpt,"Expanding physical memory of rlater to %d from %d\n",
	    inewsize,E->trace.ilatersize[j]);

    E->trace.ilatersize[j]=inewsize;

    return;
}


/***** EJECT TRACER ************************************************/


void eject_tracer(E,j,it)
     struct All_variables *E;
     int it,j;

{

    int ilast_tracer;
    int kk;


    ilast_tracer=E->trace.itrac[j][0];

    /* put last tracer in ejected tracer position */

    E->trace.itrac[j][it]=E->trace.itrac[j][ilast_tracer];

    for (kk=0;kk<=((E->trace.number_of_advection_quantities)-1);kk++)
	{
	    E->trace.rtrac[j][kk][it]=E->trace.rtrac[j][kk][ilast_tracer];
	}
    for (kk=0;kk<=((E->trace.number_of_extra_quantities)-1);kk++)
	{
	    E->trace.etrac[j][kk][it]=E->trace.etrac[j][kk][ilast_tracer];
	}


    E->trace.itrac[j][0]--;

    return;
}



/*********** MAKE REGULAR GRID ********************************/
/*                                                            */
/* This function generates the finer regular grid which is    */
/* mapped to real elements                                    */

void make_regular_grid(E)
     struct All_variables *E;
{

    int j;
    int kk;
    int mm;
    int pp,node;
    int numtheta,numphi;
    int nodestheta,nodesphi;
    unsigned int numregel;
    unsigned int numregnodes;
    int idum1,idum2;
    int ifound_one;
    int ival;
    int ilast_el;
    int imap;
    int elz;
    int nelsurf;
    int iregnode[5];
    int ntheta,nphi;
    int ichoice;
    int icount;
    int itemp[5];
    int iregel;
    int istat_ichoice[13][5];
    int isum;

    double x,y,z;
    double theta,phi,rad;
    double deltheta;
    double delphi;
    double thetamax,thetamin;
    double phimax,phimin;
    double start_time;
    double theta_min,phi_min;
    double theta_max,phi_max;
    double half_diff;
    double expansion;

    double *tmin;
    double *tmax;
    double *fmin;
    double *fmax;

    int icheck_all_columns();
    int icheck_element_column();
    int icheck_column_neighbors();
    void sphere_to_cart();

    const double two_pi=2.0*M_PI;

    elz=E->lmesh.elz;

    nelsurf=E->lmesh.elx*E->lmesh.ely;

    //TODO: find the bounding box of the mesh, if the box is too close to
    // to core, set a flag (rotated_reggrid) to true and rotate the
    // bounding box to the equator. Generate the regular grid with the new
    // bounding box. The rotation should be a simple one, e.g.
    // (theta, phi) -> (??)
    // Whenever the regular grid is used, check the flat (rotated_reggrid),
    // if true, rotate the checkpoint as well.

    /* note, mesh is rotated along theta 22.5 degrees divided by elx. */
    /* We at least want that much expansion here! Otherwise, theta min */
    /* will not be valid near poles. We do a little more (x2) to be safe */
    /* Typically 1-2 degrees. Look in nodal_mesh.c for this.             */

    expansion=2.0*0.5*(M_PI/4.0)/(1.0*E->lmesh.elx);

    start_time=CPU_time0();

    if (E->parallel.me==0) fprintf(stderr,"Generating Regular Grid\n");
    fflush(stderr);


    /* for each cap, determine theta and phi bounds, watch out near poles  */

    numregnodes=0;
    for(j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    thetamax=0.0;
	    thetamin=M_PI;

	    phimax=two_pi;
	    phimin=0.0;

	    for (kk=1;kk<=E->lmesh.nno;kk=kk+E->lmesh.noz)
		{

		    theta=E->sx[j][1][kk];
		    phi=E->sx[j][2][kk];

		    thetamax=max(thetamax,theta);
		    thetamin=min(thetamin,theta);

		}

	    /* expand range slightly (should take care of poles)  */

	    thetamax=thetamax+expansion;
	    thetamax=min(thetamax,M_PI);

	    thetamin=thetamin-expansion;
	    thetamin=max(thetamin,0.0);

	    /* Convert input data from degrees to radians  */

	    deltheta=E->trace.deltheta[0]*M_PI/180.0;
	    delphi=E->trace.delphi[0]*M_PI/180.0;


	    /* Adjust deltheta and delphi to fit a uniform number of regular elements */

	    numtheta=fabs(thetamax-thetamin)/deltheta;
	    numphi=fabs(phimax-phimin)/delphi;
	    nodestheta=numtheta+1;
	    nodesphi=numphi+1;
	    numregel=numtheta*numphi;
	    numregnodes=nodestheta*nodesphi;

	    if ((numtheta==0)||(numphi==0))
		{
		    fprintf(E->trace.fpt,"Error(make_regular_grid): numtheta: %d numphi: %d\n",numtheta,numphi);
		    fflush(E->trace.fpt);
		    exit(10);
		}

	    deltheta=fabs(thetamax-thetamin)/(1.0*numtheta);
	    delphi=fabs(phimax-phimin)/(1.0*numphi);

	    /* fill global variables */

	    E->trace.deltheta[j]=deltheta;
	    E->trace.delphi[j]=delphi;
	    E->trace.numtheta[j]=numtheta;
	    E->trace.numphi[j]=numphi;
	    E->trace.thetamax[j]=thetamax;
	    E->trace.thetamin[j]=thetamin;
	    E->trace.phimax[j]=phimax;
	    E->trace.phimin[j]=phimin;
	    E->trace.numregel[j]=numregel;
	    E->trace.numregnodes[j]=numregnodes;

	    if ( ((1.0*numregel)/(1.0*E->lmesh.elx*E->lmesh.ely)) < 0.5 )
		{
		    fprintf(E->trace.fpt,"\n ! WARNING: regular/real ratio low: %f ! \n",
			    ((1.0*numregel)/(1.0*E->lmesh.nel)) );
		    fprintf(E->trace.fpt," Should reduce size of regular mesh\n");
		    fprintf(stderr,"! WARNING: regular/real ratio low: %f ! \n",
			    ((1.0*numregel)/(1.0*E->lmesh.nel)) );
		    fprintf(stderr," Should reduce size of regular mesh\n");
		    fflush(E->trace.fpt);
		    fflush(stderr);
		    if (E->trace.itracer_warnings==1) exit(10);
		}

	    /* print some output */

	    fprintf(E->trace.fpt,"\nRegular grid:\n");
	    fprintf(E->trace.fpt,"Theta min: %f max: %f \n",thetamin,thetamax);
	    fprintf(E->trace.fpt,"Phi min: %f max: %f \n",phimin,phimax);
	    fprintf(E->trace.fpt,"Adjusted deltheta: %f delphi: %f\n",deltheta,delphi);
	    fprintf(E->trace.fpt,"(numtheta: %d  numphi: %d)\n",numtheta,numphi);
	    fprintf(E->trace.fpt,"Number of regular elements: %d  (nodes: %d)\n",numregel,numregnodes);

	    fprintf(E->trace.fpt,"regular/real ratio: %f\n",((1.0*numregel)/(1.0*E->lmesh.elx*E->lmesh.ely)));
	    fflush(E->trace.fpt);

	    /* Allocate memory for regnodetoel */
	    /* Regtoel is an integer array which represents nodes on    */
	    /* the regular mesh. Each node on the regular mesh contains */
	    /* the real element value if one exists (-99 otherwise)     */



	    if ((E->trace.regnodetoel[j]=(int *)malloc((numregnodes+1)*sizeof(int)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(make regular) -no memory - uh3ud\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}


	    /* Initialize regnodetoel - reg elements not used =-99 */

	    for (kk=1;kk<=numregnodes;kk++)
		{
		    E->trace.regnodetoel[j][kk]=-99;
		}

	    /* Begin Mapping (only need to use surface elements) */

	    parallel_process_sync(E);
	    if (E->parallel.me==0) fprintf(stderr,"Beginning Mapping\n");
	    fflush(stderr);

	    /* Generate temporary arrays of max and min values for each surface element */


	    if ((tmin=(double *)malloc((nelsurf+1)*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(make regular) -no memory - 7t1a\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}
	    if ((tmax=(double *)malloc((nelsurf+1)*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(make regular) -no memory - 7t1a\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}
	    if ((fmin=(double *)malloc((nelsurf+1)*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(make regular) -no memory - 7t1a\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}
	    if ((fmax=(double *)malloc((nelsurf+1)*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(make regular) -no memory - 7t1a\n");
		    fflush(E->trace.fpt);
		    exit(10);
		}

	    for (mm=elz;mm<=E->lmesh.nel;mm=mm+elz)
		{

		    kk=mm/elz;

		    theta_min=M_PI;
		    theta_max=0.0;
		    phi_min=two_pi;
		    phi_max=0.0;
		    for (pp=1;pp<=4;pp++)
			{
			    node=E->ien[j][mm].node[pp];
			    theta=E->sx[j][1][node];
			    phi=E->sx[j][2][node];

			    theta_min=min(theta_min,theta);
			    theta_max=max(theta_max,theta);
			    phi_min=min(phi_min,phi);
			    phi_max=max(phi_max,phi);
			}

		    /* add half difference to phi and expansion to theta to be safe */

		    theta_max=theta_max+expansion;
		    theta_min=theta_min-expansion;

		    theta_max=min(M_PI,theta_max);
		    theta_min=max(0.0,theta_min);

		    half_diff=0.5*(phi_max-phi_min);
		    phi_max=phi_max+half_diff;
		    phi_min=phi_min-half_diff;

		    fix_phi(&phi_max);
		    fix_phi(&phi_min);

		    if (phi_min>phi_max)
			{
			    phi_min=0.0;
			    phi_max=two_pi;
			}

		    tmin[kk]=theta_min;
		    tmax[kk]=theta_max;
		    fmin[kk]=phi_min;
		    fmax[kk]=phi_max;
		}

	    /* end looking through elements */

	    ifound_one=0;

	    rad=E->sphere.ro;

	    imap=0;

	    for (kk=1;kk<=numregnodes;kk++)
		{

		    E->trace.regnodetoel[j][kk]=-99;

		    /* find theta and phi for a given regular node */

		    idum1=(kk-1)/(numtheta+1);
		    idum2=kk-1-idum1*(numtheta+1);

		    theta=thetamin+(1.0*idum2*deltheta);
		    phi=phimin+(1.0*idum1*delphi);

		    sphere_to_cart(E,theta,phi,rad,&x,&y,&z);


		    ilast_el=1;

		    /* if previous element not found yet, check all surface elements */

		    /*
		      if (ifound_one==0)
		      {
		      for (mm=elz;mm<=E->lmesh.nel;mm=mm+elz)
		      {
		      pp=mm/elz;
		      if ( (theta>=tmin[pp]) && (theta<=tmax[pp]) && (phi>=fmin[pp]) && (phi<=fmax[pp]) )
		      {
		      ival=icheck_element_column(E,j,mm,x,y,z,rad);
		      if (ival>0)
		      {
		      ilast_el=mm;
		      ifound_one++;
		      E->trace.regnodetoel[j][kk]=mm;
		      goto foundit;
		      }
		      }
		      }
		      goto foundit;
		      }
		    */

		    /* first check previous element */

		    ival=icheck_element_column(E,j,ilast_el,x,y,z,rad);
		    if (ival>0)
			{
			    E->trace.regnodetoel[j][kk]=ilast_el;
			    goto foundit;
			}

		    /* check neighbors */

		    ival=icheck_column_neighbors(E,j,ilast_el,x,y,z,rad);
		    if (ival>0)
			{
			    E->trace.regnodetoel[j][kk]=ival;
			    ilast_el=ival;
			    goto foundit;
			}

		    /* check all */

		    for (mm=elz;mm<=E->lmesh.nel;mm=mm+elz)
			{
			    pp=mm/elz;
			    if ( (theta>=tmin[pp]) && (theta<=tmax[pp]) && (phi>=fmin[pp]) && (phi<=fmax[pp]) )
				{
				    ival=icheck_element_column(E,j,mm,x,y,z,rad);
				    if (ival>0)
					{
					    ilast_el=mm;
					    E->trace.regnodetoel[j][kk]=mm;
					    goto foundit;
					}
				}
			}

		foundit:

		    if (E->trace.regnodetoel[j][kk]>0) imap++;

		} /* end all regular nodes (kk) */

	    fprintf(E->trace.fpt,"percentage mapped: %f\n", (1.0*imap)/(1.0*numregnodes)*100.0);
	    fflush(E->trace.fpt);

	    /* free temporary arrays */

	    free(tmin);
	    free(tmax);
	    free(fmin);
	    free(fmax);

	} /* end j */


    /* some error control */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    for (kk=1;kk<=numregnodes;kk++)
		{

		    if (E->trace.regnodetoel[j][kk]!=-99)
			{
			    if ( (E->trace.regnodetoel[j][kk]<1)||(E->trace.regnodetoel[j][kk]>E->lmesh.nel) )
				{
				    fprintf(stderr,"Error(make_regular_grid)-invalid element: %d\n",E->trace.regnodetoel[j][kk]);
				    fprintf(E->trace.fpt,"Error(make_regular_grid)-invalid element: %d\n",E->trace.regnodetoel[j][kk]);
				    fflush(E->trace.fpt);
				    fflush(stderr);
				    exit(10);
				}
			}
		}
	}


    /* Now put regnodetoel information into regtoel */


    if (E->parallel.me==0) fprintf(stderr,"Beginning Regtoel submapping \n");
    fflush(stderr);

    /* AKMA decided it would be more efficient to have reg element choice array */
    /* rather than reg node array as used before          */


    for(j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    /* initialize statistical counter */

	    for (pp=0;pp<=4;pp++) istat_ichoice[j][pp]=0;

	    /* Allocate memory for regtoel */
	    /* Regtoel consists of 4 positions for each regular element */
	    /* Position[0] lists the number of element choices (later   */
	    /* referred to as ichoice), followed                        */
	    /* by the possible element choices.                          */
	    /* ex.) A regular element has 4 nodes. Each node resides in  */
	    /* a real element. The number of real elements a regular     */
	    /* element touches (one of its nodes are in) is ichoice.     */
	    /* Special ichoice notes:                                    */
	    /*    ichoice=-1   all regular element nodes = -99 (no elements) */
	    /*    ichoice=0    all 4 corners within one element              */
	    /*    ichoice=1     one element choice (diff from ichoice 0 in    */
	    /*                  that perhaps one reg node is in an element    */
	    /*                  and the rest are not (-99).                   */
	    /*    ichoice>1     Multiple elements to check                    */

	    numregel= E->trace.numregel[j];

	    for (pp=0;pp<=4;pp++)
		{
		    if ((E->trace.regtoel[j][pp]=(int *)malloc((numregel+1)*sizeof(int)))==NULL)
			{
			    fprintf(E->trace.fpt,"ERROR(make regular)-no memory 98d (%d %d %d)\n",pp,numregel,j);
			    fflush(E->trace.fpt);
			    exit(10);
			}
		}

	    numtheta=E->trace.numtheta[j];
	    numphi=E->trace.numphi[j];

	    for (nphi=1;nphi<=numphi;nphi++)
		{
		    for (ntheta=1;ntheta<=numtheta;ntheta++)
			{

			    iregel=ntheta+(nphi-1)*numtheta;

			    /* initialize regtoel (not necessary really) */

			    for (pp=0;pp<=4;pp++) E->trace.regtoel[j][pp][iregel]=-33;

			    if ( (iregel>numregel)||(iregel<1) )
				{
				    fprintf(E->trace.fpt,"ERROR(make_regular_grid)-weird iregel: %d (max: %d)\n",iregel,numregel);
				    fflush(E->trace.fpt);
				    exit(10);
				}

			    iregnode[1]=iregel+(nphi-1);
			    iregnode[2]=iregel+nphi;
			    iregnode[3]=iregel+nphi+E->trace.numtheta[j]+1;
			    iregnode[4]=iregel+nphi+E->trace.numtheta[j];

			    for (kk=1;kk<=4;kk++)
				{
				    if ((iregnode[kk]<1)||(iregnode[kk]>numregnodes))
					{
					    fprintf(E->trace.fpt,"ERROR(make regular)-bad regnode %d\n",iregnode[kk]);
					    fflush(E->trace.fpt);
					    exit(10);
					}
				    if (E->trace.regnodetoel[j][iregnode[kk]]>E->lmesh.nel)
					{
					    fprintf(E->trace.fpt,"AABB HERE %d %d %d %d\n",iregel,iregnode[kk],kk,E->trace.regnodetoel[j][iregnode[kk]]);
					    fflush(E->trace.fpt);
					}
				}


			    /* find number of choices */

			    ichoice=0;
			    icount=0;

			    for (kk=1;kk<=4;kk++)
				{

				    if (E->trace.regnodetoel[j][iregnode[kk]]<=0) goto next_corner;

				    icount++;
				    for (pp=1;pp<=(kk-1);pp++)
					{
					    if (E->trace.regnodetoel[j][iregnode[kk]]==E->trace.regnodetoel[j][iregnode[pp]]) goto next_corner;
					}
				    ichoice++;
				    itemp[ichoice]=E->trace.regnodetoel[j][iregnode[kk]];

				    if ((ichoice<0) || (ichoice>4) )
					{
					    fprintf(E->trace.fpt,"ERROR(make regular) - weird ichoice %d \n",ichoice);
					    fflush(E->trace.fpt);
					    exit(10);
					}
				    if ((itemp[ichoice]<0) || (itemp[ichoice]>E->lmesh.nel) )
					{
					    fprintf(E->trace.fpt,"ERROR(make regular) - weird element choice %d %d\n",itemp[ichoice],ichoice);
					    fflush(E->trace.fpt);
					    exit(10);
					}

				next_corner:
				    ;
				} /* end kk */

			    istat_ichoice[j][ichoice]++;

			    if ((ichoice<0) || (ichoice>4))
				{
				    fprintf(E->trace.fpt,"ERROR(make_regular)-wierd ichoice %d\n",ichoice);
				    fflush(E->trace.fpt);
				    exit(10);
				}

			    if (ichoice==0)
				{
				    E->trace.regtoel[j][0][iregel]=-1;
				    /*
				      fprintf(E->trace.fpt,"HH1: (%p) iregel: %d ichoice: %d value: %d %d\n",&E->trace.regtoel[j][1][iregel],iregel,ichoice,E->trace.regtoel[j][0][iregel],E->trace.regtoel[j][1][iregel]);
				    */
				}
			    else if ( (ichoice==1) && (icount==4) )
				{
				    E->trace.regtoel[j][0][iregel]=0;
				    E->trace.regtoel[j][1][iregel]=itemp[1];

				    /*
				      fprintf(E->trace.fpt,"HH2: (%p) iregel: %d ichoice: %d value: %d %d\n",&E->trace.regtoel[j][1][iregel],iregel,ichoice,E->trace.regtoel[j][0][iregel],E->trace.regtoel[j][1][iregel]);
				    */

				    if (itemp[1]<1 || itemp[1]>E->lmesh.nel)
					{
					    fprintf(E->trace.fpt,"ERROR(make_regular)-huh? wierd itemp\n");
					    fflush(E->trace.fpt);
					    exit(10);
					}
				}
			    else if ( (ichoice>0) && (ichoice<5) )
				{
				    E->trace.regtoel[j][0][iregel]=ichoice;
				    for (pp=1;pp<=ichoice;pp++)
					{
					    E->trace.regtoel[j][pp][iregel]=itemp[pp];

					    /*
					      fprintf(E->trace.fpt,"HH:(%p)  iregel: %d ichoice: %d pp: %d value: %d %d\n",&E->trace.regtoel[j][pp][iregel],iregel,ichoice,pp,itemp[pp],E->trace.regtoel[j][pp][iregel]);
					    */
					    if (itemp[pp]<1 || itemp[pp]>E->lmesh.nel)
						{
						    fprintf(E->trace.fpt,"ERROR(make_regular)-huh? wierd itemp 2 \n");
						    fflush(E->trace.fpt);
						    exit(10);
						}
					}
				}
			    else
				{
				    fprintf(E->trace.fpt,"ERROR(make_regular)- should not be here! %d\n",ichoice);
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}
		}

	    /* can now free regnodetoel */

	    free (E->trace.regnodetoel[j]);


	    /* testing */
	    for (kk=1;kk<=E->trace.numregel[j];kk++)
		{
		    if ((E->trace.regtoel[j][0][kk]<-1)||(E->trace.regtoel[j][0][kk]>4))
			{
			    fprintf(E->trace.fpt,"ERROR(make regular) regtoel ichoice0? %d %d \n",kk,E->trace.regtoel[j][pp][kk]);
			    fflush(E->trace.fpt);
			    exit(10);
			}
		    for (pp=1;pp<=4;pp++)
			{
			    if (((E->trace.regtoel[j][pp][kk]<1)&&(E->trace.regtoel[j][pp][kk]!=-33))||(E->trace.regtoel[j][pp][kk]>E->lmesh.nel))
				{
				    fprintf(E->trace.fpt,"ERROR(make regular) (%p) regtoel? %d %d(%d) %d\n",&E->trace.regtoel[j][pp][kk],kk,pp,E->trace.regtoel[j][0][kk],E->trace.regtoel[j][pp][kk]);
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}
		}

	} /* end j */


    fprintf(E->trace.fpt,"Mapping completed (%f seconds)\n",CPU_time0()-start_time);
    fflush(E->trace.fpt);

    parallel_process_sync(E);

    if (E->parallel.me==0) fprintf(stderr,"Mapping completed (%f seconds)\n",CPU_time0()-start_time);
    fflush(stderr);

    /* Print out information regarding regular/real element coverage */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    isum=0;
	    for (kk=0;kk<=4;kk++) isum=isum+istat_ichoice[j][kk];
	    fprintf(E->trace.fpt,"\n\nInformation regarding number of real elements per regular elements\n");
	    fprintf(E->trace.fpt," (stats done on regular elements that were used)\n");
	    fprintf(E->trace.fpt,"Ichoice is number of real elements touched by a regular element\n");
	    fprintf(E->trace.fpt,"  (ichoice=1 is optimal)\n");
	    fprintf(E->trace.fpt,"  (if ichoice=0, no elements are touched by regular element)\n");
	    fprintf(E->trace.fpt,"Ichoice=0: %f percent\n",(100.0*istat_ichoice[j][0])/(1.0*isum));
	    fprintf(E->trace.fpt,"Ichoice=1: %f percent\n",(100.0*istat_ichoice[j][1])/(1.0*isum));
	    fprintf(E->trace.fpt,"Ichoice=2: %f percent\n",(100.0*istat_ichoice[j][2])/(1.0*isum));
	    fprintf(E->trace.fpt,"Ichoice=3: %f percent\n",(100.0*istat_ichoice[j][3])/(1.0*isum));
	    fprintf(E->trace.fpt,"Ichoice=4: %f percent\n",(100.0*istat_ichoice[j][4])/(1.0*isum));

	} /* end j */


    return;
}



/*********  ICHECK COLUMN NEIGHBORS ***************************/
/*                                                            */
/* This function check whether a point is in a neighboring    */
/* column. Neighbor surface element number is returned        */

int icheck_column_neighbors(E,j,nel,x,y,z,rad)
     struct All_variables *E;
     int j,nel;
     double x,y,z,rad;
{

    int ival;
    int neighbor[25];
    int elx,ely,elz;
    int elxz;
    int kk;

    int icheck_element_column();

    /*
      const int number_of_neighbors=24;
    */

    /* maybe faster to only check inner ring */

    const int number_of_neighbors=8;

    elx=E->lmesh.elx;
    ely=E->lmesh.ely;
    elz=E->lmesh.elz;

    elxz=elx*elz;

    /* inner ring */

    neighbor[1]=nel-elxz-elz;
    neighbor[2]=nel-elxz;
    neighbor[3]=nel-elxz+elz;
    neighbor[4]=nel-elz;
    neighbor[5]=nel+elz;
    neighbor[6]=nel+elxz-elz;
    neighbor[7]=nel+elxz;
    neighbor[8]=nel+elxz+elz;

    /* outer ring */

    neighbor[9]=nel+2*elxz-elz;
    neighbor[10]=nel+2*elxz;
    neighbor[11]=nel+2*elxz+elz;
    neighbor[12]=nel+2*elxz+2*elz;
    neighbor[13]=nel+elxz+2*elz;
    neighbor[14]=nel+2*elz;
    neighbor[15]=nel-elxz+2*elz;
    neighbor[16]=nel-2*elxz+2*elz;
    neighbor[17]=nel-2*elxz+elz;
    neighbor[18]=nel-2*elxz;
    neighbor[19]=nel-2*elxz-elz;
    neighbor[20]=nel-2*elxz-2*elz;
    neighbor[21]=nel-elxz-2*elz;
    neighbor[22]=nel-2*elz;
    neighbor[23]=nel+elxz-2*elz;
    neighbor[24]=nel+2*elxz-2*elz;


    for (kk=1;kk<=number_of_neighbors;kk++)
	{

	    if ((neighbor[kk]>=1)&&(neighbor[kk]<=E->lmesh.nel))
		{
		    ival=icheck_element_column(E,j,neighbor[kk],x,y,z,rad);
		    if (ival>0)
			{
			    return neighbor[kk];
			}
		}
	}

    return -99;
}

/********** ICHECK ALL COLUMNS ********************************/
/*                                                            */
/* This function check all columns until the proper one for   */
/* a point (x,y,z) is found. The surface element is returned  */
/* else -99 if can't be found.                                */

int icheck_all_columns(E,j,x,y,z,rad)
     struct All_variables *E;
     int j;
     double x,y,z,rad;
{

    int icheck;
    int nel;
    int icheck_element_column();

    int elz=E->lmesh.elz;
    int numel=E->lmesh.nel;

    for (nel=elz;nel<=numel;nel=nel+elz)
	{
	    icheck=icheck_element_column(E,j,nel,x,y,z,rad);
	    if (icheck==1)
		{
		    return nel;
		}
	}


    return -99;
}



/**** WRITE TRACE INSTRUCTIONS ***************/
void write_trace_instructions(E)
     struct All_variables *E;
{
    fprintf(E->trace.fpt,"\nTracing Activated! (proc: %d)\n",E->parallel.me);
    fprintf(E->trace.fpt,"   Allen K. McNamara 12-2003\n\n");

    if (E->trace.ic_method==0)
	{
	    fprintf(E->trace.fpt,"Generating New Tracer Array\n");
	    fprintf(E->trace.fpt,"Tracers per element: %d\n",E->trace.itperel);
	    /* TODO: move
	    if (E->composition.ichemical_buoyancy==1)
		{
		    fprintf(E->trace.fpt,"Interface Height: %f\n",E->composition.z_interface);
		}
	    */
	}
    if (E->trace.ic_method==1)
	{
	    fprintf(E->trace.fpt,"Reading tracer file %s\n",E->trace.tracer_file);
	}
    if (E->trace.ic_method==2)
	{
	    fprintf(E->trace.fpt,"Restarting Tracers\n");
	}

    if (E->trace.itracer_advection_scheme==1)
	{
	    fprintf(E->trace.fpt,"\nSimple predictor-corrector method used\n");
	    fprintf(E->trace.fpt,"(Uses only velocity at to) \n");
	    fprintf(E->trace.fpt,"(xf=x0+0.5*dt*(v(x0,y0,z0) + v(xp,yp,zp))\n\n");
	}
    else if (E->trace.itracer_advection_scheme==2)
	{
	    fprintf(E->trace.fpt,"\nPredictor-corrector method used\n");
	    fprintf(E->trace.fpt,"(Uses only velocity at to and to+dt) \n");
	    fprintf(E->trace.fpt,"(xf=x0+0.5*dt*(v(x0,y0,z0,to) + v(xp,yp,zp,to+dt))\n\n");
	}
    else
	{
	    fprintf(E->trace.fpt,"Sorry-Other Advection Schemes are Unavailable %d\n",E->trace.itracer_advection_scheme);
	    fflush(E->trace.fpt);
	    parallel_process_termination();
	}

    if (E->trace.itracer_interpolation_scheme==1)
	{
	    fprintf(E->trace.fpt,"\nGreat Circle Projection Interpolation Scheme \n");
	}
    else if (E->trace.itracer_interpolation_scheme==2)
	{
	    fprintf(E->trace.fpt,"\nSimple Averaging Interpolation Scheme \n");
	    fprintf(E->trace.fpt,"\n(Not that great of a scheme!) \n");

	    fprintf(E->trace.fpt,"Sorry-Other Interpolation Schemes are Unavailable\n");
	    fflush(E->trace.fpt);
	    parallel_process_termination();

	}
    else
	{
	    fprintf(E->trace.fpt,"Sorry-Other Interpolation Schemes are Unavailable\n");
	    fflush(E->trace.fpt);
	    parallel_process_termination();
	}


    /* regular grid stuff */

    fprintf(E->trace.fpt,"Regular Grid-> deltheta: %f delphi: %f\n",
	    E->trace.deltheta[0],E->trace.delphi[0]);




    /* more obscure stuff */

    fprintf(E->trace.fpt,"Box Cushion: %f\n",E->trace.box_cushion);
    fprintf(E->trace.fpt,"Number of Advection Quantities: %d\n",
	    E->trace.number_of_advection_quantities);
    fprintf(E->trace.fpt,"Number of Extra Quantities: %d\n",
	    E->trace.number_of_extra_quantities);
    fprintf(E->trace.fpt,"Total Number of Tracer Quantities: %d\n",
	    E->trace.number_of_tracer_quantities);


    /* analytical test */

    if (E->trace.ianalytical_tracer_test==1)
	{
	    fprintf(E->trace.fpt,"\n\n ! Analytical Test Being Performed ! \n");
	    fprintf(E->trace.fpt,"(some of the above parameters may not be used or applied\n");
	    fprintf(E->trace.fpt,"Velocity functions given in main code\n");
	    fflush(E->trace.fpt);
	}

    if (E->trace.itracer_warnings==0)
	{
	    fprintf(E->trace.fpt,"\n WARNING EXITS ARE TURNED OFF! TURN THEM ON!\n");
	    fprintf(stderr,"\n WARNING EXITS ARE TURNED OFF! TURN THEM ON!\n");
	    fflush(E->trace.fpt);
	    fflush(stderr);
	}

    write_composition_instructions(E);
    return;
}


/************** RESTART TRACERS ******************************************/
/*                                                                       */
/* This function restarts tracers written from previous calculation      */
/* and the tracers are read as seperate files for each processor domain. */

void restart_tracers(E)
     struct All_variables *E;
{

    char output_file[200];
    char input_s[1000];

    int j,kk;
    int idum1,ncolumns;
    int numtracers;

    double rdum1;
    double theta,phi,rad;
    double comp;
    double x,y,z;

    void initialize_tracer_arrays();
    void sphere_to_cart();
    void keep_in_sphere();

    FILE *fp1;


    sprintf(output_file,"%s.tracer.%d.%d",E->control.old_P_file,E->parallel.me,E->monitor.solution_cycles_init);

    if ( (fp1=fopen(output_file,"r"))==NULL)
        {
            fprintf(E->trace.fpt,"ERROR(restart tracers)-file not found %s\n",output_file);
            fflush(E->trace.fpt);
            exit(10);
        }

    fprintf(stderr,"Restarting Tracers from %s\n",output_file);
    fflush(stderr);


    for(j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    fgets(input_s,200,fp1);
	    sscanf(input_s,"%d %d %d %lf",
		   &idum1, &numtracers, &ncolumns, &rdum1);

            /* some error control */
            if (E->composition.ichemical_buoyancy==0 && ncolumns!=3) {
                fprintf(E->trace.fpt,"ERROR(restart tracers)-wrong # of columns\n");
                fflush(E->trace.fpt);
                exit(10);
            }
            if (E->composition.ichemical_buoyancy==1 && ncolumns!=4) {
                fprintf(E->trace.fpt,"ERROR(restart tracers)-wrong # of columns\n");
                fflush(E->trace.fpt);
                exit(10);
            }

	    /* allocate memory for tracer arrays */

	    initialize_tracer_arrays(E,j,numtracers);
	    E->trace.itrac[j][0]=numtracers;

	    for (kk=1;kk<=numtracers;kk++)
		{
		    comp=0.0;
		    fgets(input_s,200,fp1);
		    if (E->composition.ichemical_buoyancy==0)
			{
			    sscanf(input_s,"%lf %lf %lf\n",&theta,&phi,&rad);
			}
		    //TODO: move
		    else if (E->composition.ichemical_buoyancy==1)
			{
			    sscanf(input_s,"%lf %lf %lf %lf\n",&theta,&phi,&rad,&comp);
			}
		    else
			{
			    fprintf(E->trace.fpt,"ERROR(restart tracers)-huh?\n");
			    fflush(E->trace.fpt);
			    exit(10);
			}

		    sphere_to_cart(E,theta,phi,rad,&x,&y,&z);

		    /* it is possible that if on phi=0 boundary, significant digits can push phi over 2pi */

		    keep_in_sphere(E,&x,&y,&z,&theta,&phi,&rad);

		    E->trace.rtrac[j][0][kk]=theta;
		    E->trace.rtrac[j][1][kk]=phi;
		    E->trace.rtrac[j][2][kk]=rad;
		    E->trace.rtrac[j][3][kk]=x;
		    E->trace.rtrac[j][4][kk]=y;
		    E->trace.rtrac[j][5][kk]=z;
		    //TODO: move
		    if (E->composition.ichemical_buoyancy==1) E->trace.etrac[j][0][kk]=comp;

		}

	    fprintf(E->trace.fpt,"Read %d tracers from file %s\n",numtracers,output_file);
	    fflush(E->trace.fpt);

	}


    return;
}

/************** MAKE TRACER ARRAY ********************************/
/* Here, each cap will generate tracers somewhere                */
/* in the sphere - check if its in this cap  - then check radial */

void make_tracer_array(E)
     struct All_variables *E;
{

    int kk;
    int tracers_cap;
    int j;
    int ival;
    int number_of_tries=0;
    int max_tries;

    double x,y,z;
    double theta,phi,rad;
    double dmin,dmax;
    double random1,random2,random3;
    double processor_fraction;

    void cart_to_sphere();
    void keep_in_sphere();
    void initialize_tracer_arrays();

    int icheck_cap();

    if (E->parallel.me==0) fprintf(stderr,"Making Tracer Array\n");
    fflush(stderr);


    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    processor_fraction=( ( pow(E->sx[j][3][E->lmesh.noz],3.0)-pow(E->sx[j][3][1],3.0))/
				 (pow(E->sphere.ro,3.0)-pow(E->sphere.ri,3.0)));
	    tracers_cap=E->mesh.nel*E->trace.itperel*processor_fraction;
	    /*
	      fprintf(stderr,"AA: proc frac: %f (%d) %d %d %f %f\n",processor_fraction,tracers_cap,E->lmesh.nel,E->parallel.nprocz, E->sx[j][3][E->lmesh.noz],E->sx[j][3][1]);
	    */

	    fprintf(E->trace.fpt,"\nGenerating %d Tracers\n",tracers_cap);

	    initialize_tracer_arrays(E,j,tracers_cap);


	    /* Tracers are placed randomly in cap */
	    /* (intentionally using rand() instead of srand() )*/

	    dmin=-1.0*E->sphere.ro;
	    dmax=E->sphere.ro;

	    while (E->trace.itrac[j][0]<tracers_cap)
		{

		    number_of_tries++;
		    max_tries=500*tracers_cap;

		    if (number_of_tries>max_tries)
			{
			    fprintf(E->trace.fpt,"Error(make_tracer_array)-too many tries?\n");
			    fprintf(E->trace.fpt,"%d %d %d\n",max_tries,number_of_tries,RAND_MAX);
			    fflush(E->trace.fpt);
			    exit(10);
			}


		    random1=(1.0*rand())/(1.0*RAND_MAX);
		    random2=(1.0*rand())/(1.0*RAND_MAX);
		    random3=(1.0*rand())/(1.0*RAND_MAX);

		    x=dmin+random1*(dmax-dmin);
		    y=dmin+random2*(dmax-dmin);
		    z=dmin+random3*(dmax-dmin);

		    /* first check if within shell */

		    rad=sqrt(x*x+y*y+z*z);

		    if (rad>=E->sx[j][3][E->lmesh.noz]) goto next_try;
		    if (rad<E->sx[j][3][1]) goto next_try;


		    /* check if in current cap */

		    ival=icheck_cap(E,0,x,y,z,rad);

		    if (ival!=1) goto next_try;

		    /* Made it, so record tracer information */

		    cart_to_sphere(E,x,y,z,&theta,&phi,&rad);

		    keep_in_sphere(E,&x,&y,&z,&theta,&phi,&rad);

		    E->trace.itrac[j][0]++;
		    kk=E->trace.itrac[j][0];

		    E->trace.rtrac[j][0][kk]=theta;
		    E->trace.rtrac[j][1][kk]=phi;
		    E->trace.rtrac[j][2][kk]=rad;
		    E->trace.rtrac[j][3][kk]=x;
		    E->trace.rtrac[j][4][kk]=y;
		    E->trace.rtrac[j][5][kk]=z;

		next_try:
		    ;
		} /* end while */


	}/* end j */

    fprintf(stderr,"DONE Making Tracer Array (%d)\n",E->parallel.me);
    fflush(stderr);

    return;
}

/******** READ TRACER ARRAY *********************************************/
/*                                                                      */
/* This function reads tracers from input file.                         */
/* All processors read the same input file, then sort out which ones    */
/* belong.                                                              */

void read_tracer_file(E)
     struct All_variables *E;
{

    char input_s[1000];

    int number_of_tracers;
    int kk;
    int icheck;
    int iestimate;
    int icushion;
    int j;

    int icheck_cap();
    int icheck_processor_shell();
    int isum_tracers();
    void initialize_tracer_arrays();
    void keep_in_sphere();
    void sphere_to_cart();
    void cart_to_sphere();
    void expand_tracer_arrays();

    double x,y,z;
    double theta,phi,rad;
    double rdum1,rdum2,rdum3;

    FILE *fptracer;

    fptracer=fopen(E->trace.tracer_file,"r");
    fprintf(E->trace.fpt,"Opening %s\n",E->trace.tracer_file);

    fgets(input_s,200,fptracer);
    sscanf(input_s,"%d",&number_of_tracers);
    fprintf(E->trace.fpt,"%d Tracers in file \n",number_of_tracers);

    /* initially size tracer arrays to number of tracers divided by processors */

    icushion=100;

    iestimate=number_of_tracers/E->parallel.nproc + icushion;

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    initialize_tracer_arrays(E,j,iestimate);

	    for (kk=1;kk<=number_of_tracers;kk++)
		{
		    fgets(input_s,200,fptracer);
		    sscanf(input_s,"%lf %lf %lf",&rdum1,&rdum2,&rdum3);

                    theta=rdum1;
                    phi=rdum2;
                    rad=rdum3;
                    sphere_to_cart(E,theta,phi,rad,&x,&y,&z);


		    /* make sure theta, phi is in range, and radius is within bounds */

		    keep_in_sphere(E,&x,&y,&z,&theta,&phi,&rad);

		    /* check whether tracer is within processor domain */

		    icheck=1;
		    if (E->parallel.nprocz>1) icheck=icheck_processor_shell(E,j,rad);
		    if (icheck!=1) goto next_tracer;

		    icheck=icheck_cap(E,0,x,y,z,rad);
		    if (icheck==0) goto next_tracer;

		    /* if still here, tracer is in processor domain */


		    E->trace.itrac[j][0]++;

		    if (E->trace.itrac[j][0]>=(E->trace.itracsize[j]-5)) expand_tracer_arrays(E,j);

		    E->trace.rtrac[j][0][E->trace.itrac[j][0]]=theta;
		    E->trace.rtrac[j][1][E->trace.itrac[j][0]]=phi;
		    E->trace.rtrac[j][2][E->trace.itrac[j][0]]=rad;
		    E->trace.rtrac[j][3][E->trace.itrac[j][0]]=x;
		    E->trace.rtrac[j][4][E->trace.itrac[j][0]]=y;
		    E->trace.rtrac[j][5][E->trace.itrac[j][0]]=z;

		next_tracer:
		    ;
		} /* end kk, number of tracers */

	    fprintf(E->trace.fpt,"Number of tracers in this cap is: %d\n",
		    E->trace.itrac[j][0]);

	} /* end j */

    fclose(fptracer);

    icheck=isum_tracers(E);

    if (icheck!=number_of_tracers)
	{
	    fprintf(E->trace.fpt,"ERROR(read_tracer_file) - tracers != number in file\n");
	    fprintf(E->trace.fpt,"Tracers in system: %d\n", icheck);
	    fprintf(E->trace.fpt,"Tracers in file: %d\n", number_of_tracers);
	    fflush(E->trace.fpt);
	    exit(10);
	}

    return;
}


/********** CART TO SPHERE ***********************/
void cart_to_sphere(E,x,y,z,theta,phi,rad)
     struct All_variables *E;
     double x,y,z;
     double *theta,*phi,*rad;
{

    double temp;
    double myatan();

    temp=x*x+y*y;

    *rad=sqrt(temp+z*z);
    *theta=atan2(sqrt(temp),z);
    *phi=myatan(y,x);


    return;
}

/********** SPHERE TO CART ***********************/
void sphere_to_cart(E,theta,phi,rad,x,y,z)
     struct All_variables *E;
     double theta,phi,rad;
     double *x,*y,*z;
{

    double sint,cost,sinf,cosf;
    double temp;

    sint=sin(theta);
    cost=cos(theta);
    sinf=sin(phi);
    cosf=cos(phi);

    temp=rad*sint;

    *x=temp*cosf;
    *y=temp*sinf;
    *z=rad*cost;


    return;
}


/********* ICHECK THAT PROCESSOR SHELL ********/
/*                                            */
/* Checks whether a given radius is within    */
/* a given processors radial domain.          */
/* Returns 0 if not, 1 if so.                 */
/* The domain is defined as including the bottom */
/* radius, but excluding the top radius unless   */
/* we the processor domain is the one that       */
/* is at the surface (then both boundaries are   */
/* included).                                    */

int icheck_that_processor_shell(E,j,nprocessor,rad)
     struct All_variables *E;
     int j;
     int nprocessor;
     double rad;
{
    int icheck_processor_shell();
    int me = E->parallel.me;

    /* nprocessor is right on top of me */
    if (nprocessor == me+1) {
        if (icheck_processor_shell(E, j, rad) == 0) return 1;
        else return 0;
    }

    /* nprocessor is right on bottom of me */
    if (nprocessor == me-1) {
        if (icheck_processor_shell(E, j, rad) == -99) return 1;
        else return 0;
    }

    /* Shouldn't be here */
    fprintf(E->trace.fpt, "Should not be here\n");
    fprintf(E->trace.fpt, "Error(check_shell) nprocessor: %d, radius: %f\n",
            nprocessor, rad);
    fflush(E->trace.fpt);
    exit(10);
}


/********** ICHECK PROCESSOR SHELL *************/
/* returns -99 if rad is below current shell  */
/* returns 0 if rad is above current shell    */
/* returns 1 if rad is within current shell   */
/*                                            */
/* Shell, here, refers to processor shell     */
/*                                            */
/* shell is defined as bottom boundary up to  */
/* and not including the top boundary unless  */
/* the shell in question is the top shell     */

int icheck_processor_shell(E,j,rad)
     struct All_variables *E;
     double rad;
     int j;

{

    const int noz = E->lmesh.noz;
    const int nprocz = E->parallel.nprocz;
    double top_r, bottom_r;

    if (nprocz==1) return 1;

    top_r = E->sx[j][3][noz];
    bottom_r = E->sx[j][3][1];

    /* First check bottom */

    if (rad<bottom_r) return -99;


    /* Check top */

    if (rad<top_r) return 1;

    /* top processor */

    if ( (rad<=top_r) && (E->parallel.me_loc[3]==nprocz-1) ) return 1;

    /* If here, means point is above processor */
    return 0;
}

/******* ICHECK ELEMENT *************************************/
/*                                                          */
/* This function serves to determine if a point lies within */
/* a given element                                          */

int icheck_element(E,j,nel,x,y,z,rad)
     struct All_variables *E;
     int nel,j;
     double x,y,z,rad;
{

    int icheck;
    int icheck_element_column();
    int icheck_shell();

    icheck=icheck_shell(E,nel,rad);
    if (icheck==0)
	{
	    return 0;
	}

    icheck=icheck_element_column(E,j,nel,x,y,z,rad);
    if (icheck==0)
	{
	    return 0;
	}


    return 1;
}

/********  ICHECK SHELL ************************************/
/*                                                         */
/* This function serves to check whether a point lies      */
/* within the proper radial shell of a given element       */
/* note: j set to 1; shouldn't depend on cap               */

int icheck_shell(E,nel,rad)
     struct All_variables *E;
     int nel;
     double rad;
{

    int ival;
    int ibottom_node;
    int itop_node;

    double bottom_rad;
    double top_rad;


    ibottom_node=E->ien[1][nel].node[1];
    itop_node=E->ien[1][nel].node[5];

    bottom_rad=E->sx[1][3][ibottom_node];
    top_rad=E->sx[1][3][itop_node];

    ival=0;
    if ((rad>=bottom_rad)&&(rad<top_rad)) ival=1;

    return ival;
}

/********  ICHECK ELEMENT COLUMN ****************************/
/*                                                          */
/* This function serves to determine if a point lies within */
/* a given element's column                                 */

int icheck_element_column(E,j,nel,x,y,z,rad)
     struct All_variables *E;
     int nel,j;
     double x,y,z,rad;
{

    double test_point[4];
    double rnode[5][10];

    int lev = E->mesh.levmax;
    int ival;
    int kk;
    int node;
    int icheck_bounds();


    E->trace.istat_elements_checked++;

    /* surface coords of element nodes */

    for (kk=1;kk<=4;kk++)
	{

	    node=E->ien[j][nel].node[kk+4];

	    rnode[kk][1]=E->x[j][1][node];
	    rnode[kk][2]=E->x[j][2][node];
	    rnode[kk][3]=E->x[j][3][node];

	    rnode[kk][4]=E->sx[j][1][node];
	    rnode[kk][5]=E->sx[j][2][node];

	    rnode[kk][6]=E->SinCos[lev][j][2][node]; /* cos(theta) */
	    rnode[kk][7]=E->SinCos[lev][j][0][node]; /* sin(theta) */
	    rnode[kk][8]=E->SinCos[lev][j][3][node]; /* cos(phi) */
	    rnode[kk][9]=E->SinCos[lev][j][1][node]; /* sin(phi) */

	}

    /* test_point - project to outer radius */

    test_point[1]=x/rad;
    test_point[2]=y/rad;
    test_point[3]=z/rad;

    ival=icheck_bounds(E,test_point,rnode[1],rnode[2],rnode[3],rnode[4]);


    return ival;
}


/********* ICHECK CAP ***************************************/
/*                                                          */
/* This function serves to determine if a point lies within */
/* a given cap                                              */
/*                                                          */
int icheck_cap(E,icap,x,y,z,rad)
     struct All_variables *E;
     int icap;
     double x,y,z,rad;
{

    double test_point[4];
    double rnode[5][10];

    int ival;
    int kk;
    int icheck_bounds();

    /* surface coords of cap nodes */


    for (kk=1;kk<=4;kk++)
	{

	    rnode[kk][1]=E->trace.xcap[icap][kk];
	    rnode[kk][2]=E->trace.ycap[icap][kk];
	    rnode[kk][3]=E->trace.zcap[icap][kk];
	    rnode[kk][4]=E->trace.theta_cap[icap][kk];
	    rnode[kk][5]=E->trace.phi_cap[icap][kk];
	    rnode[kk][6]=E->trace.cos_theta[icap][kk];
	    rnode[kk][7]=E->trace.sin_theta[icap][kk];
	    rnode[kk][8]=E->trace.cos_phi[icap][kk];
	    rnode[kk][9]=E->trace.sin_phi[icap][kk];
	}


    /* test_point - project to outer radius */

    test_point[1]=x/rad;
    test_point[2]=y/rad;
    test_point[3]=z/rad;

    ival=icheck_bounds(E,test_point,rnode[1],rnode[2],rnode[3],rnode[4]);


    return ival;
}

/***** ICHECK BOUNDS ******************************/
/*                                                */
/* This function check if a test_point is bounded */
/* by 4 nodes                                     */
/* This is done by:                               */
/* 1) generate vectors from node to node          */
/* 2) generate vectors from each node to point    */
/*    in question                                 */
/* 3) for each node, take cross product of vector */
/*    pointing to it from previous node and       */
/*    vector from node to point in question       */
/* 4) Find radial components of all the cross     */
/*    products.                                   */
/* 5) If all radial components are positive,      */
/*    point is bounded by the 4 nodes             */
/* 6) If some radial components are negative      */
/*    point is on a boundary - adjust it an       */
/*    epsilon amount for this analysis only       */
/*    which will force it to lie in one element   */
/*    or cap                                      */

int icheck_bounds(E,test_point,rnode1,rnode2,rnode3,rnode4)
     struct All_variables *E;
     double *test_point;
     double *rnode1;
     double *rnode2;
     double *rnode3;
     double *rnode4;
{

    int number_of_tries=0;
    int icheck;

    double v12[4];
    double v23[4];
    double v34[4];
    double v41[4];
    double v1p[4];
    double v2p[4];
    double v3p[4];
    double v4p[4];
    double cross1[4];
    double cross2[4];
    double cross3[4];
    double cross4[4];
    double rad1,rad2,rad3,rad4;
    double theta, phi;
    double tiny, eps;
    double x,y,z;

    double findradial();
    void makevector();
    void crossit();
    double myatan();

    /* make vectors from node to node */

    makevector(v12,rnode2[1],rnode2[2],rnode2[3],rnode1[1],rnode1[2],rnode1[3]);
    makevector(v23,rnode3[1],rnode3[2],rnode3[3],rnode2[1],rnode2[2],rnode2[3]);
    makevector(v34,rnode4[1],rnode4[2],rnode4[3],rnode3[1],rnode3[2],rnode3[3]);
    makevector(v41,rnode1[1],rnode1[2],rnode1[3],rnode4[1],rnode4[2],rnode4[3]);

 try_again:

    number_of_tries++;

    /* make vectors from test point to node */

    makevector(v1p,test_point[1],test_point[2],test_point[3],rnode1[1],rnode1[2],rnode1[3]);
    makevector(v2p,test_point[1],test_point[2],test_point[3],rnode2[1],rnode2[2],rnode2[3]);
    makevector(v3p,test_point[1],test_point[2],test_point[3],rnode3[1],rnode3[2],rnode3[3]);
    makevector(v4p,test_point[1],test_point[2],test_point[3],rnode4[1],rnode4[2],rnode4[3]);

    /* Calculate cross products */

    crossit(cross2,v12,v2p);
    crossit(cross3,v23,v3p);
    crossit(cross4,v34,v4p);
    crossit(cross1,v41,v1p);

    /* Calculate radial component of cross products */

    rad1=findradial(E,cross1,rnode1[6],rnode1[7],rnode1[8],rnode1[9]);
    rad2=findradial(E,cross2,rnode2[6],rnode2[7],rnode2[8],rnode2[9]);
    rad3=findradial(E,cross3,rnode3[6],rnode3[7],rnode3[8],rnode3[9]);
    rad4=findradial(E,cross4,rnode4[6],rnode4[7],rnode4[8],rnode4[9]);

    /*  Check if any radial components is zero (along a boundary), adjust if so */
    /*  Hopefully, this doesn't happen often, may be expensive                  */

    tiny=1e-15;
    eps=1e-6;

    if (number_of_tries>3)
	{
	    fprintf(E->trace.fpt,"Error(icheck_bounds)-too many tries\n");
	    fprintf(E->trace.fpt,"Rads: %f %f %f %f\n",rad1,rad2,rad3,rad4);
	    fprintf(E->trace.fpt,"Test Point: %f %f %f  \n",test_point[1],test_point[2],test_point[3]);
	    fprintf(E->trace.fpt,"Nodal points: 1: %f %f %f\n",rnode1[1],rnode1[2],rnode1[3]);
	    fprintf(E->trace.fpt,"Nodal points: 2: %f %f %f\n",rnode2[1],rnode2[2],rnode2[3]);
	    fprintf(E->trace.fpt,"Nodal points: 3: %f %f %f\n",rnode3[1],rnode3[2],rnode3[3]);
	    fprintf(E->trace.fpt,"Nodal points: 4: %f %f %f\n",rnode4[1],rnode4[2],rnode4[3]);
	    fflush(E->trace.fpt);
	    exit(10);
	}

    if (fabs(rad1)<=tiny||fabs(rad2)<=tiny||fabs(rad3)<=tiny||fabs(rad4)<=tiny)
	{
	    x=test_point[1];
	    y=test_point[2];
	    z=test_point[3];
	    theta=myatan(sqrt(x*x+y*y),z);
	    phi=myatan(y,x);

	    if (theta<=M_PI/2.0)
		{
		    theta=theta+eps;
		}
	    else
		{
		    theta=theta-eps;
		}
	    phi=phi+eps;
	    x=sin(theta)*cos(phi);
	    y=sin(theta)*sin(phi);
	    z=cos(theta);
	    test_point[1]=x;
	    test_point[2]=y;
	    test_point[3]=z;

	    number_of_tries++;
	    goto try_again;

	}

    icheck=0;
    if (rad1>0.0&&rad2>0.0&&rad3>0.0&&rad4>0.0) icheck=1;

    /*
      fprintf(stderr,"%d: icheck: %d\n",E->parallel.me,icheck);
      fprintf(stderr,"%d: rads: %f %f %f %f\n",E->parallel.me,rad1,rad2,rad3,rad4);
    */

    return icheck;

}

/****************************************************************************/
/* FINDRADIAL                                                              */
/*                                                                          */
/* This function finds the radial component of a Cartesian vector           */


double findradial(E,vec,cost,sint,cosf,sinf)
     struct All_variables *E;
     double *vec;
     double cost,sint,cosf,sinf;
{
    double radialparti,radialpartj,radialpartk;
    double radial;

    radialparti=vec[1]*sint*cosf;
    radialpartj=vec[2]*sint*sinf;
    radialpartk=vec[3]*cost;

    radial=radialparti+radialpartj+radialpartk;


    return radial;
}


/******************MAKEVECTOR*********************************************************/

void makevector(vec,xf,yf,zf,x0,y0,z0)
     double *vec;
     double xf,yf,zf,x0,y0,z0;
{

    vec[1]=xf-x0;
    vec[2]=yf-y0;
    vec[3]=zf-z0;


    return;
}

/********************CROSSIT********************************************************/

void crossit(cross,A,B)
     double *cross;
     double *A;
     double *B;
{

    cross[1]=A[2]*B[3]-A[3]*B[2];
    cross[2]=A[3]*B[1]-A[1]*B[3];
    cross[3]=A[1]*B[2]-A[2]*B[1];


    return;
}


/************ FIX RADIUS ********************************************/
/* This function moves particles back in bounds if they left     */
/* during advection                                              */

static void fix_radius(struct All_variables *E,
                       double *radius, double *theta, double *phi,
                       double *x, double *y, double *z)
{
    double sint,cost,sinf,cosf,rad;
    double max_radius, min_radius;

    max_radius = E->sphere.ro - E->trace.box_cushion;
    min_radius = E->sphere.ri + E->trace.box_cushion;

    if (*radius > max_radius) {
        *radius=max_radius;
        rad=max_radius;
        cost=cos(*theta);
        sint=sqrt(1.0-cost*cost);
        cosf=cos(*phi);
        sinf=sin(*phi);
        *x=rad*sint*cosf;
        *y=rad*sint*sinf;
        *z=rad*cost;
    }
    if (*radius < min_radius) {
        *radius=min_radius;
        rad=min_radius;
        cost=cos(*theta);
        sint=sqrt(1.0-cost*cost);
        cosf=cos(*phi);
        sinf=sin(*phi);
        *x=rad*sint*cosf;
        *y=rad*sint*sinf;
        *z=rad*cost;
    }

    return;
}


/******************************************************************/
/* FIX PHI                                                        */
/*                                                                */
/* This function constrains the value of phi to be                */
/* between 0 and 2 PI                                             */
/*                                                                */

static void fix_phi(double *phi)
{
    const double two_pi=2.0*M_PI;

    double d2 = floor(*phi / two_pi);

    *phi -= two_pi * d2;

    return;
}

/******************************************************************/
/* FIX THETA PHI                                                  */
/*                                                                */
/* This function constrains the value of theta to be              */
/* between 0 and  PI, and                                         */
/* this function constrains the value of phi to be                */
/* between 0 and 2 PI                                             */
/*                                                                */
static void fix_theta_phi(double *theta, double *phi)
{
    const double two_pi=2.0*M_PI;
    double d, d2;

    d = floor(*theta/M_PI);

    *theta -= M_PI * d;
    *phi += M_PI * d;

    d2 = floor(*phi / two_pi);

    *phi -= two_pi * d2;

    return;
}

/********** IGET ELEMENT *****************************************/
/*                                                               */
/* This function returns the the real element for a given point. */
/* Returns -99 in not in this cap.                               */
/* iprevious_element, if known, is the last known element. If    */
/* it is not known, input a negative number.                     */

int iget_element(E,j,iprevious_element,x,y,z,theta,phi,rad)
     struct All_variables *E;
     int j;
     int iprevious_element;
     double x,y,z;
     double theta,phi,rad;
{

    int iregel;
    int iel;
    int ntheta,nphi;
    int ival;
    int ichoice;
    int kk;
    int ineighbor;
    int icorner[5];
    int elx,ely,elz,elxz;
    int ifinal_iel;
    int nelem;

    int iget_regel();
    int iquick_element_column_search();
    int icheck_cap();
    int icheck_regular_neighbors();
    int iget_radial_element();

    elx=E->lmesh.elx;
    ely=E->lmesh.ely;
    elz=E->lmesh.elz;


    ntheta=0;
    nphi=0;

    /* check the radial range */
    if (E->parallel.nprocz>1)
	{
	    ival=icheck_processor_shell(E,j,rad);
	    if (ival!=1) return -99;
	}

    /* First check previous element */

    /* do quick search to see if element can be easily found. */
    /* note that element may still be out of this cap, but    */
    /* it is probably fast to do a quick search before        */
    /* checking cap                                           */


    /* get regular element number */

    iregel=iget_regel(E,j,theta,phi,&ntheta,&nphi);
    if (iregel<=0)
	{
	    return -99;
	}


    /* AKMA put safety here or in make grid */

    if (E->trace.regtoel[j][0][iregel]==0)
	{
	    iel=E->trace.regtoel[j][1][iregel];
	    goto foundit;
	}

    /* first check previous element */

    if (iprevious_element>0)
	{
	    ival=icheck_element_column(E,j,iprevious_element,x,y,z,rad);
	    if (ival==1)
		{
		    iel=iprevious_element;
		    goto foundit;
		}
	}

    /* Check all regular mapping choices */

    ichoice=0;
    if (E->trace.regtoel[j][0][iregel]>0)
	{

	    ichoice=E->trace.regtoel[j][0][iregel];
	    for (kk=1;kk<=ichoice;kk++)
		{
		    nelem=E->trace.regtoel[j][kk][iregel];

		    if (nelem!=iprevious_element)
			{
			    ival=icheck_element_column(E,j,nelem,x,y,z,rad);
			    if (ival==1)
				{
				    iel=nelem;
				    goto foundit;
				}

			}
		}
	}

    /* If here, it means that tracer could not be found quickly with regular element map */

    /* First check previous element neighbors */

    if (iprevious_element>0)
	{
	    iel=icheck_column_neighbors(E,j,iprevious_element,x,y,z,rad);
	    if (iel>0)
		{
		    goto foundit;
		}
	}

    /* check if still in cap */

    ival=icheck_cap(E,0,x,y,z,rad);
    if (ival==0)
	{
	    return -99;
	}

    /* if here, still in cap (hopefully, without a doubt) */

    /* check cap corners (they are sometimes tricky) */

    elxz=elx*elz;
    icorner[1]=elz;
    icorner[2]=elxz;
    icorner[3]=elxz*(ely-1)+elz;
    icorner[4]=elxz*ely;
    for (kk=1;kk<=4;kk++)
	{
	    ival=icheck_element_column(E,j,icorner[kk],x,y,z,rad);
	    if (ival>0)
		{
		    iel=icorner[kk];
		    goto foundit;
		}
	}


    /* if previous element is not known, check neighbors of those tried in iquick... */

    if (iprevious_element<0)
	{
	    if (ichoice>0)
		{
		    for (kk=1;kk<=ichoice;kk++)
			{
			    ineighbor=E->trace.regtoel[j][kk][iregel];
			    iel=icheck_column_neighbors(E,j,ineighbor,x,y,z,rad);
			    if (iel>0)
				{
				    goto foundit;
				}
			}
		}

	    /* Decided to remove this part - not really needed and  complicated */
	    /*
	      else
	      {
	      iel=icheck_regular_neighbors(E,j,ntheta,nphi,x,y,z,theta,phi,rad);
	      if (iel>0)
	      {
	      goto foundit;
	      }
	      }
	    */
	}

    /* As a last resort, check all element columns */

    E->trace.istat1++;

    iel=icheck_all_columns(E,j,x,y,z,rad);

    /*
      fprintf(E->trace.fpt,"WARNING(iget_element)-doing a full search!\n");
      fprintf(E->trace.fpt,"  Most often means tracers have moved more than 1 element away\n");
      fprintf(E->trace.fpt,"  or regular element resolution is way too low.\n");
      fprintf(E->trace.fpt,"  COLUMN: %d \n",iel);
      fprintf(E->trace.fpt,"  PREVIOUS ELEMENT: %d \n",iprevious_element);
      fprintf(E->trace.fpt,"  x,y,z,theta,phi,rad: %f %f %f   %f %f %f\n",x,y,z,theta,phi,rad);
      fflush(E->trace.fpt);
      if (E->trace.itracer_warnings==1) exit(10);
    */

    if (E->trace.istat1%100==0)
	{
	    fprintf(E->trace.fpt,"Checked all elements %d times already this turn\n",E->trace.istat1);
	    fprintf(stderr,"Checked all elements %d times already this turn\n",E->trace.istat1);
	    fflush(E->trace.fpt);
	    fflush(stderr);
	}
    if (iel>0)
	{
	    goto foundit;
	}


    /* if still here, there is a problem */

    fprintf(E->trace.fpt,"Error(iget_element) - element not found\n");
    fprintf(E->trace.fpt,"x,y,z,theta,phi,iregel %f %f %f %f %f %d\n",
	    x,y,z,theta,phi,iregel);
    fflush(E->trace.fpt);
    exit(10);

 foundit:

    /* find radial element */

    ifinal_iel=iget_radial_element(E,j,iel,rad);

    return ifinal_iel;
}


/***** IGET RADIAL ELEMENT ***********************************/
/*                                                           */
/* This function returns the proper radial element, given    */
/* an element (iel) from the column.                         */

int iget_radial_element(E,j,iel,rad)
     struct All_variables *E;
     int j,iel;
     double rad;
{

    int elz=E->lmesh.elz;
    int ibottom_element;
    int iradial_element;
    int node;
    int kk;
    int idum;

    double top_rad;

    /* first project to the lowest element in the column */

    idum=(iel-1)/elz;
    ibottom_element=idum*elz+1;

    iradial_element=ibottom_element;

    for (kk=1;kk<=elz;kk++)
	{

	    node=E->ien[j][iradial_element].node[8];
	    top_rad=E->sx[j][3][node];

	    if (rad<top_rad) goto found_it;

	    iradial_element++;

	} /* end kk */


    /* should not be here */

    fprintf(E->trace.fpt,"Error(iget_radial_element)-out of range %f %d %d %d\n",rad,j,iel,ibottom_element);
    fflush(E->trace.fpt);
    exit(10);

 found_it:

    return iradial_element;
}

/****** ICHECK REGULAR NEIGHBORS *****************************/
/*                                                           */
/* This function searches the regular element neighborhood.  */

/* This function is no longer used!                          */

int icheck_regular_neighbors(E,j,ntheta,nphi,x,y,z,theta,phi,rad)
     struct All_variables *E;
     int j,ntheta,nphi;
     double x,y,z;
     double theta,phi,rad;
{

    int new_ntheta,new_nphi;
    int kk,pp;
    int iregel;
    int ival;
    int imap[5];
    int ichoice;
    int irange;

    int iquick_element_column_search();

    fprintf(E->trace.fpt,"ERROR(icheck_regular_neighbors)-this subroutine is no longer used !\n");
    fflush(E->trace.fpt);
    exit(10);

    irange=2;

    for (kk=-irange;kk<=irange;kk++)
	{
	    for (pp=-irange;pp<=irange;pp++)
		{
		    new_ntheta=ntheta+kk;
		    new_nphi=nphi+pp;
		    if ( (new_ntheta>0)&&(new_ntheta<=E->trace.numtheta[j])&&(new_nphi>0)&&(new_nphi<=E->trace.numphi[j]) )
			{
			    iregel=new_ntheta+(new_nphi-1)*E->trace.numtheta[j];
			    if ((iregel>0) && (iregel<=E->trace.numregel[j]))
				{
				    ival=iquick_element_column_search(E,j,iregel,new_ntheta,new_nphi,x,y,z,theta,phi,rad,imap,&ichoice);
				    if (ival>0) return ival;
				}
			}
		}
	}


    return -99;
}





/****** IQUICK ELEMENT SEARCH *****************************/
/*                                                        */
/* This function does a quick regular to real element     */
/* map check. Element number, if found, is returned.      */
/* Otherwise, -99 is returned.                            */
/* Pointers to imap and ichoice are used because they may */
/* prove to be convenient.                                */
/* This routine is no longer used                         */

int iquick_element_column_search(E,j,iregel,ntheta,nphi,x,y,z,theta,phi,rad,imap,ich)
     struct All_variables *E;
     int j,iregel;
     int ntheta,nphi;
     double x,y,z,theta,phi,rad;
     int *imap;
     int *ich;
{

    int iregnode[5];
    int kk,pp;
    int nel,ival;
    int ichoice;
    int icount;
    int itemp1;
    int itemp2;

    int icheck_element_column();

    fprintf(E->trace.fpt,"ERROR(iquick element)-this routine is no longer used!\n");
    fflush(E->trace.fpt);
    exit(10);

    /* REMOVE*/
    /*
      ichoice=*ich;

      fprintf(E->trace.fpt,"AA: ichoice: %d\n",ichoice);
      fflush(E->trace.fpt);
    */

    /* find regular nodes on regular element */

    /*
      iregnode[1]=iregel+(nphi-1);
      iregnode[2]=iregel+nphi;
      iregnode[3]=iregel+nphi+E->trace.numtheta[j]+1;
      iregnode[4]=iregel+nphi+E->trace.numtheta[j];
    */

    itemp1=iregel+nphi;
    itemp2=itemp1+E->trace.numtheta[j];

    iregnode[1]=itemp1-1;
    iregnode[2]=itemp1;
    iregnode[3]=itemp2+1;
    iregnode[4]=itemp2;

    for (kk=1;kk<=4;kk++)
	{
	    if ((iregnode[kk]<1) || (iregnode[kk]>E->trace.numregnodes[j]) )
		{
		    fprintf(E->trace.fpt,"ERROR(iquick)-weird regnode %d\n",iregnode[kk]);
		    fflush(E->trace.fpt);
		    exit(10);
		}
	}

    /* find number of choices */

    ichoice=0;
    icount=0;
    for (kk=1;kk<=4;kk++)
	{
	    if (E->trace.regnodetoel[j][iregnode[kk]]<=0) goto next_corner;

	    icount++;
	    for (pp=1;pp<=(kk-1);pp++)
		{
		    if (E->trace.regnodetoel[j][iregnode[kk]]==E->trace.regnodetoel[j][iregnode[pp]]) goto next_corner;
		}
	    ichoice++;
	    imap[ichoice]=E->trace.regnodetoel[j][iregnode[kk]];


	next_corner:
	    ;
	} /* end kk */

    *ich=ichoice;

    /* statistical counter */

    E->trace.istat_ichoice[j][ichoice]++;

    if (ichoice==0) return -99;

    /* Here, no check is performed if all 4 corners */
    /* lie within a given element.                  */
    /* It may be possible (not sure) but unlikely   */
    /* that the tracer is still not in that element */

    /* Decided to comment this out. */
    /* May not be valid for large regular grids. */
    /*
     */
    /* AKMA */

    if ((ichoice==1)&&(icount==4)) return imap[1];

    /* check others */

    for (kk=1;kk<=ichoice;kk++)
	{
	    nel=imap[kk];
	    ival=icheck_element_column(E,j,nel,x,y,z,rad);
	    if (ival>0) return nel;
	}

    /* if still here, no element was found */

    return -99;
}


/*********** IGET REGEL ******************************************/
/*                                                               */
/* This function returns the regular element in which a point    */
/* exists. If not found, returns -99.                            */
/* npi and ntheta are modified for later use                     */

int iget_regel(E,j,theta,phi,ntheta,nphi)
     struct All_variables *E;
     int j;
     double theta, phi;
     int *ntheta;
     int *nphi;
{

    int iregel;
    int idum;

    double rdum;

    /* first check whether theta is in range */

    if (theta<E->trace.thetamin[j]) return -99;
    if (theta>E->trace.thetamax[j]) return -99;

    /* get ntheta, nphi on regular mesh */

    rdum=theta-E->trace.thetamin[j];
    idum=rdum/E->trace.deltheta[j];
    *ntheta=idum+1;

    rdum=phi-E->trace.phimin[j];
    idum=rdum/E->trace.delphi[j];
    *nphi=idum+1;

    iregel=*ntheta+(*nphi-1)*E->trace.numtheta[j];

    /* check range to be sure */

    if (iregel>E->trace.numregel[j]) return -99;
    if (iregel<1) return -99;

    return iregel;
}


/****** EXPAND TRACER ARRAYS *****************************************/

void expand_tracer_arrays(E,j)
     struct All_variables *E;
     int j;

{

    int inewsize;
    int kk;
    int icushion;

    /* expand rtrac and itrac by 20% */

    icushion=100;

    inewsize=E->trace.itracsize[j]+E->trace.itracsize[j]/5+icushion;

    if ((E->trace.itrac[j]=(int *)realloc(E->trace.itrac[j],inewsize*sizeof(int)))==NULL)
	{
	    fprintf(E->trace.fpt,"ERROR(expand tracer arrays )-no memory (itracer)\n");
	    fflush(E->trace.fpt);
	    exit(10);
	}

    for (kk=0;kk<=((E->trace.number_of_advection_quantities)-1);kk++)
	{
	    if ((E->trace.rtrac[j][kk]=(double *)realloc(E->trace.rtrac[j][kk],inewsize*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(expand tracer arrays )-no memory (%d)\n",kk);
		    fflush(E->trace.fpt);
		    exit(10);
		}
	}

    for (kk=0;kk<=((E->trace.number_of_extra_quantities)-1);kk++)
	{
	    if ((E->trace.etrac[j][kk]=(double *)realloc(E->trace.etrac[j][kk],inewsize*sizeof(double)))==NULL)
		{
		    fprintf(E->trace.fpt,"ERROR(expand tracer arrays )-no memory 78 (%d)\n",kk);
		    fflush(E->trace.fpt);
		    exit(10);
		}
	}


    fprintf(E->trace.fpt,"Expanding physical memory of itrac, rtrac, and etrac to %d from %d\n",
	    inewsize,E->trace.itracsize[j]);

    E->trace.itracsize[j]=inewsize;

    return;
}

/****************************************************************/
/* DEFINE UV SPACE                                              */
/*                                                              */
/* This function defines nodal points as orthodrome coordinates */
/* u and v.  In uv space, great circles form straight lines.    */
/* This is used for interpolation method 1                      */
/* UV[j][1][node]=u                                             */
/* UV[j][2][node]=v                                             */

void define_uv_space(E)
     struct All_variables *E;

{

    int kk,j;
    int midnode;
    int numnodes,node;

    double u,v,cosc,theta,phi;
    double theta_f,phi_f;

    if (E->parallel.me==0) fprintf(stderr,"Setting up UV space\n");
    fflush(stderr);

    numnodes=E->lmesh.nno;

    /* open memory for uv space coords */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    for (kk=1;kk<=2;kk++)
		{
                    //TODO: allocate for surface nodes only to save memory
		    if ((E->trace.UV[j][kk]=(double *)malloc((numnodes+1)*sizeof(double)))==NULL)
			{
			    fprintf(E->trace.fpt,"Error(define uv)-not enough memory(a)\n");
			    fflush(E->trace.fpt);
			    exit(10);
			}
		}

	    /* uv space requires a reference point */
	    /* UV[j][1][0]=fixed theta */
	    /* UV[j][2][0]=fixed phi */


	    midnode=numnodes/2;

	    E->trace.UV[j][1][0]=E->sx[j][1][midnode];
	    E->trace.UV[j][2][0]=E->sx[j][2][midnode];

	    theta_f=E->sx[j][1][midnode];
	    phi_f=E->sx[j][2][midnode];

	    E->trace.cos_theta_f=cos(theta_f);
	    E->trace.sin_theta_f=sin(theta_f);

	    /* convert each nodal point to u and v */

	    for (node=1;node<=numnodes;node++)
		{
		    theta=E->sx[j][1][node];
		    phi=E->sx[j][2][node];

		    cosc=cos(theta_f)*cos(theta)+sin(theta_f)*sin(theta)*
			cos(phi-phi_f);
		    u=sin(theta)*sin(phi-phi_f)/cosc;
		    v=(sin(theta_f)*cos(theta)-cos(theta_f)*sin(theta)*cos(phi-phi_f))/cosc;

		    E->trace.UV[j][1][node]=u;
		    E->trace.UV[j][2][node]=v;

		}


	}/*end cap */

    return;
}

/***************************************************************/
/* DETERMINE SHAPE COEFFICIENTS                                */
/*                                                             */
/* An initialization function that determines the coeffiecients*/
/* to all element shape functions.                             */
/* This method uses standard linear shape functions of         */
/* triangular elements. (See Cuvelier, Segal, and              */
/* van Steenhoven, 1986). This is all in UV space.             */
/*                                                             */
/* shape_coefs[cap][wedge][3 shape functions*3 coefs][nelems]  */

void determine_shape_coefficients(E)
     struct All_variables *E;
{

    int j,nelem,iwedge,kk;
    int node;

    double u[5],v[5];
    double x1=0.0;
    double x2=0.0;
    double x3=0.0;
    double y1=0.0;
    double y2=0.0;
    double y3=0.0;
    double delta,a0,a1,a2;

    /* really only have to do this for each surface element, but */
    /* for simplicity, it is done for every element              */

    if (E->parallel.me==0) fprintf(stderr," Determining Shape Coefficients\n");
    fflush(stderr);

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{

	    /* first, allocate memory */

	    for(iwedge=1;iwedge<=2;iwedge++)
		{
		    for (kk=1;kk<=9;kk++)
			{
                            //TODO: allocate for surface elements only to save memory
			    if ((E->trace.shape_coefs[j][iwedge][kk]=
				 (double *)malloc((E->lmesh.nel+1)*sizeof(double)))==NULL)
				{
				    fprintf(E->trace.fpt,"ERROR(find shape coefs)-not enough memory(a)\n");
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}
		}

	    for (nelem=1;nelem<=E->lmesh.nel;nelem++)
		{

		    /* find u,v of local nodes at one radius  */

		    for(kk=1;kk<=4;kk++)
			{
			    node=E->IEN[E->mesh.levmax][j][nelem].node[kk];
			    u[kk]=E->trace.UV[j][1][node];
			    v[kk]=E->trace.UV[j][2][node];
			}

		    for(iwedge=1;iwedge<=2;iwedge++)
			{


			    if (iwedge==1)
				{
				    x1=u[1];
				    x2=u[2];
				    x3=u[3];
				    y1=v[1];
				    y2=v[2];
				    y3=v[3];
				}
			    if (iwedge==2)
				{
				    x1=u[1];
				    x2=u[3];
				    x3=u[4];
				    y1=v[1];
				    y2=v[3];
				    y3=v[4];
				}

			    /* shape function 1 */

			    delta=(x3-x2)*(y1-y2)-(y2-y3)*(x2-x1);
			    a0=(x2*y3-x3*y2)/delta;
			    a1=(y2-y3)/delta;
			    a2=(x3-x2)/delta;

			    E->trace.shape_coefs[j][iwedge][1][nelem]=a0;
			    E->trace.shape_coefs[j][iwedge][2][nelem]=a1;
			    E->trace.shape_coefs[j][iwedge][3][nelem]=a2;

			    /* shape function 2 */

			    delta=(x3-x1)*(y2-y1)-(y1-y3)*(x1-x2);
			    a0=(x1*y3-x3*y1)/delta;
			    a1=(y1-y3)/delta;
			    a2=(x3-x1)/delta;

			    E->trace.shape_coefs[j][iwedge][4][nelem]=a0;
			    E->trace.shape_coefs[j][iwedge][5][nelem]=a1;
			    E->trace.shape_coefs[j][iwedge][6][nelem]=a2;

			    /* shape function 3 */

			    delta=(x1-x2)*(y3-y2)-(y2-y1)*(x2-x3);
			    a0=(x2*y1-x1*y2)/delta;
			    a1=(y2-y1)/delta;
			    a2=(x1-x2)/delta;

			    E->trace.shape_coefs[j][iwedge][7][nelem]=a0;
			    E->trace.shape_coefs[j][iwedge][8][nelem]=a1;
			    E->trace.shape_coefs[j][iwedge][9][nelem]=a2;


			} /* end wedge */
		} /* end elem */
	} /* end cap */


    return;
}

/****************** GET CARTESIAN VELOCITY FIELD **************************************/
/*                                                                                    */
/* This function computes the cartesian velocity field from the spherical             */
/* velocity field computed from main Citcom code.                                     */

void get_cartesian_velocity_field(E)
     struct All_variables *E;
{

    int j,m,i;
    int kk;
    int lev = E->mesh.levmax;

    double sint,sinf,cost,cosf;
    double v_theta,v_phi,v_rad;
    double vx,vy,vz;

    static int been_here=0;

    if (been_here==0)
	{
	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (kk=1;kk<=3;kk++)
			{
			    if ((E->trace.V0_cart[j][kk]=(double *)malloc((E->lmesh.nno+1)*sizeof(double)))==NULL)
				{
				    fprintf(E->trace.fpt,"ERROR(get_cartesian_velocity)-no memory 82hd7\n");
				    fflush(E->trace.fpt);
				    exit(10);
				}
			}
		}

	    been_here++;
	}


    for (m=1;m<=E->sphere.caps_per_proc;m++)
	{

	    for (i=1;i<=E->lmesh.nno;i++)
		{
		    sint=E->SinCos[lev][m][0][i];
		    sinf=E->SinCos[lev][m][1][i];
		    cost=E->SinCos[lev][m][2][i];
		    cosf=E->SinCos[lev][m][3][i];

		    v_theta=E->sphere.cap[m].V[1][i];
		    v_phi=E->sphere.cap[m].V[2][i];
		    v_rad=E->sphere.cap[m].V[3][i];



		    vx=v_theta*cost*cosf-v_phi*sinf+v_rad*sint*cosf;
		    vy=v_theta*cost*sinf+v_phi*cosf+v_rad*sint*sinf;
		    vz=-v_theta*sint+v_rad*cost;

		    E->trace.V0_cart[m][1][i]=vx;
		    E->trace.V0_cart[m][2][i]=vy;
		    E->trace.V0_cart[m][3][i]=vz;
		}
	}

    return;
}

/*********** KEEP IN SPHERE *********************************************/
/*                                                                      */
/* This function makes sure the particle is within the sphere, and      */
/* phi and theta are within the proper degree range.                    */

void keep_in_sphere(E,x,y,z,theta,phi,rad)
     struct All_variables *E;
     double *x;
     double *y;
     double *z;
     double *theta;
     double *phi;
     double *rad;
{
    fix_theta_phi(theta, phi);
    fix_radius(E,rad,theta,phi,x,y,z);

    return;
}

/*********** CHECK SUM **************************************************/
/*                                                                      */
/* This functions checks to make sure number of tracers is preserved    */

void check_sum(E)
     struct All_variables *E;
{

    int number,iold_number;

    number=isum_tracers(E);

    iold_number=E->trace.ilast_tracer_count;

    if (number!=iold_number)
	{
	    fprintf(E->trace.fpt,"ERROR(check_sum)-break in conservation %d %d\n",
		    number,iold_number);
	    fflush(E->trace.fpt);
	    exit(10);
	}

    E->trace.ilast_tracer_count=number;

    return;
}

/************* ISUM TRACERS **********************************************/
/*                                                                       */
/* This function uses MPI to sum all tracers and returns number of them. */

static int isum_tracers(struct All_variables *E)
{


    int imycount;
    int iallcount;
    int num;
    int j;

    iallcount=0;

    imycount=0;
    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    imycount=imycount+E->trace.itrac[j][0];
	}

    MPI_Allreduce(&imycount,&iallcount,1,MPI_INT,MPI_SUM,E->parallel.world);

    num=iallcount;

    return num;
}



/* &&&&&&&&&&&&&&&&&&&& ANALYTICAL TESTS &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&**************/

/**************** ANALYTICAL TEST *********************************************************/
/*                                                                                        */
/* This function (and the 2 following) are used to test advection of tracers by assigning */
/* a test function (in "analytical_test_function").                                       */

void analytical_test(E)
     struct All_variables *E;

{

    int kk,pp;
    int nsteps;
    int j;
    int my_number,number;
    int nrunge_steps;
    int nrunge_refinement;

    double dt;
    double runge_dt;
    double theta,phi,rad;
    double time;
    double vel_s[4];
    double vel_c[4];
    double my_theta0,my_phi0,my_rad0;
    double my_thetaf,my_phif,my_radf;
    double theta0,phi0,rad0;
    double thetaf,phif,radf;
    double x0_s[4],xf_s[4];
    double x0_c[4],xf_c[4];
    double vec[4];
    double runge_path_length,runge_time;
    double x0,y0,z0;
    double xf,yf,zf;
    double difference;
    double difperpath;

    void analytical_test_function();
    void predict_tracers();
    void correct_tracers();
    void analytical_runge_kutte();
    void sphere_to_cart();


    fprintf(E->trace.fpt,"Starting Analytical Test\n");
    if (E->parallel.me==0) fprintf(stderr,"Starting Analytical Test\n");
    fflush(E->trace.fpt);
    fflush(stderr);

    /* Reset Box cushion to 0 */

    E->trace.box_cushion=0.0000;

    /* test paramters */

    nsteps=200;
    dt=0.0001;

    E->advection.timestep=dt;

    fprintf(E->trace.fpt,"steps: %d  dt: %f\n",nsteps,dt);

    /* Assign test velocity to Citcom nodes */

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    for (kk=1;kk<=E->lmesh.nno;kk++)
		{

		    theta=E->sx[j][1][kk];
		    phi=E->sx[j][2][kk];
		    rad=E->sx[j][3][kk];

		    analytical_test_function(E,theta,phi,rad,vel_s,vel_c);

		    E->sphere.cap[j].V[1][kk]=vel_s[1];
		    E->sphere.cap[j].V[2][kk]=vel_s[2];
		    E->sphere.cap[j].V[3][kk]=vel_s[3];
		}
	}

    time=0.0;

    my_theta0=0.0;
    my_phi0=0.0;
    my_rad0=0.0;
    my_thetaf=0.0;
    my_phif=0.0;
    my_radf=0.0;

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    if (E->trace.itrac[j][0]>10)
		{
		    fprintf(E->trace.fpt,"Warning(analytical)-too many tracers to print!\n");
		    fflush(E->trace.fpt);
		    if (E->trace.itracer_warnings==1) exit(10);
		}
	}

    /* print initial positions */

    E->monitor.solution_cycles=0;
    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    for (pp=1;pp<=E->trace.itrac[j][0];pp++)
		{
		    theta=E->trace.rtrac[j][0][pp];
		    phi=E->trace.rtrac[j][1][pp];
		    rad=E->trace.rtrac[j][2][pp];

		    fprintf(E->trace.fpt,"(%d) time: %f theta: %f phi: %f rad: %f\n",E->monitor.solution_cycles,time,theta,phi,rad);

		    if (pp==1) fprintf(stderr,"(%d) time: %f theta: %f phi: %f rad: %f\n",E->monitor.solution_cycles,time,theta,phi,rad);

		    if (pp==1)
			{
			    my_theta0=theta;
			    my_phi0=phi;
			    my_rad0=rad;
			}
		}
	}

    /* advect tracers */

    for (kk=1;kk<=nsteps;kk++)
	{
	    E->monitor.solution_cycles=kk;

	    time=time+dt;

	    predict_tracers(E);
	    correct_tracers(E);

	    for (j=1;j<=E->sphere.caps_per_proc;j++)
		{
		    for (pp=1;pp<=E->trace.itrac[j][0];pp++)
			{
			    theta=E->trace.rtrac[j][0][pp];
			    phi=E->trace.rtrac[j][1][pp];
			    rad=E->trace.rtrac[j][2][pp];

			    fprintf(E->trace.fpt,"(%d) time: %f theta: %f phi: %f rad: %f\n",E->monitor.solution_cycles,time,theta,phi,rad);

			    if (pp==1) fprintf(stderr,"(%d) time: %f theta: %f phi: %f rad: %f\n",E->monitor.solution_cycles,time,theta,phi,rad);

			    if ((kk==nsteps) && (pp==1))
				{
				    my_thetaf=theta;
				    my_phif=phi;
				    my_radf=rad;
				}
			}
		}

	}

    /* Get ready for comparison to Runge-Kutte (only works for one tracer) */

    fflush(E->trace.fpt);
    fflush(stderr);
    parallel_process_sync(E);

    fprintf(E->trace.fpt,"\n\nComparison to Runge-Kutte\n");
    if (E->parallel.me==0) fprintf(stderr,"Comparison to Runge-Kutte\n");

    for (j=1;j<=E->sphere.caps_per_proc;j++)
	{
	    my_number=E->trace.itrac[j][0];
	}

    MPI_Allreduce(&my_number,&number,1,MPI_INT,MPI_SUM,E->parallel.world);

    fprintf(E->trace.fpt,"Number of tracers: %d\n", number);
    if (E->parallel.me==0) fprintf(stderr,"Number of tracers: %d\n", number);

    /* if more than 1 tracer, exit */

    if (number!=1)
	{
	    fprintf(E->trace.fpt,"(Note: RK comparison only appropriate for one tracing particle (%d here) \n",number);
	    if (E->parallel.me==0) fprintf(stderr,"(Note: RK comparison only appropriate for one tracing particle (%d here) \n",number);
	    fflush(E->trace.fpt);
	    fflush(stderr);
	    parallel_process_termination();
	}


    /* communicate starting and final positions */

    MPI_Allreduce(&my_theta0,&theta0,1,MPI_DOUBLE,MPI_SUM,E->parallel.world);
    MPI_Allreduce(&my_phi0,&phi0,1,MPI_DOUBLE,MPI_SUM,E->parallel.world);
    MPI_Allreduce(&my_rad0,&rad0,1,MPI_DOUBLE,MPI_SUM,E->parallel.world);
    MPI_Allreduce(&my_thetaf,&thetaf,1,MPI_DOUBLE,MPI_SUM,E->parallel.world);
    MPI_Allreduce(&my_phif,&phif,1,MPI_DOUBLE,MPI_SUM,E->parallel.world);
    MPI_Allreduce(&my_radf,&radf,1,MPI_DOUBLE,MPI_SUM,E->parallel.world);

    x0_s[1]=theta0;
    x0_s[2]=phi0;
    x0_s[3]=rad0;

    nrunge_refinement=1000;

    nrunge_steps=nsteps*nrunge_refinement;
    runge_dt=dt/(1.0*nrunge_refinement);


    analytical_runge_kutte(E,nrunge_steps,runge_dt,x0_s,x0_c,xf_s,xf_c,vec);

    runge_time=vec[1];
    runge_path_length=vec[2];

    /* initial coordinates - both citcom and RK */

    x0=x0_c[1];
    y0=x0_c[2];
    z0=x0_c[3];

    /* convert final citcom coords into cartesian */

    sphere_to_cart(E,thetaf,phif,radf,&xf,&yf,&zf);

    difference=sqrt((xf-xf_c[1])*(xf-xf_c[1])+(yf-xf_c[2])*(yf-xf_c[2])+(zf-xf_c[3])*(zf-xf_c[3]));

    difperpath=difference/runge_path_length;

    /* Print out results */

    fprintf(E->trace.fpt,"Citcom calculation: steps: %d  dt: %f\n",nsteps,dt);
    fprintf(E->trace.fpt,"  (nodes per cap: %d x %d x %d)\n",E->lmesh.nox,E->lmesh.noy,(E->lmesh.noz-1)*E->parallel.nprocz+1);
    fprintf(E->trace.fpt,"                    starting position: theta: %f phi: %f rad: %f\n", theta0,phi0,rad0);
    fprintf(E->trace.fpt,"                    final position: theta: %f phi: %f rad: %f\n", thetaf,phif,radf);
    fprintf(E->trace.fpt,"                    (final time: %f) \n",time );

    fprintf(E->trace.fpt,"\n\nRunge-Kutte calculation: steps: %d  dt: %g\n",nrunge_steps,runge_dt);
    fprintf(E->trace.fpt,"                    starting position: theta: %f phi: %f rad: %f\n", theta0,phi0,rad0);
    fprintf(E->trace.fpt,"                    final position: theta: %f phi: %f rad: %f\n",xf_s[1],xf_s[2],xf_s[3]);
    fprintf(E->trace.fpt,"                    path length: %f \n",runge_path_length );
    fprintf(E->trace.fpt,"                    (final time: %f) \n",runge_time );

    fprintf(E->trace.fpt,"\n\n Difference between Citcom and RK: %e  (diff per path length: %e)\n\n",difference,difperpath);

    if (E->parallel.me==0)
	{
	    fprintf(stderr,"Citcom calculation: steps: %d  dt: %f\n",nsteps,dt);
	    fprintf(stderr,"  (nodes per cap: %d x %d x %d)\n",E->lmesh.nox,E->lmesh.noy,(E->lmesh.noz-1)*E->parallel.nprocz+1);
	    fprintf(stderr,"                    starting position: theta: %f phi: %f rad: %f\n", theta0,phi0,rad0);
	    fprintf(stderr,"                    final position: theta: %f phi: %f rad: %f\n", thetaf,phif,radf);
	    fprintf(stderr,"                    (final time: %f) \n",time );

	    fprintf(stderr,"\n\nRunge-Kutte calculation: steps: %d  dt: %f\n",nrunge_steps,runge_dt);
	    fprintf(stderr,"                    starting position: theta: %f phi: %f rad: %f\n", theta0,phi0,rad0);
	    fprintf(stderr,"                    final position: theta: %f phi: %f rad: %f\n",xf_s[1],xf_s[2],xf_s[3]);
	    fprintf(stderr,"                    path length: %f \n",runge_path_length );
	    fprintf(stderr,"                    (final time: %f) \n",runge_time );

	    fprintf(stderr,"\n\n Difference between Citcom and RK: %e  (diff per path length: %e)\n\n",difference,difperpath);

	}

    fflush(E->trace.fpt);
    fflush(stderr);

    return;
}

/*************** ANALYTICAL RUNGE KUTTE ******************/
/*                                                       */
void analytical_runge_kutte(E,nsteps,dt,x0_s,x0_c,xf_s,xf_c,vec)
     struct All_variables *E;
     int nsteps;
     double dt;
     double *x0_c;
     double *x0_s;
     double *xf_c;
     double *xf_s;
     double *vec;

{

    int kk;

    double x_0,y_0,z_0;
    double x_p,y_p,z_p;
    double x_c=0.0;
    double y_c=0.0;
    double z_c=0.0;
    double theta_0,phi_0,rad_0;
    double theta_p,phi_p,rad_p;
    double theta_c,phi_c,rad_c;
    double vel0_s[4],vel0_c[4];
    double velp_s[4],velp_c[4];
    double time;
    double path,dtpath;

    void sphere_to_cart();
    void cart_to_sphere();
    void analytical_test_function();

    theta_0=x0_s[1];
    phi_0=x0_s[2];
    rad_0=x0_s[3];

    sphere_to_cart(E,theta_0,phi_0,rad_0,&x_0,&y_0,&z_0);

    /* fill initial cartesian vector to send back */

    x0_c[1]=x_0;
    x0_c[2]=y_0;
    x0_c[3]=z_0;

    time=0.0;
    path=0.0;

    for (kk=1;kk<=nsteps;kk++)
	{

	    /* get velocity at initial position */

	    analytical_test_function(E,theta_0,phi_0,rad_0,vel0_s,vel0_c);

	    /* Find predicted midpoint position */

	    x_p=x_0+vel0_c[1]*dt*0.5;
	    y_p=y_0+vel0_c[2]*dt*0.5;
	    z_p=z_0+vel0_c[3]*dt*0.5;

	    /* convert to spherical */

	    cart_to_sphere(E,x_p,y_p,z_p,&theta_p,&phi_p,&rad_p);

	    /* get velocity at predicted midpoint position */

	    analytical_test_function(E,theta_p,phi_p,rad_p,velp_s,velp_c);

	    /* Find corrected position using midpoint velocity */

	    x_c=x_0+velp_c[1]*dt;
	    y_c=y_0+velp_c[2]*dt;
	    z_c=z_0+velp_c[3]*dt;

	    /* convert to spherical */

	    cart_to_sphere(E,x_c,y_c,z_c,&theta_c,&phi_c,&rad_c);

	    /* compute path lenght */

	    dtpath=sqrt((x_c-x_0)*(x_c-x_0)+(y_c-y_0)*(y_c-y_0)+(z_c-z_0)*(z_c-z_0));
	    path=path+dtpath;

	    time=time+dt;

	    x_0=x_c;
	    y_0=y_c;
	    z_0=z_c;

	    /* next time step */

	}

    /* fill final spherical and cartesian vectors to send back */

    xf_s[1]=theta_c;
    xf_s[2]=phi_c;
    xf_s[3]=rad_c;

    xf_c[1]=x_c;
    xf_c[2]=y_c;
    xf_c[3]=z_c;

    vec[1]=time;
    vec[2]=path;

    return;
}

/**************** ANALYTICAL TEST FUNCTION ******************/
/*                                                          */
/* vel_s[1] => velocity in theta direction                  */
/* vel_s[2] => velocity in phi direction                    */
/* vel_s[3] => velocity in radial direction                 */
/*                                                          */
/* vel_c[1] => velocity in x direction                      */
/* vel_c[2] => velocity in y direction                      */
/* vel_c[3] => velocity in z direction                      */

void analytical_test_function(E,theta,phi,rad,vel_s,vel_c)
     struct All_variables *E;
     double theta,phi,rad;
     double *vel_s;
     double *vel_c;

{

    double sint,sinf,cost,cosf;
    double v_theta,v_phi,v_rad;
    double vx,vy,vz;

    /* This is where the function is given in spherical */

    v_theta=50.0*rad*cos(phi);
    v_phi=100.0*rad*sin(theta);
    v_rad=25.0*rad;

    vel_s[1]=v_theta;
    vel_s[2]=v_phi;
    vel_s[3]=v_rad;

    /* Convert the function into cartesian */

    sint=sin(theta);
    sinf=sin(phi);
    cost=cos(theta);
    cosf=cos(phi);

    vx=v_theta*cost*cosf-v_phi*sinf+v_rad*sint*cosf;
    vy=v_theta*cost*sinf+v_phi*cosf+v_rad*sint*sinf;
    vz=-v_theta*sint+v_rad*cost;

    vel_c[1]=vx;
    vel_c[2]=vy;
    vel_c[3]=vz;

    return;
}


/******************* get_neighboring_caps ************************************/
/*                                                                           */
/* Communicate with neighboring processors to get their cap boundaries,      */
/* which is later used by icheck_cap()                                       */
/*                                                                           */

static void get_neighboring_caps(struct All_variables *E)
{
    void sphere_to_cart();

    const int ncorners = 4; /* # of top corner nodes */
    int i, j, n, d, kk, lev, idb;
    int num_ngb, neighbor_proc, tag;
    MPI_Status status[200];
    MPI_Request request[200];

    int node[ncorners];
    double xx[ncorners*3], rr[12][ncorners*3];
    int nox,noy,noz,dims;
    double x,y,z;
    double theta,phi,rad;

    dims=E->mesh.nsd;
    nox=E->lmesh.nox;
    noy=E->lmesh.noy;
    noz=E->lmesh.noz;

    node[0]=nox*noz*(noy-1)+noz;
    node[1]=noz;
    node[2]=noz*nox;
    node[3]=noz*nox*noy;

    lev = E->mesh.levmax;
    tag = 45;

    for (j=1; j<=E->sphere.caps_per_proc; j++) {

        /* loop over top corners to get their coordinates */
        n = 0;
        for (i=0; i<ncorners; i++) {
            for (d=0; d<dims; d++) {
                xx[n] = E->sx[j][d+1][node[i]];
                n++;
            }
        }

        idb = 0;
        num_ngb = E->parallel.TNUM_PASS[lev][j];
        for (kk=1; kk<=num_ngb; kk++) {
            neighbor_proc = E->parallel.PROCESSOR[lev][j].pass[kk];

            MPI_Isend(xx, n, MPI_DOUBLE, neighbor_proc,
                      tag, E->parallel.world, &request[idb]);
            idb++;

            MPI_Irecv(rr[kk], n, MPI_DOUBLE, neighbor_proc,
                      tag, E->parallel.world, &request[idb]);
            idb++;
        }

        /* Storing the current cap information */
	for (i=0; i<n; i++)
	    rr[0][i] = xx[i];

        /* Wait for non-blocking calls to complete */

        MPI_Waitall(idb, request, status);

        /* Storing the received cap information
         * XXX: this part assumes:
         *      1) E->sphere.caps_per_proc==1
         *      2) E->mesh.nsd==3
         */
        for (kk=0; kk<=num_ngb; kk++) {
            for (i=1; i<=ncorners; i++) {
                theta = rr[kk][(i-1)*dims];
                phi = rr[kk][(i-1)*dims+1];
                rad = rr[kk][(i-1)*dims+2];

                sphere_to_cart(E, theta, phi, rad, &x, &y, &z);

                E->trace.xcap[kk][i] = x;
                E->trace.ycap[kk][i] = y;
                E->trace.zcap[kk][i] = z;
                E->trace.theta_cap[kk][i] = theta;
                E->trace.phi_cap[kk][i] = phi;
                E->trace.rad_cap[kk][i] = rad;
                E->trace.cos_theta[kk][i] = cos(theta);
                E->trace.sin_theta[kk][i] = sin(theta);
                E->trace.cos_phi[kk][i] = cos(phi);
                E->trace.sin_phi[kk][i] = sin(phi);
            }
        } /* end kk, number of neighbors */

        /* debugging output */
        for (kk=0; kk<=num_ngb; kk++) {
            for (i=1; i<=ncorners; i++) {
                fprintf(E->trace.fpt, "pass=%d corner=%d sx=(%g, %g, %g)\n",
                        kk, i,
                        E->trace.theta_cap[kk][i],
                        E->trace.phi_cap[kk][i],
                        E->trace.rad_cap[kk][i]);
            }
        }
        fflush(E->trace.fpt);

    }

    return;
}


/**** PDEBUG ***********************************************************/
static void pdebug(struct All_variables *E, int i)
{

    fprintf(E->trace.fpt,"HERE (Before Sync): %d\n",i);
    fflush(E->trace.fpt);
    parallel_process_sync(E);
    fprintf(E->trace.fpt,"HERE (After Sync): %d\n",i);
    fflush(E->trace.fpt);

    return;
}
back to top