Example #1
0
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;
}
Example #2
0
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,
                          &current_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;
}