static grid *grid_create(int n, double2 *points, double cell_size) { /* * Create a grid data structure containing n points in square cells of the size `cell_size`. * The cells are sorted by their cell number internally, so that the points of each cell are * in a continuous part of the `points` array. */ int i; int cur_cell = 0; grid *grid_ptr; assert(n > 0); grid_ptr = malloc(sizeof(grid)); assert(grid_ptr); calculate_bounding_box(n, points, grid_ptr->bounding_box, grid_ptr->bounding_box + 1); grid_ptr->num_cells.x = (int)((grid_ptr->bounding_box[1].x - grid_ptr->bounding_box[0].x) / cell_size) + 1; grid_ptr->num_cells.y = (int)((grid_ptr->bounding_box[1].y - grid_ptr->bounding_box[0].y) / cell_size) + 1; grid_ptr->cell_offsets = malloc((grid_ptr->num_cells.x * grid_ptr->num_cells.y + 1) * sizeof(int)); assert(grid_ptr->cell_offsets); grid_ptr->points = points; grid_ptr->cell_size = cell_size; assert(current_grid == NULL); current_grid = grid_ptr; qsort(points, (size_t)n, sizeof(double2), compare_element_fun); current_grid = NULL; for (i = 0; i < n; i++) { while (grid_get_cell_number(grid_ptr, points + i) >= cur_cell) { grid_ptr->cell_offsets[cur_cell++] = i; } } while (cur_cell <= grid_ptr->num_cells.x * grid_ptr->num_cells.y) { grid_ptr->cell_offsets[cur_cell++] = n; } return grid_ptr; }
int find_boundary(int n, double *x, double *y, double r, double (*r_function)(double, double), int n_contour, int *contour) { /* * Find the boundary of the `n` 2-dimensional points located at `x` and `y` using a ballpivot approach. * The indices of the contour points are stored in the `contour` array. There are several possibilities * to provide the ball radius used in the ballpivot algorithm: * - As a callback function (`r_function`) that returns the ball radius for the current position. * - As a constant ball radius `r` * - Automatically calculated / estimated from the data points * * If `r_function` is different from NULL it will be used to calculate the ball radius for each position. * In that case the parameter `r` is only used to set the cell size of the internal grid data structure storing * the points if it has a value greater 0. * If `r_function` is NULL and `r` is greater 0 it is used as a constant ball radius and determines the cell * size of the internal grid. Otherwise a (constant) ball radius is automatically calculated as 1.2 times the * the largest distance from a point to its nearest neighbor. * * If the algorithm is successful it returns the number of contour points that were written in `contour`. * Otherwise the return value is less than 0 indicating a too small (`FIND_BOUNDARY_BALL_TOO_SMALL`) or too * large (`FIND_BOUNDARY_BALL_TOO_LARGE`) ball radius, an invalid number of points (`FIND_BOUNDARY_INVALID_POINTS`) * or that the number of contour points exceed the available memory in `contour` given by `n_contour`. */ double2 bb_bottom_left, bb_top_right; double2 *points; double cell_size; neighbor_point_list data; grid *grid_ptr; int i, start_point, current_index; int num_contour_points = 0; if (n < 2) { return FIND_BOUNDARY_INVALID_POINTS; } if (n_contour <= 1) { return FIND_BOUNDARY_MEMORY_EXCEEDED; } points = malloc(n * sizeof(double2)); assert(points); for (i = 0; i < n; i++) { points[i].x = x[i]; points[i].y = y[i]; points[i].index = i; } calculate_bounding_box(n, points, &bb_bottom_left, &bb_top_right); if (r <= 0) { /* If no radius is given, estimate the cell size as 1/10 of the average of width and height of the data's * bounding box. */ cell_size = ((bb_top_right.x - bb_bottom_left.x) + (bb_top_right.y - bb_bottom_left.y)) / 2. / 10.; } else { /* Scale radius by 1.1 to make sure that only direct neighbor cells have to be checked. */ cell_size = r * 1.1; } grid_ptr = grid_create(n, points, cell_size); /* Start from the point closest to the bottom left corner of the bounding box*/ start_point = grid_find_nearest_neighbor(grid_ptr, grid_ptr->bounding_box[0], -1, cell_size).index; contour[num_contour_points] = start_point; if (r <= 0 && r_function == NULL) { /* No radius given, calculate from data */ for (i = 0; i < n; i++) { double nearest_neighbor = grid_find_nearest_neighbor(grid_ptr, grid_get_elem(grid_ptr, i), i, cell_size).x; if (nearest_neighbor > r) { /* Calculate r as the largest distance from one point to its nearest neighbor. This makes sure, that * at least one neighbor can be reached from each point. */ r = nearest_neighbor; } } r = sqrt(r); r *= 1.2; } /* Initialize list structure that stores the possible neighbors in each step. */ data.capacity = 10; data.point_list = malloc(data.capacity * sizeof(int)); assert(data.point_list); current_index = start_point; while (num_contour_points == 0 || contour[num_contour_points] != contour[0]) { double2 current_point; data.current = current_index; data.size = 0; data.num_points_reachable = 0; current_point = grid_get_elem(grid_ptr, current_index); if (num_contour_points >= n_contour) { return FIND_BOUNDARY_MEMORY_EXCEEDED; } if (r_function) { r = r_function(current_point.x, current_point.y); if (r <= 0) { return FIND_BOUNDARY_BALL_TOO_SMALL; } } /* Find the possible neighbors of `current_point` using the grid structure. */ grid_apply_function(grid_ptr, current_point, 2 * r, grid_cb_find_possible_neighbors, (void *)&data, 1, ¤t_index); if (data.size == 1) { /* Only one neighbor is possible */ current_index = data.point_list[0]; contour[++num_contour_points] = current_index; } else if (data.size > 1) { /* More than one point is a possible neighbor */ int best_neighbor = 0; double best_angle = 0; int oldest = num_contour_points + 1; int unvisited_points = 0; /* If at least one possible neighbor is not included in the contour until now use the (unvisited) one * with the smallest angle. Otherwise use the one that was visited first to avoid (infinite) loops. */ for (i = 0; i < (int)data.size; i++) { int contour_point_index = in_contour(data.point_list[i], num_contour_points, contour); if (contour_point_index < 0) { double2 previous_contour_point = grid_get_elem(grid_ptr, contour[num_contour_points - 1]); double2 possible_contour_point = grid_get_elem(grid_ptr, data.point_list[i]); double a = angle(current_point, previous_contour_point, possible_contour_point); if (a > best_angle) { best_angle = a; best_neighbor = i; } unvisited_points++; } else if (!unvisited_points) { if (contour_point_index < oldest) { oldest = contour_point_index; best_neighbor = i; } } } current_index = data.point_list[best_neighbor]; contour[++num_contour_points] = current_index; } else { /* No possible neighbor is found. */ if (data.num_points_reachable == 0) { /* No point was reachable -> ball too small */ return FIND_BOUNDARY_BALL_TOO_SMALL; } else { /* No reachable point resulted in an empty ball -> ball too large */ return FIND_BOUNDARY_BALL_TOO_LARGE; } } } /* The grid data structure reorders the points. Restore original indices of the contour points. */ for (i = 0; i < num_contour_points; i++) { contour[i] = points[contour[i]].index; } free(points); free(data.point_list); grid_destroy(grid_ptr); return num_contour_points; }
int populate_lattice(particle_data* atom_data) { /* * This routine will populate the lattice using the * values read from the pdb and itp files. * WARNING: It contains much logic and interpolation stuff! */ #ifdef DEBUG printf("pdb_n_particles=%u, itp_n_particles=%u, itp_n_parameters=%u\n",atom_data->pdb_n_particles,atom_data->itp_n_particles,atom_data->itp_n_parameters); #endif // TODO: Check if bounding box fits into simbox bounding_box bbox; calculate_bounding_box(&bbox, atom_data); // calculate the shift of the bounding box float shift[3]; shift[0] = ek_parameters.agrid / 2.0 * ek_parameters.dim_x - bbox.center[0]; shift[1] = ek_parameters.agrid / 2.0 * ek_parameters.dim_y - bbox.center[1]; shift[2] = ek_parameters.agrid / 2.0 * ek_parameters.dim_z - bbox.center[2]; #ifdef DEBUG printf("bbox.max_x=%f, bbox.max_y=%f, bbox.max_z=%f, bbox.min_x=%f, bbox.min_y=%f, bbox.min_z=%f, bbox->center=[%f; %f; %f]\n", bbox.max_x, bbox.max_y, bbox.max_z, bbox.min_x, bbox.min_y, bbox.min_z, bbox.center[0], bbox.center[1], bbox.center[2]); printf("agrid=%f, dim_x=%d, dim_y=%d, dim_z=%d\n",ek_parameters.agrid, ek_parameters.dim_x, ek_parameters.dim_y, ek_parameters.dim_z); printf("shift=[%f; %f; %f]\n",shift[0], shift[1], shift[2]); #endif // joining the array int lowernode[3]; float cellpos[3]; float gridpos; float a_x_shifted, a_y_shifted, a_z_shifted; for (unsigned int i = 0; i <= atom_data->pdb_n_particles-1; i++) { pdb_ATOM* a = &atom_data->pdb_array_ATOM[i]; itp_atoms* b; itp_atomtypes* c; for (unsigned int j = 0; j <= atom_data->itp_n_particles-1; j++) { b = &atom_data->itp_array_atoms[j]; if (a->i == b->i) { for (unsigned int k = 0; k <= atom_data->itp_n_parameters-1; k++) { c = &atom_data->itp_array_atomtypes[k]; if (strcmp(b->type,c->type) == 0) { #ifdef DEBUG printf("i=%d x=%f y=%f z=%f type=%s charge=%f sigma=%f epsilon=%f\n",a->i,a->x,a->y,a->z,b->type,b->charge,c->sigma,c->epsilon); #endif // Interpolate the charge to the lattice gridpos = (a->x + shift[0]) / ek_parameters.agrid - 0.5f; lowernode[0] = (int) floorf( gridpos ); cellpos[0] = gridpos - lowernode[0]; gridpos = (a->y + shift[1]) / ek_parameters.agrid - 0.5f; lowernode[1] = (int) floorf( gridpos ); cellpos[1] = gridpos - lowernode[1]; gridpos = (a->z + shift[2]) / ek_parameters.agrid - 0.5f; lowernode[2] = (int) floorf( gridpos ); cellpos[2] = gridpos - lowernode[2]; lowernode[0] = (lowernode[0] + ek_parameters.dim_x) % ek_parameters.dim_x; lowernode[1] = (lowernode[1] + ek_parameters.dim_y) % ek_parameters.dim_y; lowernode[2] = (lowernode[2] + ek_parameters.dim_z) % ek_parameters.dim_z; pdb_charge_lattice[pdb_rhoindex_cartesian2linear( lowernode[0],lowernode[1],lowernode[2] )] += b->charge * ( 1 - cellpos[0] ) * ( 1 - cellpos[1] ) * ( 1 - cellpos[2] ); pdb_charge_lattice[pdb_rhoindex_cartesian2linear( ( lowernode[0] + 1 ) % ek_parameters.dim_x,lowernode[1],lowernode[2] )] += b->charge * cellpos[0] * ( 1 - cellpos[1] ) * ( 1 - cellpos[2] ); pdb_charge_lattice[pdb_rhoindex_cartesian2linear( lowernode[0],( lowernode[1] + 1 ) % ek_parameters.dim_y,lowernode[2] )] += b->charge * ( 1 - cellpos[0] ) * cellpos[1] * ( 1 - cellpos[2] ); pdb_charge_lattice[pdb_rhoindex_cartesian2linear( lowernode[0],lowernode[1],( lowernode[2] + 1 ) % ek_parameters.dim_z )] += b->charge * ( 1 - cellpos[0] ) * ( 1 - cellpos[1] ) * cellpos[2]; pdb_charge_lattice[pdb_rhoindex_cartesian2linear( ( lowernode[0] + 1 ) % ek_parameters.dim_x,( lowernode[1] + 1 ) % ek_parameters.dim_y,lowernode[2] )] += b->charge * cellpos[0] * cellpos[1] * ( 1 - cellpos[2] ); pdb_charge_lattice[pdb_rhoindex_cartesian2linear( ( lowernode[0] + 1 ) % ek_parameters.dim_x,lowernode[1],( lowernode[2] + 1 ) % ek_parameters.dim_z )] += b->charge * cellpos[0] * ( 1 - cellpos[1] ) * cellpos[2]; pdb_charge_lattice[pdb_rhoindex_cartesian2linear( lowernode[0],( lowernode[1] + 1 ) % ek_parameters.dim_y,( lowernode[2] + 1 ) % ek_parameters.dim_z )] += b->charge * ( 1 - cellpos[0] ) * cellpos[1] * cellpos[2]; pdb_charge_lattice[pdb_rhoindex_cartesian2linear( ( lowernode[0] + 1 ) % ek_parameters.dim_x,( lowernode[1] + 1 ) % ek_parameters.dim_y,( lowernode[2] + 1 ) % ek_parameters.dim_z )] += b->charge * cellpos[0] * cellpos[1] * cellpos[2]; // Interpolate lennard-jones parameters to boundary float r = pow(2,1./6.)*c->sigma; a_x_shifted = (a->x + shift[0]) / ek_parameters.agrid - 0.5f; a_y_shifted = (a->y + shift[1]) / ek_parameters.agrid - 0.5f; a_z_shifted = (a->z + shift[2]) / ek_parameters.agrid - 0.5f; for (float z = a->z - r; z <= a->z + r + ek_parameters.agrid; z += ek_parameters.agrid) { for (float y = a->y - r; y <= a->y + r + ek_parameters.agrid; y += ek_parameters.agrid) { for (float x = a->x - r; x <= a->x + r + ek_parameters.agrid; x += ek_parameters.agrid) { gridpos = (x + shift[0]) / ek_parameters.agrid - 0.5f; lowernode[0] = (int) floorf( gridpos ); gridpos = (y + shift[1]) / ek_parameters.agrid - 0.5f; lowernode[1] = (int) floorf( gridpos ); gridpos = (z + shift[2]) / ek_parameters.agrid - 0.5f; lowernode[2] = (int) floorf( gridpos ); lowernode[0] = (lowernode[0] + ek_parameters.dim_x) % ek_parameters.dim_x; lowernode[1] = (lowernode[1] + ek_parameters.dim_y) % ek_parameters.dim_y; lowernode[2] = (lowernode[2] + ek_parameters.dim_z) % ek_parameters.dim_z; #ifdef DEBUG printf("shifted: %f %f %f\n", a_x_shifted, a_y_shifted, a_z_shifted); printf("lowernode: %d %d %d\n", lowernode[0], lowernode[1], lowernode[2]); printf("distance: %f %f %f\n", lowernode[0] - a_x_shifted, lowernode[1] - a_y_shifted, lowernode[2] - a_z_shifted); printf("distance: %f <= %f\n\n", pow(lowernode[0] - a_x_shifted,2) + pow(lowernode[1] - a_y_shifted,2) + pow(lowernode[2] - a_z_shifted,2), pow(r/ek_parameters.agrid,2)); #endif if ( pow(lowernode[0] - a_x_shifted,2) + pow(lowernode[1] - a_y_shifted,2) + pow(lowernode[2] - a_z_shifted,2) <= pow(r/ek_parameters.agrid,2) ) { pdb_boundary_lattice[ek_parameters.dim_y*ek_parameters.dim_x*lowernode[2] + ek_parameters.dim_x*lowernode[1] + lowernode[0]] = 1; } } } } break; } } } } } return pdb_SUCCESS; }