Exemplo n.º 1
0
void AssembleWillmoreFlow_AD(MultiLevelProblem& ml_prob) {
  //  ml_prob is the global object from/to where get/set all the data
  //  level is the level of the PDE system to be assembled
  //  levelMax is the Maximum level of the MultiLevelProblem
  //  assembleMatrix is a flag that tells if only the residual or also the matrix should be assembled

  // call the adept stack object
  adept::Stack& s = FemusInit::_adeptStack;

  //  extract pointers to the several objects that we are going to use
  TransientNonlinearImplicitSystem* mlPdeSys   = &ml_prob.get_system<TransientNonlinearImplicitSystem> ("Willmore");   // pointer to the linear implicit system named "Poisson"

  const unsigned level = mlPdeSys->GetLevelToAssemble();
  const unsigned levelMax = mlPdeSys->GetLevelMax();
  const bool assembleMatrix = mlPdeSys->GetAssembleMatrix();

  Mesh*          msh        = ml_prob._ml_msh->GetLevel(level);    // pointer to the mesh (level) object
  elem*          el         = msh->el;  // pointer to the elem object in msh (level)

  MultiLevelSolution*  mlSol        = ml_prob._ml_sol;  // pointer to the multilevel solution object
  Solution*    sol        = ml_prob._ml_sol->GetSolutionLevel(level);    // pointer to the solution (level) object

  LinearEquationSolver* pdeSys        = mlPdeSys->_LinSolver[level]; // pointer to the equation (level) object
  SparseMatrix*    KK         = pdeSys->_KK;  // pointer to the global stifness matrix object in pdeSys (level)
  NumericVector*   RES          = pdeSys->_RES; // pointer to the global residual vector object in pdeSys (level)

  const unsigned  dim = msh->GetDimension(); // get the domain dimension of the problem
  unsigned    iproc = msh->processor_id(); // get the process_id (for parallel computation)

  //solution variable
  unsigned solRIndex[3];
  solRIndex[0] = mlSol->GetIndex("X");    // get the position of "X" in the ml_sol object
  solRIndex[1] = mlSol->GetIndex("Y");    // get the position of "Y" in the ml_sol object
  solRIndex[2] = mlSol->GetIndex("Z");    // get the position of "Z" in the ml_sol object
  unsigned solRType[3];
  solRType[0]= mlSol->GetSolutionType(solRIndex[0]);    // get the finite element type for "R"
  solRType[1]= mlSol->GetSolutionType(solRIndex[1]);    // get the finite element type for "R"
  solRType[2]= mlSol->GetSolutionType(solRIndex[2]);    // get the finite element type for "R"

  unsigned solRPdeIndex[3];
  solRPdeIndex[0] = mlPdeSys->GetSolPdeIndex("X");    // get the position of "X" in the pdeSys object
  solRPdeIndex[1] = mlPdeSys->GetSolPdeIndex("Y");    // get the position of "Y" in the pdeSys object
  solRPdeIndex[2] = mlPdeSys->GetSolPdeIndex("Z");    // get the position of "Z" in the pdeSys object

  vector < adept::adouble >  solR[3]; // local solution
  vector < double > solR_old[3];


  unsigned solHIndex;
  solHIndex = mlSol->GetIndex("H");    // get the position of "H" in the ml_sol object
  unsigned solHType = mlSol->GetSolutionType(solHIndex);    // get the finite element type for "H"

  unsigned solHPdeIndex;
  solHPdeIndex = mlPdeSys->GetSolPdeIndex("H");    // get the position of "H" in the pdeSys object

  vector < adept::adouble >  solH; // local solution

  vector < vector < double > > x(dim);    // local coordinates
  unsigned xType = 2; // get the finite element type for "x", it is always 2 (LAGRANGE QUADRATIC)

  vector< int > sysDof; // local to global pdeSys dofs
  vector <double> phi;  // local test function
  vector <double> phi_x; // local test function first order partial derivatives
  vector <double> phi_xx; // local test function second order partial derivatives
  double weight; // gauss point weight

  vector< double > Res; // local redidual vector
  vector< adept::adouble > aResR[3]; // local redidual vector
  vector< adept::adouble > aResH; // local redidual vector


  // reserve memory for the local standar vectors
  const unsigned maxSize = static_cast< unsigned >(ceil(pow(3, dim)));          // conservative: based on line3, quad9, hex27
  for(int i=0;i<3;i++){
    solR[i].reserve(maxSize);
    solR_old[i].reserve(maxSize);
  }
  solH.reserve(maxSize);

  for (unsigned i = 0; i < dim; i++)
    x[i].reserve(maxSize);

  sysDof.reserve(4 * maxSize);
  phi.reserve(maxSize);
  phi_x.reserve(maxSize * dim);
  unsigned dim2 = (3 * (dim - 1) + !(dim - 1));        // dim2 is the number of second order partial derivatives (1,3,6 depending on the dimension)
  phi_xx.reserve(maxSize * dim2);

  Res.reserve(4 * maxSize);
  for(int i=0;i<3;i++){
    aResR[i].reserve(maxSize);
  }
  aResH.reserve(maxSize);

  vector < double > Jac; // local Jacobian matrix (ordered by column, adept)
  Jac.reserve(4 * maxSize * 4 * maxSize);


  if (assembleMatrix)
    KK->zero(); // Set to zero all the entries of the Global Matrix

  // element loop: each process loops only on the elements that owns
  for (int iel = msh->_elementOffset[iproc]; iel < msh->_elementOffset[iproc + 1]; iel++) {

    short unsigned ielGeom = el->GetElementType(iel);    // element geometry type
    unsigned nDofs  = el->GetElementDofNumber(iel, solHType);    // number of solution element dofs
    unsigned nDofs2 = el->GetElementDofNumber(iel, xType);    // number of coordinate element dofs

    // resize local arrays
    sysDof.resize(4 * nDofs);
    for(int i = 0; i < 3; i++){
      solR[i].resize(nDofs);
      solR_old[i].resize(nDofs);
    }
    solH.resize(nDofs);

    for (int i = 0; i < dim; i++) {
      x[i].resize(nDofs2);
    }

    Res.resize(4 * nDofs);    //resize
    for(int i = 0; i < 3; i++){
      aResR[i].resize(nDofs);    //resize
    }
    aResH.resize(nDofs);    //resize

    for(int i = 0; i < 3; i++){
      std::fill(aResR[i].begin(), aResR[i].end(), 0);    //set aRes to zero
    }
    std::fill(aResH.begin(), aResH.end(), 0);    //set aRes to zero

    // local storage of global mapping and solution
    for (unsigned i = 0; i < nDofs; i++) {
      unsigned solDof = msh->GetSolutionDof(i, iel, solHType);    // global to global mapping between solution node and solution dof
      for(int k = 0; k < 3; k++){
	solR[k][i] = (*sol->_Sol[solRIndex[k]])(solDof);      // global extraction and local storage for the solution
	solR_old[k][i] = (*sol->_SolOld[solRIndex[k]])(solDof);      // global extraction and local storage for the solution

      }
      solH[i] = (*sol->_Sol[solHIndex])(solDof);      // global extraction and local storage for the solution
      for(int k = 0; k < 3; k++){
	sysDof[k*nDofs + i] = pdeSys->GetSystemDof(solRIndex[k], solRPdeIndex[k], i, iel);    // global to global mapping between solution node and pdeSys dof
      }
      sysDof[3*nDofs + i] = pdeSys->GetSystemDof(solHIndex, solHPdeIndex, i, iel);    // global to global mapping between solution node and pdeSys dof
    }

    // local storage of coordinates
    for (unsigned i = 0; i < nDofs2; i++) {
      unsigned xDof  = msh->GetSolutionDof(i, iel, xType);    // global to global mapping between coordinates node and coordinate dof

      for (unsigned idim = 0; idim < dim; idim++) {
        x[idim][i] = (*msh->_topology->_Sol[idim])(xDof);      // global extraction and local storage for the element coordinates
      }
    }

    if (level == levelMax || !el->GetRefinedElementIndex(iel)) {      // do not care about this if now (it is used for the AMR)
      // start a new recording of all the operations involving adept::adouble variables
      s.new_recording();


      // *** Gauss point loop ***
      for (unsigned ig = 0; ig < msh->_finiteElement[ielGeom][solHType]->GetGaussPointNumber(); ig++) {
        // *** get gauss point weight, test function and test function partial derivatives ***
        msh->_finiteElement[ielGeom][solHType]->Jacobian(x, ig, weight, phi, phi_x, phi_xx);

	// evaluate the solution, the solution derivatives and the coordinates in the gauss point
        adept::adouble solRGauss[3];
	double solRGaussOld[3];
        adept::adouble solRGauss_x[3][2];
	adept::adouble solRGauss_xx[3][2][2];

	adept::adouble sol_x[2];
	sol_x[0]=sol_x[1]=0.;

	for(int k=0; k<3; k++){
	  solRGauss[k]=0.;
	  solRGaussOld[k]=0.;
	  for(int i=0; i<dim; i++){
	    solRGauss_x[k][i]=0.;
	    for(int j=0; j<dim; j++){
	      solRGauss_xx[k][i][j]=0.;
	    }
	  }
	}

        adept::adouble solHGauss = 0;
        adept::adouble solHGauss_x[2]={0.,0.};

        for (unsigned i = 0; i < nDofs; i++) {
	  for(int k = 0; k < 3; k++){
	    solRGauss[k] += phi[i] * solR[k][i];
	    solRGaussOld[k] += phi[i] * solR_old[k][i];
	  }
          solHGauss += phi[i] * solH[i];

	  for (unsigned u = 0; u < dim; u++) {
	    sol_x[u] += phi[i] * x[u][i];
	  }

          for (unsigned u = 0; u < dim; u++) { // gradient
	    for(int k=0; k < 3; k++){
	      solRGauss_x[k][u] += phi_x[i * dim + u] * solR[k][i];
	    }
            solHGauss_x[u] += phi_x[i * dim + u] * solH[i];
          }

	  for( unsigned u = 0; u < dim; u++ ) { // hessian
	    for( unsigned v = 0; v < dim; v++ ) {
	      unsigned uvindex = 0; //_uu
	      if( u != v ) uvindex = 2; //_uv or _vu
	      else if( u == 1 ) uvindex = 1; //_vv
	      for(int k = 0; k < 3; k++){
		solRGauss_xx[k][u][v] += phi_xx[i * dim2 + uvindex] * solR[k][i];
	      }
	    }
	  }
	}

        adept::adouble g[2][2];

        g[0][0] = g[0][1] = g[1][0] = g[1][1] = 0.;

	for(int k = 0; k < 3; k++){
	  for(int u = 0; u < dim; u++){
	    for(int v = 0; v < dim; v++){
	      g[u][v] += solRGauss_x[k][u] * solRGauss_x[k][v];
	    }
	  }
	}

	adept::adouble detg = g[0][0]*g[1][1]-g[0][1]*g[1][0];

	adept::adouble  A = sqrt(detg);

	adept::adouble gI[2][2];

        gI[0][0] =  g[1][1]/detg;
	gI[0][1] = -g[0][1]/detg;
	gI[1][0] = -g[1][0]/detg;
	gI[1][1] =  g[0][0]/detg;

	adept::adouble N[3];

	N[0] = ( solRGauss_x[1][0] * solRGauss_x[2][1] - solRGauss_x[1][1] * solRGauss_x[2][0] ) / A;
	N[1] = ( solRGauss_x[2][0] * solRGauss_x[0][1] - solRGauss_x[2][1] * solRGauss_x[0][0] ) / A;
	N[2] = ( solRGauss_x[0][0] * solRGauss_x[1][1] - solRGauss_x[0][1] * solRGauss_x[1][0] ) / A;

	adept::adouble h[2][2];

        h[0][0]=h[0][1]=h[1][0]=h[1][1]=0.;

	for(int k=0; k<3; k++){
	  for(int u=0; u<dim; u++){
	    for(int v=0; v<dim; v++){
	      h[u][v] += solRGauss_xx[k][u][v] * N[k];

	    }
	  }
	}

        //adept::adouble K = cos(sol_x[0])/(a+cos(sol_x[0]));//(h[0][0]*h[1][1]-h[0][1]*h[1][0])/detg;

	adept::adouble K = (h[0][0]*h[1][1]-h[0][1]*h[1][0])/detg;

	adept::adouble H_exact = 0.5*(1. + cos(sol_x[0])/(a+cos(sol_x[0])));

        // *** phi_i loop ***
        for (unsigned i = 0; i < nDofs; i++) {

	  for(int k=0; k<3; k++){
	    for(int u=0; u<dim; u++){
	      adept::adouble AgIgradRgradPhi=0;
	      for(int v=0; v<dim; v++){
		AgIgradRgradPhi += A * gI[u][v].value() * solRGauss_x[k][v];
	      }
	      aResR[k][i] += AgIgradRgradPhi * phi_x[i * dim + u] * weight;
	    }
	    aResR[k][i] += 2.* A * solHGauss.value() * N[k] * phi[i] * weight;

	  }


	  for(int u=0; u<dim; u++){
	    adept::adouble AgIgradHgradPhi=0;
	    for(int v=0; v<dim; v++){
	      AgIgradHgradPhi += A * gI[u][v].value() * solHGauss_x[v];
	    }
	    aResH[i] -= AgIgradHgradPhi * phi_x[i * dim + u] * weight;
	  }
	   aResH[i] += A * ( -0*(solRGauss[0]-solRGaussOld[0])*N[0].value()
		             -0*(solRGauss[1]-solRGaussOld[1])*N[1].value()
		             -0*(solRGauss[2]-solRGaussOld[2])*N[2].value()
	               + 2. * solHGauss * ( solHGauss * solHGauss  - K.value() ) )* phi[i] * weight;
	} // end phi_i loop
      } // end gauss point loop
    } // endif single element not refined or fine grid loop

    //--------------------------------------------------------------------------------------------------------
    // Add the local Matrix/Vector into the global Matrix/Vector

    //copy the value of the adept::adoube aRes in double Res and store
    for (int i = 0; i < nDofs; i++) {
      for( int k=0;k<3;k++){
	Res[ k * nDofs + i] = -aResR[k][i].value();
      }
      Res[ 3 * nDofs + i] = -aResH[i].value();
    }

    RES->add_vector_blocked(Res, sysDof);

    if (assembleMatrix) {
      Jac.resize((4 * nDofs) *(4 * nDofs));
      // define the dependent variables
      for( int k=0;k<3;k++){
	s.dependent(&aResR[k][0], nDofs);
      }
      s.dependent(&aResH[0], nDofs);

      // define the independent variables
      for( int k=0;k<3;k++){
	s.independent(&solR[k][0], nDofs);
      }
      s.independent(&solH[0], nDofs);
      // get the jacobian matrix (ordered by row)
      s.jacobian(&Jac[0], true);

      KK->add_matrix_blocked(Jac, sysDof, sysDof);

      s.clear_independents();
      s.clear_dependents();
    }
  } //end element loop for each process

  RES->close();

  if (assembleMatrix) KK->close();

  // ***************** END ASSEMBLY *******************
}