// Take Rational Function Optimization step
void MOLECULE::rfo_step(void) {
  int i, j;
  int dim = g_nintco();
  double tval, tval2;
  double *f_q = p_Opt_data->g_forces_pointer();
  double **H = p_Opt_data->g_H_pointer();
  double *dq = p_Opt_data->g_dq_pointer();
fprintf(outfile,"doing rfo_step\n"); fflush(outfile);

  // build (lower-triangle of) RFO matrix and diagonalize
  double **rfo_mat = init_matrix(dim+1, dim+1);
  for (i=0; i<dim; ++i)
    for (j=0; j<=i; ++j)
      rfo_mat[i][j] = H[i][j];

  for (i=0; i<dim; ++i)
    rfo_mat[dim][i] = - f_q[i];

  if (Opt_params.print_lvl >= 3) {
    fprintf(outfile,"RFO mat\n");
    print_matrix(outfile, rfo_mat, dim+1, dim+1);
  }

  double *lambda = init_array(dim+1);
  opt_symm_matrix_eig(rfo_mat, dim+1, lambda);
  if (Opt_params.print_lvl >= 2) {
    fprintf(outfile,"RFO eigenvalues/lambdas\n");
    print_matrix(outfile, &(lambda), 1, dim+1);
  }

  // Do intermediate normalization.  
  // RFO paper says to scale eigenvector to make the last element equal to 1.
  // During the course of an optimization some evects may appear that are bogus leads
  // - the root following can avoid them. 
  for (i=0; i<dim+1; ++i) {
    tval = rfo_mat[i][dim];
    if (fabs(tval) > Opt_params.rfo_normalization_min) {
      for (j=0;j<dim+1;++j)
        rfo_mat[i][j] /= rfo_mat[i][dim];
    }
  }
  if (Opt_params.print_lvl >= 3) {
    fprintf(outfile,"RFO eigenvectors (rows)\n");
    print_matrix(outfile, rfo_mat, dim+1, dim+1);
  }

  int rfo_root, f;
  double rfo_eval;
  double *rfo_u;      // unit vector in step direction
  double rfo_dqnorm;   // norm of step
  double rfo_g;         // gradient in step direction
  double rfo_h;         // hessian in step direction
  double DE_projected; // projected energy change by quadratic approximation

  // *** choose which RFO eigenvector to use
  // if not root following, then use rfo_root'th lowest eigenvalue; default is 0 (lowest)
  if ( (!Opt_params.rfo_follow_root) || (p_Opt_data->g_iteration() == 1)) {
    rfo_root = Opt_params.rfo_root;
    fprintf(outfile,"\tFollowing RFO solution %d.\n", rfo_root);
  }
  else { // do root following
    double * rfo_old_evect = p_Opt_data->g_rfo_eigenvector_pointer();
    tval = 0;
    for (i=0; i<dim; ++i) { // dot only within H block, excluding rows and columns approaching 0
      tval2 = array_dot(rfo_mat[i], rfo_old_evect,dim);
      if (tval2 > tval) {
        tval = tval2;
        rfo_root = i;
      }
    }
    fprintf(outfile,"RFO vector %d has maximal overlap with previous step\n", rfo_root+1);
  }
  p_Opt_data->set_rfo_eigenvector(rfo_mat[rfo_root]);

  // print out lowest energy evects
  if (Opt_params.print_lvl >= 2) {
    for (i=0; i<dim+1; ++i) {
      if ((lambda[i] < 0.0) || (i <rfo_root)) {
        fprintf(outfile,"RFO eigenvalue %d: %15.10lf (or 2*%-15.10lf)\n", i+1, lambda[i],lambda[i]/2);
        fprintf(outfile,"eigenvector:\n");
        print_matrix(outfile, &(rfo_mat[i]), 1, dim+1);
      }
    }
  }
  free_array(lambda);

  for (j=0; j<dim; ++j)
    dq[j] = rfo_mat[rfo_root][j]; // leave out last column

  // Zero steps for frozen fragment
  for (f=0; f<fragments.size(); ++f) {
    if (fragments[f]->is_frozen() || Opt_params.freeze_intrafragment) {
      fprintf(outfile,"\tZero'ing out displacements for frozen fragment %d\n", f+1);
      for (i=0; i<fragments[f]->g_nintco(); ++i)
        dq[ g_intco_offset(f) + i ] = 0.0;
    }
  }

  apply_intrafragment_step_limit(dq);
  //check_intrafragment_zero_angles(dq);

  // get norm |dq| and unit vector in the step direction
  rfo_dqnorm = sqrt( array_dot(dq, dq, dim) );
  rfo_u = init_array(dim);
  array_copy(rfo_mat[rfo_root], rfo_u, dim);
  array_normalize(rfo_u, dim);
  free_matrix(rfo_mat);

  // get gradient and hessian in step direction
  rfo_g = -1 * array_dot(f_q, rfo_u, dim);
  rfo_h = 0;
  for (i=0; i<dim; ++i)
    rfo_h += rfo_u[i] * array_dot(H[i], rfo_u, dim);

  DE_projected = DE_rfo_energy(rfo_dqnorm, rfo_g, rfo_h);
  fprintf(outfile,"\tProjected energy change by RFO approximation: %20.10lf\n", DE_projected);

  // do displacements for each fragment separately
  for (f=0; f<fragments.size(); ++f) {
    if (fragments[f]->is_frozen() || Opt_params.freeze_intrafragment) {
      fprintf(outfile,"\tDisplacements for frozen fragment %d skipped.\n", f+1);
      continue;
    }
    fragments[f]->displace(&(dq[g_intco_offset(f)]), true, g_intco_offset(f));
  }

  // do displacements for interfragment coordinates
  double *q_target;
  for (int I=0; I<interfragments.size(); ++I) {

    q_target = interfragments[I]->intco_values();
    for (i=0; i<interfragments[I]->g_nintco(); ++i)
      q_target[i] += dq[g_interfragment_intco_offset(I) + i];

    interfragments[I]->orient_fragment(q_target);

    free_array(q_target);
  }

#if defined(OPTKING_PACKAGE_QCHEM)
//  // fix rotation matrix for rotations in QCHEM EFP code
//  for (int I=0; I<efp_fragments.size(); ++I)
//    efp_fragments[I]->displace( I, &(dq[g_efp_fragment_intco_offset(I)]) );
#endif

  //symmetrize_geom(); // now symmetrize the geometry for next step

  // save values in step data
  p_Opt_data->save_step_info(DE_projected, rfo_u, rfo_dqnorm, rfo_g, rfo_h);

  free_array(rfo_u);
  fflush(outfile);

} // end take RFO step
Ejemplo n.º 2
0
T SparseVectorCompressed<T>::normSquared() const
{
  return array_dot(vals,vals,num_entries);
}