Beispiel #1
0
void smat_free(struct smat *mat)
{
	if (mat == NULL)
		return;

	free_2d_double(mat->data);
	free(mat);
}
/******************************************************************************
 * @brief    Compute the state variables (energy balance, water balance,
 *           and snow components) that are derived from the variables that
 *           are stored in state files.
 *****************************************************************************/
void
compute_derived_state_vars(all_vars_struct *all_vars,
                           soil_con_struct *soil_con,
                           veg_con_struct  *veg_con)
{
    extern global_param_struct global_param;
    extern option_struct       options;

    char                       FIRST_VEG;
    size_t                     Nveg;
    size_t                     veg;
    size_t                     lidx;
    size_t                     band;
    size_t                     tmpTshape[] = {
        options.Nlayer, options.Nnode,
        options.Nfrost + 1
    };
    size_t                     tmpZshape[] = {
        options.Nlayer, options.Nnode
    };
    int                        ErrorFlag;
    double                     Cv;
    double                     moist[MAX_VEG][MAX_BANDS][MAX_LAYERS];
    double                     dt_thresh;
    double                     tmp_runoff;
    double                  ***tmpT;
    double                   **tmpZ;

    cell_data_struct         **cell;
    energy_bal_struct        **energy;
    snow_data_struct         **snow;

    cell = all_vars->cell;
    energy = all_vars->energy;
    snow = all_vars->snow;
    Nveg = veg_con[0].vegetat_type_num;

    // allocate memory for tmpT and tmpZ
    malloc_3d_double(tmpTshape, &tmpT);
    malloc_2d_double(tmpZshape, &tmpZ);

    /******************************************
       Compute derived soil layer vars
    ******************************************/
    for (veg = 0; veg <= Nveg; veg++) {
        // Initialize soil for existing vegetation types
        Cv = veg_con[veg].Cv;

        if (Cv > 0) {
            for (band = 0; band < options.SNOW_BAND; band++) {
                // Initialize soil for existing snow elevation bands
                if (soil_con->AreaFract[band] > 0.) {
                    // set up temporary moist arrays
                    for (lidx = 0; lidx < options.Nlayer; lidx++) {
                        moist[veg][band][lidx] =
                            cell[veg][band].layer[lidx].moist;
                    }

                    // compute saturated area and water table
                    compute_runoff_and_asat(soil_con, moist[veg][band], 0,
                                            &(cell[veg][band].asat),
                                            &tmp_runoff);
                    wrap_compute_zwt(soil_con, &(cell[veg][band]));
                }
            }
        }
    }

    /******************************************
       Compute derived soil snow state vars
    ******************************************/
    for (veg = 0; veg <= Nveg; veg++) {
        for (band = 0; band < options.SNOW_BAND; band++) {
            if (snow[veg][band].density > 0.) {
                snow[veg][band].depth = CONST_RHOFW * snow[veg][band].swq /
                                        snow[veg][band].density;
            }
        }
    }

    /******************************************
       Compute soil thermal node properties
    ******************************************/
    FIRST_VEG = true;
    for (veg = 0; veg <= Nveg; veg++) {
        // Initialize soil for existing vegetation types
        Cv = veg_con[veg].Cv;

        if (Cv > 0) {
            for (band = 0; band < options.SNOW_BAND; band++) {
                // Initialize soil for existing snow elevation bands
                if (soil_con->AreaFract[band] > 0.) {
                    /** Set soil properties for all soil nodes **/
                    if (FIRST_VEG) {
                        FIRST_VEG = false;
                        set_node_parameters(soil_con->Zsum_node,
                                            soil_con->max_moist_node,
                                            soil_con->expt_node,
                                            soil_con->bubble_node,
                                            soil_con->alpha, soil_con->beta,
                                            soil_con->gamma, soil_con->depth,
                                            soil_con->max_moist, soil_con->expt,
                                            soil_con->bubble,
                                            options.Nnode, options.Nlayer);
                    }

                    // set soil moisture properties for all soil thermal nodes
                    if (options.FULL_ENERGY || options.FROZEN_SOIL) {
                        ErrorFlag =
                            distribute_node_moisture_properties(
                                energy[veg][band].moist,
                                energy[veg][band].ice,
                                energy[veg][band].kappa_node,
                                energy[veg][band].Cs_node,
                                soil_con->Zsum_node,
                                energy[veg][band].T,
                                soil_con->max_moist_node,
                                soil_con->expt_node,
                                soil_con->bubble_node,
                                moist[veg][band],
                                soil_con->depth,
                                soil_con->soil_dens_min,
                                soil_con->bulk_dens_min,
                                soil_con->quartz,
                                soil_con->soil_density,
                                soil_con->bulk_density,
                                soil_con->organic,
                                options.Nnode, options.Nlayer,
                                soil_con->FS_ACTIVE);
                        if (ErrorFlag == ERROR) {
                            log_err("Error setting physical properties for "
                                    "soil thermal nodes");
                        }
                    }

                    // Check node spacing v time step
                    // (note this is only approximate since heat capacity and
                    // conductivity can change considerably during the
                    // simulation depending on soil moisture and ice content)
                    if ((options.FROZEN_SOIL &&
                         !options.QUICK_FLUX) && !options.IMPLICIT) {
                        // in seconds
                        dt_thresh = 0.5 * energy[veg][band].Cs_node[1] /
                                    energy[veg][band].kappa_node[1] *
                                    pow((soil_con->dz_node[1]),
                                        2);
                        if (global_param.dt > dt_thresh) {
                            log_err("You are currently running FROZEN SOIL "
                                    "with an explicit method (IMPLICIT is "
                                    "set to FALSE).  For the explicit method "
                                    "to be stable, time step %f seconds is too "
                                    "large for the given thermal node spacing "
                                    "%f m, soil heat capacity %f J/m3/K, and "
                                    "soil thermal conductivity %f J/m/s/K.  "
                                    "Either set IMPLICIT to TRUE in your "
                                    "global parameter file (this is the "
                                    "recommended action), or decrease time "
                                    "step length to <= %f seconds, or decrease "
                                    "the number of soil thermal nodes.",
                                    global_param.dt,
                                    soil_con->dz_node[1],
                                    energy[veg][band].Cs_node[1],
                                    energy[veg][band].kappa_node[1], dt_thresh);
                        }
                    }

                    /* calculate soil layer temperatures  */
                    if (options.QUICK_FLUX) {
                        ErrorFlag =
                            estimate_layer_temperature_quick_flux(
                                cell[veg][band].layer,
                                soil_con->depth, soil_con->dp,
                                energy[veg][band].T[0],
                                energy[veg][band].T[1],
                                soil_con->avg_temp);
                        if (ErrorFlag == ERROR) {
                            log_err("Error calculating layer temperature "
                                    "using QUICK_FLUX option");
                        }
                    }
                    else {
                        estimate_frost_temperature_and_depth(
                            tmpT,
                            tmpZ,
                            soil_con->Zsum_node,
                            energy[veg][band].T,
                            soil_con->depth,
                            soil_con->frost_fract,
                            soil_con->frost_slope,
                            options.Nnode,
                            options.Nlayer);
                        ErrorFlag = estimate_layer_temperature(
                            cell[veg][band].layer,
                            tmpT,
                            tmpZ,
                            soil_con->Zsum_node,
                            soil_con->depth,
                            options.Nnode,
                            options.Nlayer);
                        if (ErrorFlag == ERROR) {
                            log_err("Error calculating layer temperature");
                        }
                    }

                    /* Find freezing and thawing front depths */
                    if (!options.QUICK_FLUX && soil_con->FS_ACTIVE) {
                        find_0_degree_fronts(&energy[veg][band],
                                             soil_con->Zsum_node,
                                             energy[veg][band].T,
                                             options.Nnode);
                    }
                }
            }
        }
    }
    // free memory for tmpT and tmpZ
    free_3d_double(tmpTshape, tmpT);
    free_2d_double(tmpZshape, tmpZ);
}
void ellipsetrack(avi_t *video, double *xc0, double *yc0, int Nc, int R, int Np, int Nf) {
    /*
    % ELLIPSETRACK tracks cells in the movie specified by 'video', at
    %  locations 'xc0'/'yc0' with radii R using an ellipse with Np discrete
    %  points, starting at frame number one and stopping at frame number 'Nf'.
    %
    % INPUTS:
    %   video.......pointer to avi video object
    %   xc0,yc0.....initial center location (Nc entries)
    %   Nc..........number of cells
    %   R...........initial radius
    %   Np..........nbr of snaxels points per snake
    %   Nf..........nbr of frames in which to track
    %
    % Matlab code written by: DREW GILLIAM (based on code by GANG DONG /
    %                                                        NILANJAN RAY)
    % Ported to C by: MICHAEL BOYER
    */

    int i, j;

    // Compute angle parameter
    double *t = (double *) malloc(sizeof(double) * Np);
    double increment = (2.0 * PI) / (double) Np;
    for (i = 0; i < Np; i++) {
        t[i] =  increment * (double) i ;
    }

    // Allocate space for a snake for each cell in each frame
    double **xc = alloc_2d_double(Nc, Nf + 1);
    double **yc = alloc_2d_double(Nc, Nf + 1);
    double ***r = alloc_3d_double(Nc, Np, Nf + 1);
    double ***x = alloc_3d_double(Nc, Np, Nf + 1);
    double ***y = alloc_3d_double(Nc, Np, Nf + 1);

    // Save the first snake for each cell
    for (i = 0; i < Nc; i++) {
        xc[i][0] = xc0[i];
        yc[i][0] = yc0[i];
        for (j = 0; j < Np; j++) {
            r[i][j][0] = (double) R;
        }
    }

    // Generate ellipse points for each cell
    for (i = 0; i < Nc; i++) {
        for (j = 0; j < Np; j++) {
            x[i][j][0] = xc[i][0] + (r[i][j][0] * cos(t[j]));
            y[i][j][0] = yc[i][0] + (r[i][j][0] * sin(t[j]));
        }
    }

    // Keep track of the total time spent on computing
    //  the MGVF matrix and evolving the snakes
    long long  MGVF_time = 0;
    long long snake_time = 0;


    // Process each frame
    int frame_num, cell_num;
    for (frame_num = 1; frame_num <= Nf; frame_num++) {
        printf("\rProcessing frame %d / %d", frame_num, Nf);
        fflush(stdout);

        // Get the current video frame and its dimensions
        MAT *I = get_frame(video, frame_num, 0, 1);
        int Ih = I->m;
        int Iw = I->n;

        // Set the current positions equal to the previous positions
        for (i = 0; i < Nc; i++) {
            xc[i][frame_num] = xc[i][frame_num - 1];
            yc[i][frame_num] = yc[i][frame_num - 1];
            for (j = 0; j < Np; j++) {
                r[i][j][frame_num] = r[i][j][frame_num - 1];
            }
        }

        // Split the work among multiple threads, if OPEN is defined
#ifdef OPEN
        #pragma omp parallel for num_threads(omp_num_threads) private(i, j)
#endif
        // Track each cell
        for (cell_num = 0; cell_num < Nc; cell_num++) {
            // Make copies of the current cell's location
            double xci = xc[cell_num][frame_num];
            double yci = yc[cell_num][frame_num];
            double *ri = (double *) malloc(sizeof(double) * Np);
            for (j = 0; j < Np; j++) {
                ri[j] = r[cell_num][j][frame_num];
            }

            // Add up the last ten y-values for this cell
            //  (or fewer if there are not yet ten previous frames)
            double ycavg = 0.0;
            for (i = (frame_num > 10 ? frame_num - 10 : 0); i < frame_num; i++) {
                ycavg += yc[cell_num][i];
            }
            // Compute the average of the last ten y-values
            //  (this represents the expected y-location of the cell)
            ycavg = ycavg / (double) (frame_num > 10 ? 10 : frame_num);

            // Determine the range of the subimage surrounding the current position
            int u1 = max(xci - 4.0 * R + 0.5, 0 );
            int u2 = min(xci + 4.0 * R + 0.5, Iw - 1);
            int v1 = max(yci - 2.0 * R + 1.5, 0 );
            int v2 = min(yci + 2.0 * R + 1.5, Ih - 1);

            // Extract the subimage
            MAT *Isub = m_get(v2 - v1 + 1, u2 - u1 + 1);
            for (i = v1; i <= v2; i++) {
                for (j = u1; j <= u2; j++) {
                    m_set_val(Isub, i - v1, j - u1, m_get_val(I, i, j));
                }
            }

            // Compute the subimage gradient magnitude
            MAT *Ix = gradient_x(Isub);
            MAT *Iy = gradient_y(Isub);
            MAT *IE = m_get(Isub->m, Isub->n);
            for (i = 0; i < Isub->m; i++) {
                for (j = 0; j < Isub->n; j++) {
                    double temp_x = m_get_val(Ix, i, j);
                    double temp_y = m_get_val(Iy, i, j);
                    m_set_val(IE, i, j, sqrt((temp_x * temp_x) + (temp_y * temp_y)));
                }
            }

            // Compute the motion gradient vector flow (MGVF) edgemaps
            long long MGVF_start_time = get_time();
            MAT *IMGVF = MGVF(IE, 1, 1);
            MGVF_time += get_time() - MGVF_start_time;

            // Determine the position of the cell in the subimage
            xci = xci - (double) u1;
            yci = yci - (double) (v1 - 1);
            ycavg = ycavg - (double) (v1 - 1);

            // Evolve the snake
            long long snake_start_time = get_time();
            ellipseevolve(IMGVF, &xci, &yci, ri, t, Np, (double) R, ycavg);
            snake_time += get_time() - snake_start_time;

            // Compute the cell's new position in the full image
            xci = xci + u1;
            yci = yci + (v1 - 1);

            // Store the new location of the cell and the snake
            xc[cell_num][frame_num] = xci;
            yc[cell_num][frame_num] = yci;
            for (j = 0; j < Np; j++) {
                r[cell_num][j][frame_num] = ri[j];
                x[cell_num][j][frame_num] = xc[cell_num][frame_num] + (ri[j] * cos(t[j]));
                y[cell_num][j][frame_num] = yc[cell_num][frame_num] + (ri[j] * sin(t[j]));
            }

            // Output the updated center of each cell
            //printf("%d,%f,%f\n", cell_num, xci[cell_num], yci[cell_num]);

            // Free temporary memory
            m_free(IMGVF);
            free(ri);
        }

        // Output a new line to visually distinguish the output from different frames
        //printf("\n");
    }

    // Free temporary memory
    free(t);
    free_2d_double(xc);
    free_2d_double(yc);
    free_3d_double(r);
    free_3d_double(x);
    free_3d_double(y);

    // Report average processing time per frame
    printf("\n\nTracking runtime (average per frame):\n");
    printf("------------------------------------\n");
    printf("MGVF computation: %.5f seconds\n", ((float) (MGVF_time)) / (float) (1000*1000*Nf));
    printf(" Snake evolution: %.5f seconds\n", ((float) (snake_time)) / (float) (1000*1000*Nf));
}
int main(int argc, char ** argv)
{
	/* ----------- VARIABLE DECLARATIONS ----------- */

	/* Size of matrix - the matrix will be n x n */
	int n;

	/* Number of processes and rank of this process*/
	int nprocs, rank;
	
	/* Size and periodicity of cartesian grid */
	int dim_size[2];
	int periods[2];
	
	/* Number of processes in x and y directions */
	int npx, npy;
	
	/* Offset for this process in x and y directions */
	int x_offset, y_offset;
	
	/* The standard (normal) number of rows and cols
	per core - not valid for the last in a row or col */
	int std_rows_in_core, std_cols_in_core;
	
	/* Coordinates in cartesian grid, and num of rows and cols
	this process has */
	int coords[2];
	int rows_in_core;
	int cols_in_core;
	
	/* Array to store the chunk of the matrix that we have */
	double **array;
	double **new_array;
	
	
	/* Loop variables */
	int i, j;
	
	/* Cartesian communicator */
	MPI_Comm cart_comm;
	
	int coords2[2];
	int rank2;

	int global_x1, global_x2;
	int global_y1, global_y2;

	struct global_index gi;
	struct local_index li;
	
	MPI_Request send_request;
	MPI_Request recv_request;
	
	/* Stores the number of requests this core will need to keep track of
	and creates a pointer to eventually hold the array of these requests */
	int n_requests;
	MPI_Request *all_requests;
	
	int request_counter;
	
	int tag;
	
	/* Determines whether the final result should be printed or not */
	int print = 0;
	
	/* Holds start and end times for the transpose */
	double tstart, tend, walltime;
	
	/* ----------- PROCESS CMD LINE ARGUMENTS ----------- */
	
	/* If there isn't one command-line argument (argc also counts the program name)
	then print an error message and exit.
	Only do this if we're on process 0 - otherwise nprocs error messages will be printed!*/
	if (argc < 2 && rank == 0)
	{
		printf("You must specify the size of the matrix (n) as a command-line argument\n");
		printf("Exiting\n");
		MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
		return EXIT_FAILURE;
	}
	
	/* As we've got here, there must be at least one command-line argument, so try and convert it to an
	integer */
	n = atoi(argv[1]);
	
	/* Process other command line arguments */
	/* Process command line arguments checking them and running the appropriate function */
	for (i = 2; i < argc; i++)  /* Skip argv[0] (program name) and argv[1] (size of matrix)*/
    {
    	/* if strcmp returns 0 then the strings are identical */
        if (strcmp(argv[i], "print") == 0)
        {
            print = 1;
        }

    }	
	
	/* If atoi returns zero it is either because it couldn't convert the string to an integer,
	or the integer given was 0. In either case, we must print an error message because it is a
	nonsensical size for the matrix */
	if (n == 0)
	{
		printf("You must an integer > 0 as the size of the matrix\n");
		printf("Exiting\n");
		MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
		return EXIT_FAILURE;
	}
	
	/* ----------- MPI Setup ----------- */

	/* Initialise MPI */
	MPI_Init(&argc, &argv);

	/* Get the rank for this process, and the number of processes */
	MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	
	/* Work out how big the grid should be, given the number of processes */
	factorise( nprocs, &npx, &npy );
	
	/* Create the cartesian communicator */
	dim_size[0] = npy;
	dim_size[1] = npx;
	periods[0] = 1;
	periods[1] = 1;
	
	MPI_Cart_create(MPI_COMM_WORLD, 2, dim_size, periods, 1, &cart_comm);
	
	/* Get our co-ordinates within that communicator */
	MPI_Cart_coords(cart_comm, rank, 2, coords);
		
	rows_in_core = ceil(n / (float) npx);
	cols_in_core = ceil(n / (float) npy);
	
	std_rows_in_core = rows_in_core;
	std_cols_in_core = cols_in_core;
		
	if (coords[0] == (npy - 1))
	{
		/* We're at the far end of a row */
		cols_in_core = n - (cols_in_core * (npy - 1));
	}
	if (coords[1] == (npx - 1))
	{
		/* We're at the bottom of a col */
		rows_in_core = n - (rows_in_core * (npx - 1));
	}
	
	/* Calculate the number of individual communications that are needed to transpose the matrix.
	We know that this will be the number of requests we need to store, so malloc some memory for the
	array of the right size. Each process has its own requests array - so it's simply 2 * ncols * nrows
	*/
	n_requests = 2 * rows_in_core * cols_in_core;
	all_requests = malloc(n_requests * sizeof(MPI_Request));
	
	/* ----------- INITIALISE MATRIX CHUNKS ----------- */
	
	/* Allocate an array to store this chunk of the matrix.
	If the allocation fails, print an error */
	array = alloc_2d_double(rows_in_core, cols_in_core);
	new_array = alloc_2d_double(rows_in_core, cols_in_core);
	if (array == NULL || new_array == NULL)
	{
		printf("Problem with array allocation.\nExiting\n");
		return 1;
	}
	
	for (i = 0; i < rows_in_core; i++)
	{
		for (j = 0; j < cols_in_core; j++)
		{
			new_array[i][j] = NAN;
		}
	}
	
	/* Calculate the offset of the top left-hand corner of our chunk of the matrix from the
	top left-hand corner of the whole matrix */
	x_offset = coords[0] * std_cols_in_core;
	y_offset = coords[1] * std_rows_in_core;
	
	for (i = 0; i < rows_in_core; i++)
	{
		/*printf("Process (%d, %d): ", coords[0], coords[1]);*/
		for (j = 0; j < cols_in_core; j++)
		{
			array[i][j] = (float) ( (i + y_offset) * n) + ( (j + x_offset) + 1);
			
			/*printf("%f ", array[i][j]);*/
		}
		/*printf("\n");*/
	}
	
	MPI_Barrier(MPI_COMM_WORLD);
	
	/* ----------- DO TRANSPOSE ----------- */
	/* Record the start time, making sure all processes have got there at the same time using
	a barrier */
	MPI_Barrier(cart_comm);
	tstart = timer();
		
	request_counter = 0;
	
	for (i = 0; i < rows_in_core; i++)
	{
		for (j = 0; j < cols_in_core; j++)
		{
			/* For each item in this chunk of the array:
			
			Send it to the opposite index
			Receive from the opposite index */
						
			/* Calculate the opposite index */
			gi = local_to_global(coords[0], coords[1], j, i, std_cols_in_core, std_rows_in_core);
						
			/* The two global indices for swapping */
			global_x1 = gi.x;
			global_y1 = gi.y;
			
			global_x2 = global_y1;
			global_y2 = global_x1;
			

			/* We know which rank one of them is (as we're running as it!)
			but we need to calculate the rank of the other which involves:
			1. Getting the local index of the swapped global index
			2. Converting the X, Y process co-ordinates to a rank */
			li = global_to_local(global_x2, global_y2, std_cols_in_core, std_rows_in_core);
						
			coords2[0] = li.procX;
			coords2[1] = li.procY;
						
			MPI_Cart_rank(cart_comm, coords2, &rank2);
			
			tag = (global_x2 + 1) * (global_y2 + 1);
						
			/*printf("Swapping\n");
			printf("Value = %f\n", array[i][j]);
			printf("(%f) From global (%d, %d) - Processor (%d, %d) with local (%d, %d)\n", array[i][j], global_x1, global_y1, coords[0], coords[1], j, i);
			printf("(%f) To global (%d, %d)- Processor (%d, %d) with local (%d, %d)\n", array[i][j], global_x2, global_y2, coords2[0], coords2[1], li.arrayX, li.arrayY);
			printf("(%f) Going to rank %d\n", array[i][j], rank2); */
			
			MPI_Isend(&array[i][j], 1, MPI_DOUBLE, rank2, (global_y2 * n) + global_x2, MPI_COMM_WORLD, &send_request);

			MPI_Irecv(&new_array[i][j], 1, MPI_DOUBLE, rank2, (global_x2 * n) + global_y2, MPI_COMM_WORLD, &recv_request);
			all_requests[request_counter] = send_request;
			request_counter++;
			all_requests[request_counter] = recv_request;
			request_counter++;
		}
	}
	MPI_Waitall(request_counter, all_requests, MPI_STATUSES_IGNORE);

	/* Store end time (using barrier before-hand to make the calculated times for all of the
	processes almost exactly equal, meaning we can just take the time from one process
	for output */
	MPI_Barrier(cart_comm);
	tend = timer();	
	
	/* ----------- PRINT OUTPUT AND FINALISE ENVIRONMENT ----------- */
		
	walltime = tend - tstart;
	 
	if (print == 1)
	{
		for (i = 0; i < rows_in_core; i++)
		{
			printf("Result at Process (%d, %d): ", coords[0], coords[1]);
			for (j = 0; j < cols_in_core; j++)
			{			
				printf("%f ", new_array[i][j]);
			}
			printf("\n");
		}
	}
	
	if (rank == 0)
	{
			printf("%d\t%d\t%f\n", nprocs, n, walltime);
	}
	
	/* Free the memory we grabbed for the requests array */
	free(all_requests);
	
	/* Free the memory we grabbed earlier for the data arrays */
	free_2d_double(array);
	free_2d_double(new_array);
	
	/* Close down the MPI environment */
	MPI_Finalize();
	
	return EXIT_SUCCESS;
}
void ellipsetrack(avi_t *video, double *xc0, double *yc0, int Nc, int R, int Np, int Nf) {
	/*
	% ELLIPSETRACK tracks cells in the movie specified by 'video', at
	%  locations 'xc0'/'yc0' with radii R using an ellipse with Np discrete
	%  points, starting at frame number one and stopping at frame number 'Nf'.
	%
	% INPUTS:
	%   video.......pointer to avi video object
	%   xc0,yc0.....initial center location (Nc entries)
	%   Nc..........number of cells
	%   R...........initial radius
	%   Np..........number of snaxels points per snake
	%   Nf..........number of frames in which to track
	%
	% Matlab code written by: DREW GILLIAM (based on code by GANG DONG /
	%                                                        NILANJAN RAY)
	% Ported to C by: MICHAEL BOYER
	*/
	
	// Compute angle parameter
	double *t = (double *) malloc(sizeof(double) * Np);
	double increment = (2.0 * PI) / (double) Np;
	int i, j;
	for (i = 0; i < Np; i++) {
		t[i] =  increment * (double) i ;
	}

	// Allocate space for a snake for each cell in each frame
	double **xc = alloc_2d_double(Nc, Nf + 1);
	double **yc = alloc_2d_double(Nc, Nf + 1);
	double ***r = alloc_3d_double(Nc, Np, Nf + 1);
	double ***x = alloc_3d_double(Nc, Np, Nf + 1);
	double ***y = alloc_3d_double(Nc, Np, Nf + 1);
	
	// Save the first snake for each cell
	for (i = 0; i < Nc; i++) {
		xc[i][0] = xc0[i];
		yc[i][0] = yc0[i];
		for (j = 0; j < Np; j++) {
			r[i][j][0] = (double) R;
		}
	}
	
	// Generate ellipse points for each cell
	for (i = 0; i < Nc; i++) {
		for (j = 0; j < Np; j++) {
			x[i][j][0] = xc[i][0] + (r[i][j][0] * cos(t[j]));
			y[i][j][0] = yc[i][0] + (r[i][j][0] * sin(t[j]));
		}
	}
	
	// Allocate arrays so we can break up the per-cell for loop below
	double *xci = (double *) malloc(sizeof(double) * Nc);
	double *yci = (double *) malloc(sizeof(double) * Nc);
	double **ri = alloc_2d_double(Nc, Np);
	double *ycavg = (double *) malloc(sizeof(double) * Nc);
	int *u1 = (int *) malloc(sizeof(int) * Nc);
	int *u2 = (int *) malloc(sizeof(int) * Nc);
	int *v1 = (int *) malloc(sizeof(int) * Nc);
	int *v2 = (int *) malloc(sizeof(int) * Nc);
	MAT **Isub = (MAT **) malloc(sizeof(MAT *) * Nc);
	MAT **Ix = (MAT **) malloc(sizeof(MAT *) * Nc);
	MAT **Iy = (MAT **) malloc(sizeof(MAT *) * Nc);
	MAT **IE = (MAT **) malloc(sizeof(MAT *) * Nc);
	
	// Keep track of the total time spent on computing
	//  the MGVF matrix and evolving the snakes
	long long  MGVF_time = 0;
	long long snake_time = 0;
	
	
	// Process each frame sequentially
	int frame_num;
	for (frame_num = 1; frame_num <= Nf; frame_num++) {	 
		printf("\rProcessing frame %d / %d", frame_num, Nf);
		fflush(stdout);
		
		// Get the current video frame and its dimensions
		MAT *I = get_frame(video, frame_num, 0, 1);
		int Ih = I->m;
		int Iw = I->n;
	    
	    // Initialize the current positions to be equal to the previous positions		
		for (i = 0; i < Nc; i++) {
			xc[i][frame_num] = xc[i][frame_num - 1];
			yc[i][frame_num] = yc[i][frame_num - 1];
			for (j = 0; j < Np; j++) {
				r[i][j][frame_num] = r[i][j][frame_num - 1];
			}
		}
		
		// Sequentially extract the subimage near each cell
		int cell_num;
		for (cell_num = 0; cell_num < Nc; cell_num++) {
			// Make copies of the current cell's location
			xci[cell_num] = xc[cell_num][frame_num];
			yci[cell_num] = yc[cell_num][frame_num];
			for (j = 0; j < Np; j++) {
				ri[cell_num][j] = r[cell_num][j][frame_num];
			}
			
			// Add up the last ten y values for this cell
			//  (or fewer if there are not yet ten previous frames)
			ycavg[cell_num] = 0.0;
			for (i = (frame_num > 10 ? frame_num - 10 : 0); i < frame_num; i++) {
				ycavg[cell_num] += yc[cell_num][i];
			}
			// Compute the average of the last ten values
			//  (this represents the expected location of the cell)
			ycavg[cell_num] = ycavg[cell_num] / (double) (frame_num > 10 ? 10 : frame_num);
			
			// Determine the range of the subimage surrounding the current position
			u1[cell_num] = max(xci[cell_num] - 4.0 * R + 0.5, 0 );
			u2[cell_num] = min(xci[cell_num] + 4.0 * R + 0.5, Iw - 1);
			v1[cell_num] = max(yci[cell_num] - 2.0 * R + 1.5, 0 );    
			v2[cell_num] = min(yci[cell_num] + 2.0 * R + 1.5, Ih - 1);

                
			// Extract the subimage
			Isub[cell_num] = m_get(v2[cell_num] - v1[cell_num] + 1, u2[cell_num] - u1[cell_num] + 1);
			for (i = v1[cell_num]; i <= v2[cell_num]; i++) {
				for (j = u1[cell_num]; j <= u2[cell_num]; j++) {
					m_set_val(Isub[cell_num], i - v1[cell_num], j - u1[cell_num], m_get_val(I, i, j));
				}
			}
			
	        // Compute the subimage gradient magnitude			
			Ix[cell_num] = gradient_x(Isub[cell_num]);
			Iy[cell_num] = gradient_y(Isub[cell_num]);
			IE[cell_num] = m_get(Isub[cell_num]->m, Isub[cell_num]->n);
			for (i = 0; i < Isub[cell_num]->m; i++) {
				for (j = 0; j < Isub[cell_num]->n; j++) {
					double temp_x = m_get_val(Ix[cell_num], i, j);
					double temp_y = m_get_val(Iy[cell_num], i, j);
					m_set_val(IE[cell_num], i, j, sqrt((temp_x * temp_x) + (temp_y * temp_y)));
				}
			}
		
		}
		
		// Compute the motion gradient vector flow (MGVF) edgemaps for all cells concurrently
		long long MGVF_start_time = get_time();
		MAT **IMGVF = MGVF(IE, 1, 1, Nc);
		MGVF_time += get_time() - MGVF_start_time;
		
		// Sequentially determine the new location of each cell
		for (cell_num = 0; cell_num < Nc; cell_num++) {	
			// Determine the position of the cell in the subimage			
			xci[cell_num] = xci[cell_num] - (double) u1[cell_num];
			yci[cell_num] = yci[cell_num] - (double) (v1[cell_num] - 1);
			ycavg[cell_num] = ycavg[cell_num] - (double) (v1[cell_num] - 1);
			
			// Evolve the snake
			long long snake_start_time = get_time();
			ellipseevolve(IMGVF[cell_num], &(xci[cell_num]), &(yci[cell_num]), ri[cell_num], t, Np, (double) R, ycavg[cell_num]);
			snake_time += get_time() - snake_start_time;
			
			// Compute the cell's new position in the full image
			xci[cell_num] = xci[cell_num] + u1[cell_num];
			yci[cell_num] = yci[cell_num] + (v1[cell_num] - 1);
			
			// Store the new location of the cell and the snake
			xc[cell_num][frame_num] = xci[cell_num];
			yc[cell_num][frame_num] = yci[cell_num];
			for (j = 0; j < Np; j++) {
				r[cell_num][j][frame_num] = 0;
				r[cell_num][j][frame_num] = ri[cell_num][j];
				x[cell_num][j][frame_num] = xc[cell_num][frame_num] + (ri[cell_num][j] * cos(t[j]));
				y[cell_num][j][frame_num] = yc[cell_num][frame_num] + (ri[cell_num][j] * sin(t[j]));
			}
			
			// Output the updated center of each cell
			// printf("\n%d,%f,%f", cell_num, xci[cell_num], yci[cell_num]);


			
			// Free temporary memory
			m_free(Isub[cell_num]);
			m_free(Ix[cell_num]);
			m_free(Iy[cell_num]);
			m_free(IE[cell_num]);
			m_free(IMGVF[cell_num]);
	    }

#ifdef OUTPUT
		if (frame_num == Nf)
		  {
		    FILE * pFile;
		    pFile = fopen ("result.txt","w+");
	
		    for (cell_num = 0; cell_num < Nc; cell_num++)		
		      fprintf(pFile,"\n%d,%f,%f", cell_num, xci[cell_num], yci[cell_num]);

		    fclose (pFile);
		  }
		
#endif

		
		free(IMGVF);
		
		// Output a new line to visually distinguish the output from different frames
		//printf("\n");
	}
	
	// Free temporary memory
	free_2d_double(xc);
	free_2d_double(yc);
	free_3d_double(r);
	free_3d_double(x);
	free_3d_double(y);
	free(t);	
	free(xci);
	free(yci);
	free_2d_double(ri);
	free(ycavg);
	free(u1);
	free(u2);
	free(v1);
	free(v2);
	free(Isub);
	free(Ix);
	free(Iy);
	free(IE);
	
	// Report average processing time per frame
	printf("\n\nTracking runtime (average per frame):\n");
	printf("------------------------------------\n");
	printf("MGVF computation: %.5f seconds\n", ((float) (MGVF_time)) / (float) (1000*1000*Nf));
	printf(" Snake evolution: %.5f seconds\n", ((float) (snake_time)) / (float) (1000*1000*Nf));


        printf("CAUTION: cpu_offset: %d time: %lf mseconds\n", cpu_offset, ((float) (MGVF_time)) / (float) (1000*1000*Nf)*1000);
}