Пример #1
0
void
InitialConditionTempl<T>::compute()
{
  // -- NOTE ----
  // The following code is a copy from libMesh project_vector.C plus it adds some features, so we
  // can couple variable values
  // and we also do not call any callbacks, but we use our initial condition system directly.
  // ------------

  // The dimension of the current element
  _dim = _current_elem->dim();
  // The element type
  const ElemType elem_type = _current_elem->type();
  // The number of nodes on the new element
  const unsigned int n_nodes = _current_elem->n_nodes();

  // Get FE objects of the appropriate type
  // We cannot use the FE object in Assembly, since the following code is messing with the
  // quadrature rules
  // for projections and would screw it up. However, if we implement projections from one mesh to
  // another,
  // this code should use that implementation.
  std::unique_ptr<FEBaseType> fe(FEBaseType::build(_dim, _fe_type));

  // Prepare variables for projection
  std::unique_ptr<QBase> qrule(_fe_type.default_quadrature_rule(_dim));
  std::unique_ptr<QBase> qedgerule(_fe_type.default_quadrature_rule(1));
  std::unique_ptr<QBase> qsiderule(_fe_type.default_quadrature_rule(_dim - 1));

  // The values of the shape functions at the quadrature points
  _phi = &fe->get_phi();

  // The gradients of the shape functions at the quadrature points on the child element.
  _dphi = nullptr;

  _cont = fe->get_continuity();

  if (_cont == C_ONE)
  {
    const std::vector<std::vector<GradientType>> & ref_dphi = fe->get_dphi();
    _dphi = &ref_dphi;
  }

  // The Jacobian * quadrature weight at the quadrature points
  _JxW = &fe->get_JxW();
  // The XYZ locations of the quadrature points
  _xyz_values = &fe->get_xyz();

  // Update the DOF indices for this element based on the current mesh
  _var.prepareIC();
  _dof_indices = _var.dofIndices();

  // The number of DOFs on the element
  const unsigned int n_dofs = _dof_indices.size();
  if (n_dofs == 0)
    return;

  // Fixed vs. free DoFs on edge/face projections
  _dof_is_fixed.clear();
  _dof_is_fixed.resize(n_dofs, false);
  _free_dof.clear();
  _free_dof.resize(n_dofs, 0);

  // Zero the interpolated values
  _Ue.resize(n_dofs);
  _Ue.zero();

  // In general, we need a series of
  // projections to ensure a unique and continuous
  // solution.  We start by interpolating nodes, then
  // hold those fixed and project edges, then
  // hold those fixed and project faces, then
  // hold those fixed and project interiors

  // Interpolate node values first
  _current_dof = 0;
  for (_n = 0; _n != n_nodes; ++_n)
  {
    // FIXME: this should go through the DofMap,
    // not duplicate _dof_indices code badly!
    _nc = FEInterface::n_dofs_at_node(_dim, _fe_type, elem_type, _n);
    if (!_current_elem->is_vertex(_n))
    {
      _current_dof += _nc;
      continue;
    }
    if (_cont == DISCONTINUOUS)
      libmesh_assert(_nc == 0);
    else if (_cont == C_ZERO)
      setCZeroVertices();
    else if (_fe_type.family == HERMITE)
      setHermiteVertices();
    else if (_cont == C_ONE)
      setOtherCOneVertices();
    else
      libmesh_error();
  } // loop over nodes

  // From here on out we won't be sampling at nodes anymore
  _current_node = nullptr;

  // In 3D, project any edge values next
  if (_dim > 2 && _cont != DISCONTINUOUS)
    for (unsigned int e = 0; e != _current_elem->n_edges(); ++e)
    {
      FEInterface::dofs_on_edge(_current_elem, _dim, _fe_type, e, _side_dofs);

      // Some edge dofs are on nodes and already
      // fixed, others are free to calculate
      _free_dofs = 0;
      for (unsigned int i = 0; i != _side_dofs.size(); ++i)
        if (!_dof_is_fixed[_side_dofs[i]])
          _free_dof[_free_dofs++] = i;

      // There may be nothing to project
      if (!_free_dofs)
        continue;

      // Initialize FE data on the edge
      fe->attach_quadrature_rule(qedgerule.get());
      fe->edge_reinit(_current_elem, e);
      _n_qp = qedgerule->n_points();

      choleskySolve(false);
    }

  // Project any side values (edges in 2D, faces in 3D)
  if (_dim > 1 && _cont != DISCONTINUOUS)
    for (unsigned int s = 0; s != _current_elem->n_sides(); ++s)
    {
      FEInterface::dofs_on_side(_current_elem, _dim, _fe_type, s, _side_dofs);

      // Some side dofs are on nodes/edges and already
      // fixed, others are free to calculate
      _free_dofs = 0;
      for (unsigned int i = 0; i != _side_dofs.size(); ++i)
        if (!_dof_is_fixed[_side_dofs[i]])
          _free_dof[_free_dofs++] = i;

      // There may be nothing to project
      if (!_free_dofs)
        continue;

      // Initialize FE data on the side
      fe->attach_quadrature_rule(qsiderule.get());
      fe->reinit(_current_elem, s);
      _n_qp = qsiderule->n_points();

      choleskySolve(false);
    }

  // Project the interior values, finally

  // Some interior dofs are on nodes/edges/sides and
  // already fixed, others are free to calculate
  _free_dofs = 0;
  for (unsigned int i = 0; i != n_dofs; ++i)
    if (!_dof_is_fixed[i])
      _free_dof[_free_dofs++] = i;

  // There may be nothing to project
  if (_free_dofs)
  {
    // Initialize FE data
    fe->attach_quadrature_rule(qrule.get());
    fe->reinit(_current_elem);
    _n_qp = qrule->n_points();

    choleskySolve(true);
  } // if there are free interior dofs

  // Make sure every DoF got reached!
  for (unsigned int i = 0; i != n_dofs; ++i)
    libmesh_assert(_dof_is_fixed[i]);

  NumericVector<Number> & solution = _var.sys().solution();

  // 'first' and 'last' are no longer used, see note about subdomain-restricted variables below
  // const dof_id_type
  //   first = solution.first_local_index(),
  //   last  = solution.last_local_index();

  // Lock the new_vector since it is shared among threads.
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);

    for (unsigned int i = 0; i < n_dofs; i++)
    // We may be projecting a new zero value onto
    // an old nonzero approximation - RHS
    // if (_Ue(i) != 0.)

    // This is commented out because of subdomain restricted variables.
    // It can be the case that if a subdomain restricted variable's boundary
    // aligns perfectly with a processor boundary that the variable will get
    // no value.  To counteract this we're going to let every processor set a
    // value at every node and then let PETSc figure it out.
    // Later we can choose to do something different / better.
    //      if ((_dof_indices[i] >= first) && (_dof_indices[i] < last))
    {
      solution.set(_dof_indices[i], _Ue(i));
    }
    _var.setDofValues(_Ue);
  }
}
Пример #2
0
/* ******************************************************************************** */
int getSearchDir(double *p, double *Grad, double **Hes, double delta, int numSS) {
  /*
    Computes the search direction using the dogleg method (Nocedal and Wright,
    page 71).  Notation is consistent with that in this reference.

    Due to the construction of the problem, the minimization routine
    to find tau can be solved exactly by solving a quadratic.  There
    can be precision issues with this, so this is checked.  If the
    argument of the square root in the quadratic formula is negative,
    there must be a precision error, as such a situation is not
    possible in the construction of the problem.

    Returns:
      1 if step was a pure Newton step (didn't hit trust region boundary)
      2 if the step was purely Cauchy in nature (hit trust region boundary)
      3 if the step was a dogleg step (part Newton and part Cauchy)
      4 if Cholesky decomposition failed and we had to take a Cauchy step
      5 if Cholesky decompostion failed but we would've taken Cauchy step anyways
      6 if the dogleg calculation failed (should never happen)
  */

  int i,j; // counters
  double a,b,c,sgnb; // Constants used in quadratic formula
  double q,x1,x2; // results from quadratic formula
  double tau; // Multipler in Newtonstep
  double *pB; // Unconstrained minimizer (the regular Newton step)
  double *pU; // Minimizer along steepest descent direction
  double pB2; // pj^2
  double pU2; // pu^2
  double pBpU; // pj . pc
  double *CholDiag; // Diagonal from Cholesky decomposition
  double **HesCopy; // Copy of the upper triangle of the Hessian (don't want to mess
                   // with actual Hessian).
  int CholSuccess; // Whether of not Cholesky decomposition is successful
  double *HGrad; // Hessian dotted with the gradient
  double pUcoeff;  // The coefficient on the Cauchy step
  double delta2; // delta^2
  double mag1;   // temporary variables for vector magnitudes
  double mag2;

  // Initialize pB2 just so compiler doesn't give a warning when optimization is on
  pB2 = 0.0;

  delta2 = pow(delta,2);

  /* ********** Compute the Newton step ************** */
  // Memory allocation for computation of  Newton step
  pB = (double *) malloc(numSS * sizeof(double));
  CholDiag = (double *) malloc(numSS * sizeof(double));
  HesCopy = (double **) malloc(numSS * sizeof(double *));
  for (j = 0; j < numSS; j++) {
    HesCopy[j] = (double *) malloc(numSS * sizeof(double));
  }
 
  // Make a copy of the Hessian because the Cholesky decomposition messes
  // with its entries.  We only have to copy the upper diagonal.
  for (j = 0; j < numSS; j++) {
    for (i = j; i < numSS; i++) {
      HesCopy[i][j] = Hes[i][j];
    }
  }

  CholSuccess = choleskyDecomposition(HesCopy,numSS);

  if (CholSuccess == 1) {
    choleskySolve(HesCopy,numSS,Grad,pB);

    // Free memory from Cholesky computation
    free(CholDiag);
    for (i = 0; i < numSS; i++) {
      free(HesCopy[i]);
    }
    free(HesCopy);

    // Newton step is -H^{-1} Grad
    for (i = 0; i < numSS; i++) {
      pB[i] *= -1.0;
    }

    // If Newton is in trust region, take it
    pB2 = dot(pB,pB,numSS);
    if (pB2 <= delta2) {
      for (i = 0; i < numSS; i++) {
	p[i] = pB[i];
      }
      free(pB);
      return 1; // Signifies we took a pure Newton step
    }
  }
  else { // Free memory from failed Newton step computation
    free(CholDiag);
    for (i = 0; i < numSS; i++) {
      free(HesCopy[i]);
    }
    free(HesCopy);
  }
  /* ************************************************* */


  /* ********** Compute the Cauchy step ************** */
  // Allocate necessary arrays
  HGrad = (double *) malloc(numSS * sizeof(double));
  pU = (double *) malloc(numSS * sizeof(double));

  // The direction of the Cauchy step
  for (i = 0; i < numSS; i++) {
    pU[i] =  -Grad[i];
  }

  // prefactor for the Cauchy step
  MatrixVectorMult(HGrad,Hes,Grad,numSS);
  // Should this be sqrt too?

  mag1 = dot(Grad,Grad,numSS);
  mag2 = dot(Grad,HGrad,numSS);
  pUcoeff = mag1 / mag2;

  for (i = 0; i < numSS; i++) {
    pU[i] = pUcoeff * pU[i];
  }
  free(HGrad); // Don't need this any more

  pU2 = dot(pU,pU,numSS);

  if (pU2 >= delta2) { // In this case we just take the Cauchy step, 0 < tau <= 1
    tau = sqrt(delta2/pU2);
    for (i = 0; i < numSS; i++) {
      p[i] = tau*pU[i];
    }
    free(pU);
    free(pB);
    if (CholSuccess != 1) {
      return 5; // Signifies Cholesky failure, but doesn't matter, would take Cauchy
    }           // regardless
    else {
      return 2; // Signifies that we just took the Cauchy step
    }
  }

  if (CholSuccess != 1) { // We failed computing Newton step and have to take Cauchy
    for (i = 0; i < numSS; i++) {
      p[i] = pU[i];
    }
    free(pU);
    free(pB);
    return 4; // Signifies Cholesky failure and we just took the Cauchy step
  }
  /* ************************************************* */


  /* ************ Take the dogleg step *************** */
  pBpU = dot(pB,pU,numSS); // Need this for dogleg calculation
  
  // Constants for quadratic formula for solving ||pU + (alpha)(pB-pU)||^2 = delta2
  a = pB2 + pU2 - 2.0*pBpU;
  b = 2*(pBpU - pU2);
  c = pU2 - delta2;
  sgnb = 1;

  if(b < 0) {
    sgnb = -1;
  }

  q = -0.5 * (b + sgnb * sqrt(b*b - 4*a*c));
  x1 = q / a;
  x2 = c / q;

  // x2 should be the positive root, x1 should be the negative root.
  if(x2 >= 0 && x2 <= 1.0) {
    for(i = 0; i < numSS; i++) {
      p[i] = pU[i] + x2 * (pB[i] - pU[i]);
    }
    free(pU);
    free(pB);
    return 3; // Signifies we took a dogleg step
  } else if(x1 >= 0 && x1 <= 1.0) {
    for(i = 0; i < numSS; i++) {
      p[i] = pU[i] + x1 * (pB[i] - pU[i]);
    }
    free(pU);
    free(pB);
    return 3;
  } else {
    for (i = 0; i < numSS; i++) {
      p[i] = pU[i];
    }
    free(pU);
    free(pB);
    return 6; // Signifies no root satisfies the dogleg step and we took a Cauchy step
  }
}