void KernelGrad::computeJacobian() { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), _var.number()); _local_ke.resize(ke.m(), ke.n()); _local_ke.zero(); const unsigned int n_test = _test.size(); for (_qp = 0; _qp < _qrule->n_points(); _qp++) for (_j = 0; _j < _phi.size(); _j++) { RealGradient value = precomputeQpJacobian() * _JxW[_qp] * _coord[_qp]; for (_i = 0; _i < n_test; _i++) // target for auto vectorization _local_ke(_i, _j) += value * _grad_test[_i][_qp]; } ke += _local_ke; if (_has_diag_save_in) { const unsigned int rows = ke.m(); DenseVector<Number> diag(rows); for (unsigned int i = 0; i < rows; i++) // target for auto vectorization 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()); } }
void KernelValue::computeJacobian() { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), _var.number()); _local_ke.resize(ke.m(), ke.n()); _local_ke.zero(); for (_qp = 0; _qp < _qrule->n_points(); _qp++) { for (_j = 0; _j < _phi.size(); _j++) { // NOTE: is it possible to move this out of the for-loop and multiply the _value by _phi[_j][_qp] _value = precomputeQpJacobian(); for (_i = 0; _i < _test.size(); _i++) _local_ke(_i, _j) += _JxW[_qp]*_coord[_qp]*_value*_test[_i][_qp]; } } ke += _local_ke; if (_has_diag_save_in) { 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()); } }
void EigenKernel::computeJacobian() { if (!_is_implicit) return; DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), _var.number()); _local_ke.resize(ke.m(), ke.n()); _local_ke.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 (_j = 0; _j < _phi.size(); _j++) for (_qp = 0; _qp < _qrule->n_points(); _qp++) _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * one_over_eigen * computeQpJacobian(); ke += _local_ke; if (_has_diag_save_in) { 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()); } }
void ADIntegratedBCTempl<T, compute_stage>::computeJacobian() { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), _var.number()); _local_ke.resize(ke.m(), ke.n()); _local_ke.zero(); size_t ad_offset = _var.number() * _sys.getMaxVarNDofsPerElem(); for (_qp = 0; _qp < _qrule->n_points(); _qp++) for (_i = 0; _i < _test.size(); _i++) { DualReal residual = computeQpResidual(); for (_j = 0; _j < _var.phiSize(); ++_j) _local_ke(_i, _j) += (_ad_JxW[_qp] * _coord[_qp] * residual).derivatives()[ad_offset + _j]; } ke += _local_ke; if (_has_diag_save_in) { 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()); } }
void Kernel::computeJacobian() { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.index(), _var.index()); _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] * computeQpJacobian(); ke += _local_ke; if (_has_diag_save_in) { 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()); } }
void StressDivergenceTruss::computeJacobian() { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), _var.number()); _local_ke.resize(ke.m(), ke.n()); _local_ke.zero(); ColumnMajorMatrix stiff_global(3,3); computeStiffness( stiff_global ); for (_i = 0; _i < _test.size(); _i++) { for (_j = 0; _j < _phi.size(); _j++) { int sign( _i == _j ? 1 : -1 ); _local_ke(_i, _j) += sign * stiff_global(_component, _component); } } ke += _local_ke; if(_has_diag_save_in) { 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()); } }
void InertialForceBeam::computeJacobian() { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), _var.number()); _local_ke.resize(ke.m(), ke.n()); _local_ke.zero(); mooseAssert(_beta > 0.0, "InertialForceBeam: Beta parameter should be positive."); Real factor = 0.0; if (isParamValid("beta")) factor = 1.0 / (_beta * _dt * _dt) + _eta[0] * (1.0 + _alpha) * _gamma / _beta / _dt; else factor = (*_du_dotdot_du)[0] + _eta[0] * (1.0 + _alpha) * (*_du_dot_du)[0]; for (unsigned int i = 0; i < _test.size(); ++i) { for (unsigned int j = 0; j < _phi.size(); ++j) { if (_component < 3) _local_ke(i, j) = (i == j ? 1.0 / 3.0 : 1.0 / 6.0) * _density[0] * _area[0] * _original_length[0] * factor; else if (_component > 2) { RankTwoTensor I; if (isParamValid("Ix")) I(0, 0) = _Ix[0]; else I(0, 0) = _Iy[0] + _Iz[0]; I(1, 1) = _Iz[0]; I(2, 2) = _Iy[0]; // conversion from local config to global coordinate system RankTwoTensor Ig = _original_local_config[0].transpose() * I * _original_local_config[0]; _local_ke(i, j) = (i == j ? 1.0 / 3.0 : 1.0 / 6.0) * _density[0] * Ig(_component - 3, _component - 3) * _original_length[0] * factor; } } } ke += _local_ke; if (_has_diag_save_in) { 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()); } }
void StressDivergence::computeJacobian() { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), _var.number()); _local_ke.resize(ke.m(), ke.n()); _local_ke.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(3); _avg_grad_test[_i][_component] = 0.0; for (_qp = 0; _qp < _qrule->n_points(); _qp++) _avg_grad_test[_i][_component] += _grad_test[_i][_qp](_component) * _JxW[_qp] * _coord[_qp]; _avg_grad_test[_i][_component] /= _current_elem_volume; } _avg_grad_phi.resize(_phi.size()); for (_i = 0; _i < _phi.size(); _i++) { _avg_grad_phi[_i].resize(3); for (unsigned int component = 0; component < _mesh.dimension(); component++) { _avg_grad_phi[_i][component] = 0.0; for (_qp = 0; _qp < _qrule->n_points(); _qp++) _avg_grad_phi[_i][component] += _grad_phi[_i][_qp](component) * _JxW[_qp] * _coord[_qp]; _avg_grad_phi[_i][component] /= _current_elem_volume; } } } 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] * computeQpJacobian(); ke += _local_ke; if (_has_diag_save_in) { 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 (const auto & var : _diag_save_in) var->sys().solution().add_vector(diag, var->dofIndices()); } }
void FDKernel::computeOffDiagJacobian(unsigned int jvar_index) { DenseMatrix<Number> & ke = _assembly.jacobianBlock(_var.number(), jvar_index); DenseMatrix<Number> local_ke; local_ke.resize(ke.m(), ke.n()); local_ke.zero(); // FIXME: pull out the already computed element residual instead of recomputing it Real h; DenseVector<Number> re = perturbedResidual(_var.number(),0,0.0,h); for (_j = 0; _j < _phi.size(); _j++) { DenseVector<Number> p_re = perturbedResidual(jvar_index,_j,_scale,h); for (_i = 0; _i < _test.size(); _i++) { local_ke(_i,_j) = (p_re(_i) - re(_i))/h; } } ke += local_ke; if (jvar_index == _var.number()) { _local_ke = local_ke; if (_has_diag_save_in) { 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()); } } }
void IntegratedBC::computeJacobianBlock(MooseVariableFEBase & jvar) { size_t jvar_num = jvar.number(); prepareMatrixTag(_assembly, _var.number(), jvar_num); for (_qp = 0; _qp < _qrule->n_points(); _qp++) for (_i = 0; _i < _test.size(); _i++) for (_j = 0; _j < jvar.phiFaceSize(); _j++) { if (_var.number() == jvar_num) _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpJacobian(); else _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpOffDiagJacobian(jvar_num); } accumulateTaggedLocalMatrix(); }
void IntegratedBC::computeJacobianBlock(unsigned int jvar) { mooseDeprecated("The computeJacobianBlock method signature has changed. Developers, please " "pass in a MooseVariableFEBase reference instead of the variable number"); prepareMatrixTag(_assembly, _var.number(), jvar); for (_qp = 0; _qp < _qrule->n_points(); _qp++) for (_i = 0; _i < _test.size(); _i++) for (_j = 0; _j < _phi.size(); _j++) { if (_var.number() == jvar) _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpJacobian(); else _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpOffDiagJacobian(jvar); } accumulateTaggedLocalMatrix(); }
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); } } }
void NodalEqualValueConstraint::computeJacobian() { prepareMatrixTag(_assembly, _var.number(), _var.number()); // put zeroes on the diagonal (we have to do it, otherwise PETSc will complain!) for (unsigned int i = 0; i < _local_ke.m(); i++) for (unsigned int j = 0; j < _local_ke.n(); j++) _local_ke(i, j) = 0.; assignTaggedLocalMatrix(); for (unsigned int k = 0; k < _value.size(); k++) { prepareMatrixTag(_assembly, _var.number(), _val_number[k]); _local_ke(k, 0) = 1.; _local_ke(k, 1) = -1.; assignTaggedLocalMatrix(); } }
void IntegratedBC::computeJacobianBlockScalar(unsigned int jvar) { prepareMatrixTag(_assembly, _var.number(), jvar); MooseVariableScalar & jv = _sys.getScalarVariable(_tid, jvar); for (_qp = 0; _qp < _qrule->n_points(); _qp++) for (_i = 0; _i < _test.size(); _i++) for (_j = 0; _j < jv.order(); _j++) _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpOffDiagJacobian(jvar); accumulateTaggedLocalMatrix(); }
void IntegratedBC::computeJacobian() { prepareMatrixTag(_assembly, _var.number(), _var.number()); for (_qp = 0; _qp < _qrule->n_points(); _qp++) for (_i = 0; _i < _test.size(); _i++) for (_j = 0; _j < _phi.size(); _j++) _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpJacobian(); accumulateTaggedLocalMatrix(); if (_has_diag_save_in) { unsigned int rows = _local_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()); } }
void ODEKernel::computeJacobian() { prepareMatrixTag(_assembly, _var.number(), _var.number()); for (_i = 0; _i < _var.order(); _i++) for (_j = 0; _j < _var.order(); _j++) _local_ke(_i, _j) += computeQpJacobian(); accumulateTaggedLocalMatrix(); // compute off-diagonal jacobians wrt scalar variables const std::vector<MooseVariableScalar *> & scalar_vars = _sys.getScalarVariables(_tid); for (const auto & var : scalar_vars) computeOffDiagJacobian(var->number()); }
void ODEKernel::computeOffDiagJacobian(unsigned int jvar) { if (_sys.isScalarVariable(jvar)) { prepareMatrixTag(_assembly, _var.number(), jvar); MooseVariableScalar & var_j = _sys.getScalarVariable(_tid, jvar); for (_i = 0; _i < _var.order(); _i++) for (_j = 0; _j < var_j.order(); _j++) { if (jvar != _var.number()) _local_ke(_i, _j) += computeQpOffDiagJacobian(jvar); } accumulateTaggedLocalMatrix(); } }
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()); } } }
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()); } } }
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()); } } }