Beispiel #1
0
void
StressDivergenceTruss::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  mooseAssert(re.size() == 2, "Truss elements must have two nodes");
  _local_re.resize(re.size());
  _local_re.zero();

  RealGradient orientation( (*_orientation)[0] );
  orientation /= orientation.size();
  VectorValue<Real> force_local = _axial_stress[0] * _area[0] * orientation;

  int sign(-_test[0][0]/std::abs(_test[0][0]));
  _local_re(0) = sign * force_local(_component);
  _local_re(1) = -_local_re(0);

  re += _local_re;

  if(_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for(unsigned int i=0; i<_save_in.size(); i++)
      _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
  }
}
Beispiel #2
0
void
ODEKernel::computeResidual()
{
  prepareVectorTag(_assembly, _var.number());

  for (_i = 0; _i < _var.order(); _i++)
    _local_re(_i) += computeQpResidual();

  accumulateTaggedLocalResidual();
}
void
NodalEqualValueConstraint::computeResidual()
{
  prepareVectorTag(_assembly, _var.number());

  for (unsigned int k = 0; k < _value.size(); k++)
    _local_re(k) = (*_value[k])[0] - (*_value[k])[1];

  assignTaggedLocalResidual();
}
Beispiel #4
0
void
AuxKernel::compute()
{
  precalculateValue();

  if (isNodal()) /* nodal variables */
  {
    if (_var.isNodalDefined())
    {
      _qp = 0;
      Real value = computeValue();
      // update variable data, which is referenced by other kernels, so the value is up-to-date
      _var.setNodalValue(value);
    }
  }
  else /* elemental variables */
  {
    _n_local_dofs = _var.numberOfDofs();

    if (_n_local_dofs == 1) /* p0 */
    {
      Real value = 0;
      for (_qp = 0; _qp < _qrule->n_points(); _qp++)
        value += _JxW[_qp] * _coord[_qp] * computeValue();
      value /= (_bnd ? _current_side_volume : _current_elem_volume);
      // update the variable data refernced by other kernels.
      // Note that this will update the values at the quadrature points too
      // (because this is an Elemental variable)
      _var.setNodalValue(value);
    }
    else /* high-order */
    {
      _local_re.resize(_n_local_dofs);
      _local_re.zero();
      _local_ke.resize(_n_local_dofs, _n_local_dofs);
      _local_ke.zero();

      // assemble the local mass matrix and the load
      for (unsigned int i = 0; i < _test.size(); i++)
        for (_qp = 0; _qp < _qrule->n_points(); _qp++)
        {
          Real t = _JxW[_qp] * _coord[_qp] * _test[i][_qp];
          _local_re(i) += t * computeValue();
          for (unsigned int j = 0; j < _test.size(); j++)
            _local_ke(i, j) += t * _test[j][_qp];
        }

      // mass matrix is always SPD
      _local_sol.resize(_n_local_dofs);
      _local_ke.cholesky_solve(_local_re, _local_sol);

      _var.setNodalValue(_local_sol);
    }
  }
}
Beispiel #5
0
void
IntegratedBC::computeResidual()
{
  prepareVectorTag(_assembly, _var.number());

  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
    for (_i = 0; _i < _test.size(); _i++)
      _local_re(_i) += _JxW[_qp] * _coord[_qp] * computeQpResidual();

  accumulateTaggedLocalResidual();

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (unsigned int i = 0; i < _save_in.size(); i++)
      _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
  }
}
Beispiel #6
0
void
IntegratedBC::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  _local_re.resize(re.size());
  _local_re.zero();

  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
    for (_i = 0; _i < _test.size(); _i++)
      _local_re(_i) += _JxW[_qp]*_coord[_qp]*computeQpResidual();

  re += _local_re;

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (unsigned int i=0; i<_save_in.size(); i++)
      _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
  }
}
Beispiel #7
0
void
StressDivergenceRZ::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  _local_re.resize(re.size());
  _local_re.zero();

  if (_volumetric_locking_correction)
  {
    // calculate volume averaged value of shape function derivative
    _avg_grad_test.resize(_test.size());
    for (_i = 0; _i < _test.size(); _i++)
    {
      _avg_grad_test[_i].resize(2);
      _avg_grad_test[_i][_component] = 0.0;
      for (_qp = 0; _qp < _qrule->n_points(); _qp++)
      {
        if (_component == 0)
          _avg_grad_test[_i][_component] += (_grad_test[_i][_qp](_component) + _test[_i][_qp] / _q_point[_qp](0)) * _JxW[_qp] * _coord[_qp];
        else
          _avg_grad_test[_i][_component] += _grad_test[_i][_qp](_component) * _JxW[_qp] * _coord[_qp];

      }
      _avg_grad_test[_i][_component] /= _current_elem_volume;
    }
  }

  precalculateResidual();
  for (_i = 0; _i < _test.size(); _i++)
    for (_qp = 0; _qp < _qrule->n_points(); _qp++)
      _local_re(_i) += _JxW[_qp] * _coord[_qp] * computeQpResidual();

  re += _local_re;

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (const auto & var : _save_in)
      var->sys().solution().add_vector(_local_re, var->dofIndices());
  }
}
Beispiel #8
0
void
Kernel::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  _local_re.resize(re.size());
  _local_re.zero();

  precalculateResidual();
  for (_i = 0; _i < _test.size(); _i++)
    for (_qp = 0; _qp < _qrule->n_points(); _qp++)
      _local_re(_i) += _JxW[_qp] * _coord[_qp] * computeQpResidual();

  re += _local_re;

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (const auto & var : _save_in)
      var->sys().solution().add_vector(_local_re, var->dofIndices());
  }
}
Beispiel #9
0
void
EigenKernel::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  _local_re.resize(re.size());
  _local_re.zero();

  mooseAssert(*_eigenvalue != 0.0, "Can't divide by zero eigenvalue in EigenKernel!");
  Real one_over_eigen = 1.0 / *_eigenvalue;
  for (_i = 0; _i < _test.size(); _i++)
    for (_qp = 0; _qp < _qrule->n_points(); _qp++)
      _local_re(_i) += _JxW[_qp] * _coord[_qp] * one_over_eigen * computeQpResidual();

  re += _local_re;

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (const auto & var : _save_in)
      var->sys().solution().add_vector(_local_re, var->dofIndices());
  }
}
void
PorousFlowFluxLimitedTVDAdvection::computeResidual()
{
  prepareVectorTag(_assembly, _var.number());
  precalculateResidual();

  // get the residual contributions from _fluo
  for (unsigned i = 0; i < _current_elem->n_nodes(); ++i)
  {
    const dof_id_type node_id_i = _current_elem->node_id(i);
    _local_re(i) = _fluo.getFluxOut(node_id_i) / _fluo.getValence(node_id_i);
  }

  accumulateTaggedLocalResidual();

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (unsigned int i = 0; i < _save_in.size(); i++)
      _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
  }
}
void
StressDivergenceTensors::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  _local_re.resize(re.size());
  _local_re.zero();

  if (_volumetric_locking_correction)
    computeAverageGradientTest();

  precalculateResidual();
  for (_i = 0; _i < _test.size(); ++_i)
    for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
      _local_re(_i) += _JxW[_qp] * _coord[_qp] * computeQpResidual();

  re += _local_re;

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (const auto & var : _save_in)
      var->sys().solution().add_vector(_local_re, var->dofIndices());
  }
}
Beispiel #12
0
void
KernelGrad::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  _local_re.resize(re.size());
  _local_re.zero();

  const unsigned int n_test = _test.size();
  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
  {
    RealGradient value = precomputeQpResidual() * _JxW[_qp] * _coord[_qp];
    for (_i = 0; _i < n_test; _i++)  // target for auto vectorization
      _local_re(_i) += value * _grad_test[_i][_qp];
  }

  re += _local_re;

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (unsigned int i = 0; i < _save_in.size(); i++)
      _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
  }
}
Beispiel #13
0
void
Q2PPorepressureFlux::upwind(bool compute_res, bool compute_jac, unsigned int jvar)
{
  if (compute_jac && !(jvar == _var.number() || jvar == _sat_var))
    return;

  // calculate the mobility values and their derivatives
  prepareNodalValues();

  // compute the residual without the mobility terms
  // Even if we are computing the jacobian we still need this
  // in order to see which nodes are upwind and which are downwind
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  _local_re.resize(re.size());
  _local_re.zero();

  for (_i = 0; _i < _test.size(); _i++)
    for (_qp = 0; _qp < _qrule->n_points(); _qp++)
      _local_re(_i) += _JxW[_qp] * _coord[_qp] * computeQpResidual();

  DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), jvar);
  if (compute_jac)
  {
    _local_ke.resize(ke.m(), ke.n());
    _local_ke.zero();

    for (_i = 0; _i < _test.size(); _i++)
      for (_j = 0; _j < _phi.size(); _j++)
        for (_qp = 0; _qp < _qrule->n_points(); _qp++)
          _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpJac(jvar);
  }

  // Now perform the upwinding by multiplying the residuals at the
  // upstream nodes by their mobilities
  //
  // The residual for the kernel is the darcy flux.
  // This is
  // R_i = int{mobility*flux_no_mob} = int{mobility*grad(pot)*permeability*grad(test_i)}
  // for node i.  where int is the integral over the element.
  // However, in fully-upwind, the first step is to take the mobility outside the
  // integral, which was done in the _local_re calculation above.
  //
  // NOTE: Physically _local_re(_i) is a measure of fluid flowing out of node i
  // If we had left in mobility, it would be exactly the mass flux flowing out of node i.
  //
  // This leads to the definition of upwinding:
  // ***
  // If _local_re(i) is positive then we use mobility_i.  That is
  // we use the upwind value of mobility.
  // ***
  //
  // The final subtle thing is we must also conserve fluid mass: the total mass
  // flowing out of node i must be the sum of the masses flowing
  // into the other nodes.

  // FIRST:
  // this is a dirty way of getting around precision loss problems
  // and problems at steadystate where upwinding oscillates from
  // node-to-node causing nonconvergence.
  // I'm not sure if i actually need to do this in moose.  Certainly
  // in cosflow it is necessary.
  // I will code a better algorithm if necessary
  bool reached_steady = true;
  for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
  {
    if (_local_re(nodenum) >= 1E-20)
    {
      reached_steady = false;
      break;
    }
  }
  reached_steady = false;

  // DEFINE VARIABLES USED TO ENSURE MASS CONSERVATION
  // total mass out - used for mass conservation
  Real total_mass_out = 0;
  // total flux in
  Real total_in = 0;

  // the following holds derivatives of these
  std::vector<Real> dtotal_mass_out;
  std::vector<Real> dtotal_in;
  if (compute_jac)
  {
    dtotal_mass_out.assign(_num_nodes, 0);
    dtotal_in.assign(_num_nodes, 0);
  }

  // PERFORM THE UPWINDING!
  for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
  {
    if (_local_re(nodenum) >= 0 || reached_steady) // upstream node
    {
      if (compute_jac)
      {
        for (_j = 0; _j < _phi.size(); _j++)
          _local_ke(nodenum, _j) *= _mobility[nodenum];
        if (jvar == _var.number())
          // deriv wrt P
          _local_ke(nodenum, nodenum) += _dmobility_dp[nodenum] * _local_re(nodenum);
        else
          // deriv wrt S
          _local_ke(nodenum, nodenum) += _dmobility_ds[nodenum] * _local_re(nodenum);
        for (_j = 0; _j < _phi.size(); _j++)
          dtotal_mass_out[_j] += _local_ke(nodenum, _j);
      }
      _local_re(nodenum) *= _mobility[nodenum];
      total_mass_out += _local_re(nodenum);
    }
    else
    {
      total_in -= _local_re(nodenum); // note the -= means the result is positive
      if (compute_jac)
        for (_j = 0; _j < _phi.size(); _j++)
          dtotal_in[_j] -= _local_ke(nodenum, _j);
    }
  }

  // CONSERVE MASS
  // proportion the total_mass_out mass to the inflow nodes, weighting by their _local_re values
  if (!reached_steady)
    for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
      if (_local_re(nodenum) < 0)
      {
        if (compute_jac)
          for (_j = 0; _j < _phi.size(); _j++)
          {
            _local_ke(nodenum, _j) *= total_mass_out / total_in;
            _local_ke(nodenum, _j) +=
                _local_re(nodenum) * (dtotal_mass_out[_j] / total_in -
                                      dtotal_in[_j] * total_mass_out / total_in / total_in);
          }
        _local_re(nodenum) *= total_mass_out / total_in;
      }

  // ADD RESULTS TO RESIDUAL OR JACOBIAN
  if (compute_res)
  {
    re += _local_re;

    if (_has_save_in)
    {
      Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
      for (unsigned int i = 0; i < _save_in.size(); i++)
        _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
    }
  }

  if (compute_jac)
  {
    ke += _local_ke;

    if (_has_diag_save_in && jvar == _var.number())
    {
      const unsigned int rows = ke.m();
      DenseVector<Number> diag(rows);
      for (unsigned int i = 0; i < rows; i++)
        diag(i) = _local_ke(i, i);

      Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
      for (unsigned int i = 0; i < _diag_save_in.size(); i++)
        _diag_save_in[i]->sys().solution().add_vector(diag, _diag_save_in[i]->dofIndices());
    }
  }
}
Beispiel #14
0
void
InertialForceBeam::computeResidual()
{
  DenseVector<Number> & re = _assembly.residualBlock(_var.number());
  mooseAssert(re.size() == 2, "Beam element only has two nodes.");
  _local_re.resize(re.size());
  _local_re.zero();

  if (_dt != 0.0)
  {
    // fetch the two end nodes for _current_elem
    std::vector<Node *> node;
    for (unsigned int i = 0; i < 2; ++i)
      node.push_back(_current_elem->get_node(i));

    // Fetch the solution for the two end nodes at time t
    NonlinearSystemBase & nonlinear_sys = _fe_problem.getNonlinearSystemBase();

    if (isParamValid("beta"))
    {
      const NumericVector<Number> & sol = *nonlinear_sys.currentSolution();
      const NumericVector<Number> & sol_old = nonlinear_sys.solutionOld();

      AuxiliarySystem & aux = _fe_problem.getAuxiliarySystem();
      const NumericVector<Number> & aux_sol_old = aux.solutionOld();

      mooseAssert(_beta > 0.0, "InertialForceBeam: Beta parameter should be positive.");
      mooseAssert(_eta[0] >= 0.0, "InertialForceBeam: Eta parameter should be non-negative.");

      for (unsigned int i = 0; i < _ndisp; ++i)
      {
        // obtain delta displacement
        unsigned int dof_index_0 = node[0]->dof_number(nonlinear_sys.number(), _disp_num[i], 0);
        unsigned int dof_index_1 = node[1]->dof_number(nonlinear_sys.number(), _disp_num[i], 0);
        const Real disp_0 = sol(dof_index_0) - sol_old(dof_index_0);
        const Real disp_1 = sol(dof_index_1) - sol_old(dof_index_1);

        dof_index_0 = node[0]->dof_number(nonlinear_sys.number(), _rot_num[i], 0);
        dof_index_1 = node[1]->dof_number(nonlinear_sys.number(), _rot_num[i], 0);
        const Real rot_0 = sol(dof_index_0) - sol_old(dof_index_0);
        const Real rot_1 = sol(dof_index_1) - sol_old(dof_index_1);

        // obtain new translational and rotational velocities and accelerations using newmark-beta
        // time integration
        _vel_old_0(i) = aux_sol_old(node[0]->dof_number(aux.number(), _vel_num[i], 0));
        _vel_old_1(i) = aux_sol_old(node[1]->dof_number(aux.number(), _vel_num[i], 0));
        const Real accel_old_0 = aux_sol_old(node[0]->dof_number(aux.number(), _accel_num[i], 0));
        const Real accel_old_1 = aux_sol_old(node[1]->dof_number(aux.number(), _accel_num[i], 0));

        _rot_vel_old_0(i) = aux_sol_old(node[0]->dof_number(aux.number(), _rot_vel_num[i], 0));
        _rot_vel_old_1(i) = aux_sol_old(node[1]->dof_number(aux.number(), _rot_vel_num[i], 0));
        const Real rot_accel_old_0 =
            aux_sol_old(node[0]->dof_number(aux.number(), _rot_accel_num[i], 0));
        const Real rot_accel_old_1 =
            aux_sol_old(node[1]->dof_number(aux.number(), _rot_accel_num[i], 0));

        _accel_0(i) =
            1. / _beta * (disp_0 / (_dt * _dt) - _vel_old_0(i) / _dt - accel_old_0 * (0.5 - _beta));
        _accel_1(i) =
            1. / _beta * (disp_1 / (_dt * _dt) - _vel_old_1(i) / _dt - accel_old_1 * (0.5 - _beta));
        _rot_accel_0(i) =
            1. / _beta *
            (rot_0 / (_dt * _dt) - _rot_vel_old_0(i) / _dt - rot_accel_old_0 * (0.5 - _beta));
        _rot_accel_1(i) =
            1. / _beta *
            (rot_1 / (_dt * _dt) - _rot_vel_old_1(i) / _dt - rot_accel_old_1 * (0.5 - _beta));

        _vel_0(i) = _vel_old_0(i) + (_dt * (1 - _gamma)) * accel_old_0 + _gamma * _dt * _accel_0(i);
        _vel_1(i) = _vel_old_1(i) + (_dt * (1 - _gamma)) * accel_old_1 + _gamma * _dt * _accel_1(i);
        _rot_vel_0(i) = _rot_vel_old_0(i) + (_dt * (1 - _gamma)) * rot_accel_old_0 +
                        _gamma * _dt * _rot_accel_0(i);
        _rot_vel_1(i) = _rot_vel_old_1(i) + (_dt * (1 - _gamma)) * rot_accel_old_1 +
                        _gamma * _dt * _rot_accel_1(i);
      }
    }
    else
    {
      if (!nonlinear_sys.solutionUDot())
        mooseError("InertialForceBeam: Time derivative of solution (`u_dot`) is not stored. Please "
                   "set uDotRequested() to true in FEProblemBase before requesting `u_dot`.");

      if (!nonlinear_sys.solutionUDotOld())
        mooseError("InertialForceBeam: Old time derivative of solution (`u_dot_old`) is not "
                   "stored. Please set uDotOldRequested() to true in FEProblemBase before "
                   "requesting `u_dot_old`.");

      if (!nonlinear_sys.solutionUDotDot())
        mooseError("InertialForceBeam: Second time derivative of solution (`u_dotdot`) is not "
                   "stored. Please set uDotDotRequested() to true in FEProblemBase before "
                   "requesting `u_dotdot`.");

      const NumericVector<Number> & vel = *nonlinear_sys.solutionUDot();
      const NumericVector<Number> & vel_old = *nonlinear_sys.solutionUDotOld();
      const NumericVector<Number> & accel = *nonlinear_sys.solutionUDotDot();

      for (unsigned int i = 0; i < _ndisp; ++i)
      {
        // translational velocities and accelerations
        unsigned int dof_index_0 = node[0]->dof_number(nonlinear_sys.number(), _disp_num[i], 0);
        unsigned int dof_index_1 = node[1]->dof_number(nonlinear_sys.number(), _disp_num[i], 0);
        _vel_0(i) = vel(dof_index_0);
        _vel_1(i) = vel(dof_index_1);
        _vel_old_0(i) = vel_old(dof_index_0);
        _vel_old_1(i) = vel_old(dof_index_1);
        _accel_0(i) = accel(dof_index_0);
        _accel_1(i) = accel(dof_index_1);

        // rotational velocities and accelerations
        dof_index_0 = node[0]->dof_number(nonlinear_sys.number(), _rot_num[i], 0);
        dof_index_1 = node[1]->dof_number(nonlinear_sys.number(), _rot_num[i], 0);
        _rot_vel_0(i) = vel(dof_index_0);
        _rot_vel_1(i) = vel(dof_index_1);
        _rot_vel_old_0(i) = vel_old(dof_index_0);
        _rot_vel_old_1(i) = vel_old(dof_index_1);
        _rot_accel_0(i) = accel(dof_index_0);
        _rot_accel_1(i) = accel(dof_index_1);
      }
    }

    // transform translational and rotational velocities and accelerations to the initial local
    // configuration of the beam
    _local_vel_old_0 = _original_local_config[0] * _vel_old_0;
    _local_vel_old_1 = _original_local_config[0] * _vel_old_1;
    _local_vel_0 = _original_local_config[0] * _vel_0;
    _local_vel_1 = _original_local_config[0] * _vel_1;
    _local_accel_0 = _original_local_config[0] * _accel_0;
    _local_accel_1 = _original_local_config[0] * _accel_1;

    _local_rot_vel_old_0 = _original_local_config[0] * _rot_vel_old_0;
    _local_rot_vel_old_1 = _original_local_config[0] * _rot_vel_old_1;
    _local_rot_vel_0 = _original_local_config[0] * _rot_vel_0;
    _local_rot_vel_1 = _original_local_config[0] * _rot_vel_1;
    _local_rot_accel_0 = _original_local_config[0] * _rot_accel_0;
    _local_rot_accel_1 = _original_local_config[0] * _rot_accel_1;

    // local residual
    for (unsigned int i = 0; i < _ndisp; ++i)
    {
      if (_component < 3)
      {
        _local_force[0](i) = _density[0] * _area[0] * _original_length[0] / 3.0 *
                             (_local_accel_0(i) + _local_accel_1(i) / 2.0 +
                              _eta[0] * (1.0 + _alpha) * (_local_vel_0(i) + _local_vel_1(i) / 2.0) -
                              _alpha * _eta[0] * (_local_vel_old_0(i) + _local_vel_old_1(i) / 2.0));
        _local_force[1](i) = _density[0] * _area[0] * _original_length[0] / 3.0 *
                             (_local_accel_1(i) + _local_accel_0(i) / 2.0 +
                              _eta[0] * (1.0 + _alpha) * (_local_vel_1(i) + _local_vel_0(i) / 2.0) -
                              _alpha * _eta[0] * (_local_vel_old_1(i) + _local_vel_old_0(i) / 2.0));
      }

      if (_component > 2)
      {
        Real I = _Iy[0] + _Iz[0];
        if (isParamValid("Ix") && (i == 0))
          I = _Ix[0];
        if (i == 1)
          I = _Iz[0];
        else if (i == 2)
          I = _Iy[0];

        _local_moment[0](i) =
            _density[0] * I * _original_length[0] / 3.0 *
            (_local_rot_accel_0(i) + _local_rot_accel_1(i) / 2.0 +
             _eta[0] * (1.0 + _alpha) * (_local_rot_vel_0(i) + _local_rot_vel_1(i) / 2.0) -
             _alpha * _eta[0] * (_local_rot_vel_old_0(i) + _local_rot_vel_old_1(i) / 2.0));
        _local_moment[1](i) =
            _density[0] * I * _original_length[0] / 3.0 *
            (_local_rot_accel_1(i) + _local_rot_accel_0(i) / 2.0 +
             _eta[0] * (1.0 + _alpha) * (_local_rot_vel_1(i) + _local_rot_vel_0(i) / 2.0) -
             _alpha * _eta[0] * (_local_rot_vel_old_1(i) + _local_rot_vel_old_0(i) / 2.0));
      }
    }

    // If Ay or Az are non-zero, contribution of rotational accelerations to translational forces
    // and vice versa have to be added
    if (_component < 3)
    {
      _local_force[0](0) +=
          _density[0] * _original_length[0] / 3.0 *
          (_Az[0] * (_local_rot_accel_0(1) + _local_rot_accel_1(1) / 2.0 +
                     _eta[0] * (1.0 + _alpha) * (_local_rot_vel_0(1) + _local_rot_vel_1(1) / 2.0) -
                     _alpha * _eta[0] * (_local_rot_vel_old_0(1) + _local_rot_vel_old_1(1) / 2.0)) -
           _Ay[0] * (_local_rot_accel_0(2) + _local_rot_accel_1(2) / 2.0 +
                     _eta[0] * (1.0 + _alpha) * (_local_rot_vel_0(2) + _local_rot_vel_1(2) / 2.0) -
                     _alpha * _eta[0] * (_local_rot_vel_old_0(2) + _local_rot_vel_old_1(2) / 2.0)));
      _local_force[1](0) +=
          _density[0] * _original_length[0] / 3.0 *
          (_Az[0] * (_local_rot_accel_1(1) + _local_rot_accel_0(1) / 2.0 +
                     _eta[0] * (1.0 + _alpha) * (_local_rot_vel_1(1) + _local_rot_vel_0(1) / 2.0) -
                     _alpha * _eta[0] * (_local_rot_vel_old_1(1) + _local_rot_vel_old_0(1) / 2.0)) -
           _Ay[0] * (_local_rot_accel_1(2) + _local_rot_accel_0(2) / 2.0 +
                     _eta[0] * (1.0 + _alpha) * (_local_rot_vel_1(2) + _local_rot_vel_0(2) / 2.0) -
                     _alpha * _eta[0] * (_local_rot_vel_old_1(2) + _local_rot_vel_old_0(2) / 2.0)));

      _local_force[0](1) +=
          -_density[0] * _original_length[0] / 3.0 * _Az[0] *
          (_local_rot_accel_0(0) + _local_rot_accel_1(0) / 2.0 +
           _eta[0] * (1.0 + _alpha) * (_local_rot_vel_0(0) + _local_rot_vel_1(0) / 2.0) -
           _alpha * _eta[0] * (_local_rot_vel_old_0(0) + _local_rot_vel_old_1(0) / 2.0));
      _local_force[1](1) +=
          -_density[0] * _original_length[0] / 3.0 * _Az[0] *
          (_local_rot_accel_1(0) + _local_rot_accel_0(0) / 2.0 +
           _eta[0] * (1.0 + _alpha) * (_local_rot_vel_1(0) + _local_rot_vel_0(0) / 2.0) -
           _alpha * _eta[0] * (_local_rot_vel_old_1(0) + _local_rot_vel_old_0(0) / 2.0));

      _local_force[0](2) +=
          _density[0] * _original_length[0] / 3.0 * _Ay[0] *
          (_local_rot_accel_0(0) + _local_rot_accel_1(0) / 2.0 +
           _eta[0] * (1.0 + _alpha) * (_local_rot_vel_0(0) + _local_rot_vel_1(0) / 2.0) -
           _alpha * _eta[0] * (_local_rot_vel_old_0(0) + _local_rot_vel_old_1(0) / 2.0));
      _local_force[1](2) +=
          _density[0] * _original_length[0] / 3.0 * _Ay[0] *
          (_local_rot_accel_1(0) + _local_rot_accel_0(0) / 2.0 +
           _eta[0] * (1.0 + _alpha) * (_local_rot_vel_1(0) + _local_rot_vel_0(0) / 2.0) -
           _alpha * _eta[0] * (_local_rot_vel_old_1(0) + _local_rot_vel_old_0(0) / 2.0));
    }
    else
    {
      _local_moment[0](0) += _density[0] * _original_length[0] / 3.0 *
                             (-_Az[0] * (_local_accel_0(1) + _local_accel_1(1) / 2.0) +
                              _Ay[0] * (_local_accel_0(1) + _local_accel_1(1) / 2.0));
      _local_moment[1](0) += _density[0] * _original_length[0] / 3.0 *
                             (-_Az[0] * (_local_accel_1(1) + _local_accel_0(1) / 2.0) +
                              _Ay[0] * (_local_accel_1(1) + _local_accel_0(1) / 2.0));

      _local_moment[0](1) += _density[0] * _original_length[0] / 3.0 * _Az[0] *
                             (_local_accel_0(0) + _local_accel_1(0) / 2.0);
      _local_moment[1](1) += _density[0] * _original_length[0] / 3.0 * _Az[0] *
                             (_local_accel_1(0) + _local_accel_0(0) / 2.0);

      _local_moment[0](2) += -_density[0] * _original_length[0] / 3.0 * _Ay[0] *
                             (_local_accel_0(0) + _local_accel_1(0) / 2.0);
      _local_moment[1](2) += -_density[0] * _original_length[0] / 3.0 * _Ay[0] *
                             (_local_accel_1(0) + _local_accel_0(0) / 2.0);
    }

    // Global force and moments
    if (_component < 3)
    {
      _global_force_0 = _original_local_config[0] * _local_force[0];
      _global_force_1 = _original_local_config[0] * _local_force[1];
      _local_re(0) = _global_force_0(_component);
      _local_re(1) = _global_force_1(_component);
    }
    else
    {
      _global_moment_0 = _original_local_config[0] * _local_moment[0];
      _global_moment_1 = _original_local_config[0] * _local_moment[1];
      _local_re(0) = _global_moment_0(_component - 3);
      _local_re(1) = _global_moment_1(_component - 3);
    }
  }

  re += _local_re;

  if (_has_save_in)
  {
    Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    for (unsigned int i = 0; i < _save_in.size(); ++i)
      _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
  }
}
Beispiel #15
0
void
PorousFlowDarcyBase::upwind(JacRes res_or_jac, unsigned int jvar)
{
  if ((res_or_jac == JacRes::CALCULATE_JACOBIAN) &&
      _porousflow_dictator.notPorousFlowVariable(jvar))
    return;

  /// The PorousFlow variable index corresponding to the variable number jvar
  const unsigned int pvar =
      ((res_or_jac == JacRes::CALCULATE_JACOBIAN) ? _porousflow_dictator.porousFlowVariableNum(jvar)
                                                  : 0);

  /// The number of nodes in the element
  const unsigned int num_nodes = _test.size();

  /// Compute the residual and jacobian without the mobility terms. Even if we are computing the Jacobian
  /// we still need this in order to see which nodes are upwind and which are downwind.

  std::vector<std::vector<Real>> component_re(num_nodes);
  for (_i = 0; _i < num_nodes; ++_i)
  {
    component_re[_i].assign(_num_phases, 0.0);
    for (_qp = 0; _qp < _qrule->n_points(); _qp++)
      for (unsigned ph = 0; ph < _num_phases; ++ph)
        component_re[_i][ph] += _JxW[_qp] * _coord[_qp] * darcyQp(ph);
  }

  DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), jvar);
  if ((ke.n() == 0) &&
      (res_or_jac == JacRes::CALCULATE_JACOBIAN)) // this removes a problem encountered in
                                                  // the initial timestep when
                                                  // use_displaced_mesh=true
    return;

  std::vector<std::vector<std::vector<Real>>> component_ke;
  if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
  {
    component_ke.resize(ke.m());
    for (_i = 0; _i < _test.size(); _i++)
    {
      component_ke[_i].resize(ke.n());
      for (_j = 0; _j < _phi.size(); _j++)
      {
        component_ke[_i][_j].resize(_num_phases);
        for (_qp = 0; _qp < _qrule->n_points(); _qp++)
          for (unsigned ph = 0; ph < _num_phases; ++ph)
            component_ke[_i][_j][ph] += _JxW[_qp] * _coord[_qp] * darcyQpJacobian(jvar, ph);
      }
    }
  }

  /**
   * Now perform the upwinding by multiplying the residuals at the upstream nodes by their
   *mobilities.
   * Mobility is different for each phase, and in each situation:
   *   mobility = density / viscosity    for single-component Darcy flow
   *   mobility = mass_fraction * density * relative_perm / viscosity    for multi-component,
   *multiphase flow
   *   mobility = enthalpy * density * relative_perm / viscosity    for heat convection
   *
   * The residual for the kernel is the sum over Darcy fluxes for each phase.
   * The Darcy flux for a particular phase is
   * R_i = int{mobility*flux_no_mob} = int{mobility*grad(pot)*permeability*grad(test_i)}
   * for node i.  where int is the integral over the element.
   * However, in fully-upwind, the first step is to take the mobility outside the integral,
   * which was done in the _component_re calculation above.
   *
   * NOTE: Physically _component_re[i][ph] is a measure of fluid of phase ph flowing out of node i.
   * If we had left in mobility, it would be exactly the component mass flux flowing out of node i.
   *
   * This leads to the definition of upwinding:
   *
   * If _component_re(i)[ph] is positive then we use mobility_i.  That is we use the upwind value of
   *mobility.
   *
   * The final subtle thing is we must also conserve fluid mass: the total component mass flowing
   *out of node i
   * must be the sum of the masses flowing into the other nodes.
  **/

  // Following are used below in steady-state calculations
  Real knorm = 0.0;
  for (unsigned int qp = 0; qp < _qrule->n_points(); ++qp)
    knorm += _permeability[qp].tr();
  const Real lsq = _grad_test[0][0] * _grad_test[0][0];
  const unsigned int dim = _mesh.dimension();
  const Real l2md = std::pow(lsq, 0.5 * (2.0 - dim));
  const Real l1md = std::pow(lsq, 0.5 * (1.0 - dim));

  /// Loop over all the phases
  for (unsigned int ph = 0; ph < _num_phases; ++ph)
  {

    // FIRST:
    // this is a dirty way of getting around precision loss problems
    // and problems at steadystate where upwinding oscillates from
    // node-to-node causing nonconvergence.
    // The residual = int_{ele}*grad_test*k*(gradP - rho*g) = L^(d-1)*k*(gradP - rho*g), where d is
    // dimension
    // I assume that if one nodal P changes by P*1E-8 then this is
    // a "negligible" change.  The residual will change by L^(d-2)*k*P*1E-8
    // Similarly if rho*g changes by rho*g*1E-8 then this is a "negligible change"
    // Hence the formulae below, with grad_test = 1/L
    Real pnorm = 0.0;
    Real gravnorm = 0.0;
    for (unsigned int n = 0; n < num_nodes; ++n)
    {
      pnorm += _pp[n][ph] * _pp[n][ph];
      gravnorm += _fluid_density_node[n][ph] * _fluid_density_node[n][ph];
    }
    gravnorm *= _gravity * _gravity;
    const Real cutoff = 1.0E-8 * knorm * (std::sqrt(pnorm) * l2md + std::sqrt(gravnorm) * l1md);
    bool reached_steady = true;
    for (unsigned int nodenum = 0; nodenum < num_nodes; ++nodenum)
    {
      if (component_re[nodenum][ph] >= cutoff)
      {
        reached_steady = false;
        break;
      }
    }

    Real mob;
    Real dmob;
    /// Define variables used to ensure mass conservation
    Real total_mass_out = 0.0;
    Real total_in = 0.0;

    /// The following holds derivatives of these
    std::vector<Real> dtotal_mass_out;
    std::vector<Real> dtotal_in;
    if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
    {
      dtotal_mass_out.resize(num_nodes);
      dtotal_in.resize(num_nodes);

      for (unsigned int n = 0; n < num_nodes; ++n)
      {
        dtotal_mass_out[n] = 0.0;
        dtotal_in[n] = 0.0;
      }
    }

    /// Perform the upwinding using the mobility
    std::vector<bool> upwind_node(num_nodes);
    for (unsigned int n = 0; n < num_nodes; ++n)
    {
      if (component_re[n][ph] >= cutoff || reached_steady) // upstream node
      {
        upwind_node[n] = true;
        /// The mobility at the upstream node
        mob = mobility(n, ph);
        if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
        {
          /// The derivative of the mobility wrt the PorousFlow variable
          dmob = dmobility(n, ph, pvar);

          for (_j = 0; _j < _phi.size(); _j++)
            component_ke[n][_j][ph] *= mob;

          if (_test.size() == _phi.size())
            /* mobility at node=n depends only on the variables at node=n, by construction.  For
             * linear-lagrange variables, this means that Jacobian entries involving the derivative
             * of mobility will only be nonzero for derivatives wrt variables at node=n.  Hence the
             * [n][n] in the line below.  However, for other variable types (eg constant monomials)
             * I cannot tell what variable number contributes to the derivative.  However, in all
             * cases I can possibly imagine, the derivative is zero anyway, since in the full
             * upwinding scheme, mobility shouldn't depend on these other sorts of variables.
             */
            component_ke[n][n][ph] += dmob * component_re[n][ph];

          for (_j = 0; _j < _phi.size(); _j++)
            dtotal_mass_out[_j] += component_ke[n][_j][ph];
        }
        component_re[n][ph] *= mob;
        total_mass_out += component_re[n][ph];
      }
      else
      {
        upwind_node[n] = false;
        total_in -= component_re[n][ph]; /// note the -= means the result is positive
        if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
          for (_j = 0; _j < _phi.size(); _j++)
            dtotal_in[_j] -= component_ke[n][_j][ph];
      }
    }

    /// Conserve mass over all phases by proportioning the total_mass_out mass to the inflow nodes, weighted by their component_re values
    if (!reached_steady)
    {
      for (unsigned int n = 0; n < num_nodes; ++n)
      {
        if (!upwind_node[n]) // downstream node
        {
          if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
            for (_j = 0; _j < _phi.size(); _j++)
            {
              component_ke[n][_j][ph] *= total_mass_out / total_in;
              component_ke[n][_j][ph] +=
                  component_re[n][ph] * (dtotal_mass_out[_j] / total_in -
                                         dtotal_in[_j] * total_mass_out / total_in / total_in);
            }
          component_re[n][ph] *= total_mass_out / total_in;
        }
      }
    }
  }

  /// Add results to the Residual or Jacobian
  if (res_or_jac == JacRes::CALCULATE_RESIDUAL)
  {
    DenseVector<Number> & re = _assembly.residualBlock(_var.number());

    _local_re.resize(re.size());
    _local_re.zero();
    for (_i = 0; _i < _test.size(); _i++)
      for (unsigned int ph = 0; ph < _num_phases; ++ph)
        _local_re(_i) += component_re[_i][ph];

    re += _local_re;

    if (_has_save_in)
    {
      Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
      for (unsigned int i = 0; i < _save_in.size(); i++)
        _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
    }
  }

  if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
  {
    _local_ke.resize(ke.m(), ke.n());
    _local_ke.zero();

    for (_i = 0; _i < _test.size(); _i++)
      for (_j = 0; _j < _phi.size(); _j++)
        for (unsigned int ph = 0; ph < _num_phases; ++ph)
          _local_ke(_i, _j) += component_ke[_i][_j][ph];

    ke += _local_ke;

    if (_has_diag_save_in && jvar == _var.number())
    {
      unsigned int rows = ke.m();
      DenseVector<Number> diag(rows);
      for (unsigned int i = 0; i < rows; i++)
        diag(i) = _local_ke(i, i);

      Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
      for (unsigned int i = 0; i < _diag_save_in.size(); i++)
        _diag_save_in[i]->sys().solution().add_vector(diag, _diag_save_in[i]->dofIndices());
    }
  }
}
Beispiel #16
0
void
PorousFlowDarcyBase::computeResidualAndJacobian(JacRes res_or_jac, unsigned int jvar)
{
  if ((res_or_jac == JacRes::CALCULATE_JACOBIAN) && _dictator.notPorousFlowVariable(jvar))
    return;

  // The PorousFlow variable index corresponding to the variable number jvar
  const unsigned int pvar =
      ((res_or_jac == JacRes::CALCULATE_JACOBIAN) ? _dictator.porousFlowVariableNum(jvar) : 0);

  DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), jvar);
  if ((ke.n() == 0) && (res_or_jac == JacRes::CALCULATE_JACOBIAN)) // this removes a problem
                                                                   // encountered in the initial
                                                                   // timestep when
                                                                   // use_displaced_mesh=true
    return;

  // The number of nodes in the element
  const unsigned int num_nodes = _test.size();

  // Compute the residual and jacobian without the mobility terms. Even if we are computing the
  // Jacobian we still need this in order to see which nodes are upwind and which are downwind.
  for (unsigned ph = 0; ph < _num_phases; ++ph)
  {
    _proto_flux[ph].assign(num_nodes, 0);
    for (_qp = 0; _qp < _qrule->n_points(); _qp++)
    {
      for (_i = 0; _i < num_nodes; ++_i)
        _proto_flux[ph][_i] += _JxW[_qp] * _coord[_qp] * darcyQp(ph);
    }
  }

  // for this element, record whether each node is "upwind" or "downwind" (or neither)
  const unsigned elem = _current_elem->id();
  if (_num_upwinds.find(elem) == _num_upwinds.end())
  {
    _num_upwinds[elem] = std::vector<std::vector<unsigned>>(_num_phases);
    _num_downwinds[elem] = std::vector<std::vector<unsigned>>(_num_phases);
    for (unsigned ph = 0; ph < _num_phases; ++ph)
    {
      _num_upwinds[elem][ph].assign(num_nodes, 0);
      _num_downwinds[elem][ph].assign(num_nodes, 0);
    }
  }
  // record the information once per nonlinear iteration
  if (res_or_jac == JacRes::CALCULATE_JACOBIAN && jvar == _var.number())
  {
    for (unsigned ph = 0; ph < _num_phases; ++ph)
    {
      for (unsigned nod = 0; nod < num_nodes; ++nod)
      {
        if (_proto_flux[ph][nod] > 0)
          _num_upwinds[elem][ph][nod]++;
        else if (_proto_flux[ph][nod] < 0)
          _num_downwinds[elem][ph][nod]++;
      }
    }
  }

  // based on _num_upwinds and _num_downwinds, calculate the maximum number
  // of upwind-downwind swaps that have been encountered in this timestep
  // for this element
  std::vector<unsigned> max_swaps(_num_phases, 0);
  for (unsigned ph = 0; ph < _num_phases; ++ph)
  {
    for (unsigned nod = 0; nod < num_nodes; ++nod)
      max_swaps[ph] = std::max(
          max_swaps[ph], std::min(_num_upwinds[elem][ph][nod], _num_downwinds[elem][ph][nod]));
  }

  // size the _jacobian correctly and calculate it for the case residual = _proto_flux
  if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
  {
    for (unsigned ph = 0; ph < _num_phases; ++ph)
    {
      _jacobian[ph].resize(ke.m());
      for (_i = 0; _i < _test.size(); _i++)
      {
        _jacobian[ph][_i].assign(ke.n(), 0.0);
        for (_j = 0; _j < _phi.size(); _j++)
          for (_qp = 0; _qp < _qrule->n_points(); _qp++)
            _jacobian[ph][_i][_j] += _JxW[_qp] * _coord[_qp] * darcyQpJacobian(jvar, ph);
      }
    }
  }

  // Loop over all the phases, computing the mass flux, which
  // gets placed into _proto_flux, and the derivative of this
  // which gets placed into _jacobian
  for (unsigned int ph = 0; ph < _num_phases; ++ph)
  {
    if (max_swaps[ph] < _full_upwind_threshold)
      fullyUpwind(res_or_jac, ph, pvar);
    else
    {
      switch (_fallback_scheme)
      {
        case FallbackEnum::QUICK:
          quickUpwind(res_or_jac, ph, pvar);
          break;
        case FallbackEnum::HARMONIC:
          harmonicMean(res_or_jac, ph, pvar);
          break;
      }
    }
  }

  // Add results to the Residual or Jacobian
  if (res_or_jac == JacRes::CALCULATE_RESIDUAL)
  {
    DenseVector<Number> & re = _assembly.residualBlock(_var.number());

    _local_re.resize(re.size());
    _local_re.zero();
    for (_i = 0; _i < _test.size(); _i++)
      for (unsigned int ph = 0; ph < _num_phases; ++ph)
        _local_re(_i) += _proto_flux[ph][_i];

    re += _local_re;

    if (_has_save_in)
    {
      Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
      for (unsigned int i = 0; i < _save_in.size(); i++)
        _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
    }
  }

  if (res_or_jac == JacRes::CALCULATE_JACOBIAN)
  {
    _local_ke.resize(ke.m(), ke.n());
    _local_ke.zero();

    for (_i = 0; _i < _test.size(); _i++)
      for (_j = 0; _j < _phi.size(); _j++)
        for (unsigned int ph = 0; ph < _num_phases; ++ph)
          _local_ke(_i, _j) += _jacobian[ph][_i][_j];

    ke += _local_ke;

    if (_has_diag_save_in && jvar == _var.number())
    {
      unsigned int rows = ke.m();
      DenseVector<Number> diag(rows);
      for (unsigned int i = 0; i < rows; i++)
        diag(i) = _local_ke(i, i);

      Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
      for (unsigned int i = 0; i < _diag_save_in.size(); i++)
        _diag_save_in[i]->sys().solution().add_vector(diag, _diag_save_in[i]->dofIndices());
    }
  }
}