LCOV - code coverage report
Current view: top level - src/materials - KKSPhaseConcentrationMaterial.C (source / functions) Hit Total Coverage
Test: idaholab/moose phase_field: #31405 (292dce) with base fef103 Lines: 135 152 88.8 %
Date: 2025-09-04 07:55:36 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "KKSPhaseConcentrationMaterial.h"
      11             : #include "MatrixTools.h"
      12             : 
      13             : registerMooseObject("PhaseFieldApp", KKSPhaseConcentrationMaterial);
      14             : 
      15             : InputParameters
      16          94 : KKSPhaseConcentrationMaterial::validParams()
      17             : {
      18          94 :   InputParameters params = DerivativeMaterialInterface<Material>::validParams();
      19          94 :   params.addClassDescription("Computes the KKS phase concentrations by using nested Newton "
      20             :                              "iteration to solve the equal chemical "
      21             :                              "potential and concentration conservation equations. This class is "
      22             :                              "intended to be used with KKSPhaseConcentrationDerivatives.");
      23         188 :   params.addRequiredCoupledVar("global_cs", "The interpolated concentrations c, b, etc.");
      24         188 :   params.addRequiredParam<MaterialPropertyName>("h_name", "Switching function h(eta).");
      25         188 :   params.addRequiredParam<std::vector<MaterialPropertyName>>(
      26             :       "ci_names",
      27             :       "Phase concentrations. The order must match Fa, Fb, and global_cs, for example, c1, c2, b1, "
      28             :       "b2, etc.");
      29         188 :   params.addRequiredParam<std::vector<Real>>("ci_IC",
      30             :                                              "Initial values of ci in the same order as ci_names.");
      31         188 :   params.addRequiredParam<MaterialName>("fa_name", "Fa material object.");
      32         188 :   params.addRequiredParam<MaterialName>("fb_name", "Fb material object.");
      33         188 :   params.addParam<MaterialPropertyName>(
      34             :       "nested_iterations",
      35             :       "The output number of nested Newton iterations at each quadrature point.");
      36         188 :   params.addCoupledVar("args", "The coupled variables of Fa and Fb.");
      37         188 :   params.addParam<bool>(
      38         188 :       "damped_Newton", false, "Whether or not to use the damped Newton's method.");
      39         188 :   params.addParam<MaterialName>("conditions",
      40             :                                 "C",
      41             :                                 "Material property that checks bounds and conditions on the "
      42             :                                 "material properties being solved for.");
      43          94 :   params += NestedSolve::validParams();
      44          94 :   return params;
      45           0 : }
      46             : 
      47          72 : KKSPhaseConcentrationMaterial::KKSPhaseConcentrationMaterial(const InputParameters & parameters)
      48             :   : DerivativeMaterialInterface<Material>(parameters),
      49          72 :     _prop_c(coupledValues("global_cs")),
      50          72 :     _num_c(coupledComponents("global_cs")),
      51         144 :     _prop_h(getMaterialProperty<Real>("h_name")),
      52         144 :     _ci_names(getParam<std::vector<MaterialPropertyName>>("ci_names")),
      53          72 :     _prop_ci(_num_c * 2),
      54          72 :     _ci_old(_num_c * 2),
      55         144 :     _ci_IC(getParam<std::vector<Real>>("ci_IC")),
      56          72 :     _Fa_name(getParam<MaterialName>("fa_name")),
      57          72 :     _Fb_name(getParam<MaterialName>("fb_name")),
      58          72 :     _prop_Fi(2),
      59          72 :     _Fi_copy(2),
      60          72 :     _dFidci(_num_c * 2),
      61          72 :     _dFidci_copy(_num_c * 2),
      62          72 :     _d2Fidcidbi(2),
      63          72 :     _d2Fadc1db1_copy(_num_c),
      64          72 :     _args_names(coupledNames("args")),
      65          72 :     _n_args(coupledComponents("args")),
      66          72 :     _dFadarg(_n_args),
      67          72 :     _dFadarg_copy(_n_args),
      68          72 :     _dFbdarg(_n_args),
      69          72 :     _dFbdarg_copy(_n_args),
      70          72 :     _d2Fadcadarg(_n_args),
      71          72 :     _d2Fadcadarg_copy(_n_args),
      72          72 :     _iter(declareProperty<Real>("nested_iterations")),
      73         144 :     _abs_tol(getParam<Real>("absolute_tolerance")),
      74         144 :     _rel_tol(getParam<Real>("relative_tolerance")),
      75         144 :     _damped_newton(getParam<bool>("damped_Newton")),
      76          72 :     _condition_name(getParam<MaterialName>("conditions")),
      77         144 :     _nested_solve(NestedSolve(parameters))
      78             : 
      79             : {
      80             :   // phase concentrations
      81         216 :   for (const auto m : make_range(_num_c * 2))
      82             :   {
      83         144 :     _prop_ci[m] = &declareProperty<Real>(_ci_names[m]);
      84         144 :     _ci_old[m] = &getMaterialPropertyOld<Real>(_ci_names[m]);
      85             :   }
      86             : 
      87             :   // free energies
      88          72 :   _prop_Fi[0] = &getMaterialPropertyByName<Real>(_Fa_name);
      89          72 :   _prop_Fi[1] = &getMaterialPropertyByName<Real>(_Fb_name);
      90             : 
      91             :   // declare _fi_copy to be passed to the kernels
      92          72 :   _Fi_copy[0] = &declareProperty<Real>("cp" + _Fa_name);
      93          72 :   _Fi_copy[1] = &declareProperty<Real>("cp" + _Fb_name);
      94             : 
      95             :   // derivative of free energy wrt phase concentrations
      96         144 :   for (const auto m : make_range(_num_c))
      97             :   {
      98          72 :     _dFidci[m * 2] = &getMaterialPropertyDerivative<Real>(_Fa_name, _ci_names[m * 2]);
      99          72 :     _dFidci[m * 2 + 1] = &getMaterialPropertyDerivative<Real>(_Fb_name, _ci_names[m * 2 + 1]);
     100             : 
     101             :     // declare _dFidci_copy to be passed to the kernels
     102          72 :     _dFidci_copy[m * 2] = &declarePropertyDerivative<Real>("cp" + _Fa_name, _ci_names[m * 2]);
     103          72 :     _dFidci_copy[m * 2 + 1] =
     104         144 :         &declarePropertyDerivative<Real>("cp" + _Fb_name, _ci_names[m * 2 + 1]);
     105             :   }
     106             : 
     107             :   // Second derivative of free energy wrt phase concentrations for use in this material. In
     108             :   // _d2Fidcidbi[m][n][l], m is phase index of Fi, n is the species index of ci, l is the species
     109             :   // index of bi.
     110         216 :   for (const auto m : make_range(2))
     111             :   {
     112         144 :     _d2Fidcidbi[m].resize(_num_c);
     113         288 :     for (const auto n : make_range(_num_c))
     114             :     {
     115         144 :       _d2Fidcidbi[m][n].resize(_num_c);
     116         288 :       for (const auto l : make_range(_num_c))
     117             :       {
     118         144 :         if (m == 0)
     119          72 :           _d2Fidcidbi[0][n][l] =
     120         144 :               &getMaterialPropertyDerivative<Real>(_Fa_name, _ci_names[n * 2], _ci_names[l * 2]);
     121             : 
     122             :         else
     123          72 :           _d2Fidcidbi[1][n][l] = &getMaterialPropertyDerivative<Real>(
     124          72 :               _Fb_name, _ci_names[n * 2 + 1], _ci_names[l * 2 + 1]);
     125             :       }
     126             :     }
     127             :   }
     128             : 
     129             :   // _d2Fadc1db1_copy (2D symmetric matrix), to be passed to kernels
     130         144 :   for (const auto m : make_range(_num_c))
     131             :   {
     132          72 :     _d2Fadc1db1_copy[m].resize(_num_c);
     133         144 :     for (const auto n : make_range(_num_c))
     134          72 :       _d2Fadc1db1_copy[m][n] =
     135         144 :           &declarePropertyDerivative<Real>("cp" + _Fa_name, _ci_names[m * 2], _ci_names[n * 2]);
     136             :   }
     137             : 
     138             :   // partial derivative of Fa and Fb wrt coupled variables, to be passed to kernels
     139          72 :   for (const auto m : make_range(_n_args))
     140             :   {
     141           0 :     _dFadarg[m] = &getMaterialPropertyDerivative<Real>(_Fa_name, _args_names[m]);
     142           0 :     _dFadarg_copy[m] = &declarePropertyDerivative<Real>("cp" + _Fa_name, _args_names[m]);
     143           0 :     _dFbdarg[m] = &getMaterialPropertyDerivative<Real>(_Fb_name, _args_names[m]);
     144           0 :     _dFbdarg_copy[m] = &declarePropertyDerivative<Real>("cp" + _Fb_name, _args_names[m]);
     145             :   }
     146             : 
     147             :   // second partial derivatives of Fa wrt ca and another coupled variable, to be passed to
     148             :   // kernels
     149          72 :   for (const auto m : make_range(_n_args))
     150             :   {
     151           0 :     _d2Fadcadarg[m].resize(_num_c);
     152           0 :     _d2Fadcadarg_copy[m].resize(_num_c);
     153           0 :     for (const auto n : make_range(_num_c))
     154             :     {
     155           0 :       _d2Fadcadarg[m][n] =
     156           0 :           &getMaterialPropertyDerivative<Real>(_Fa_name, _ci_names[n * 2], _args_names[m]);
     157           0 :       _d2Fadcadarg_copy[m][n] =
     158           0 :           &declarePropertyDerivative<Real>("cp" + _Fa_name, _ci_names[n * 2], _args_names[m]);
     159             :     }
     160             :   }
     161             : 
     162          72 :   if (_damped_newton)
     163          36 :     _C = &getMaterialPropertyByName<Real>(_condition_name);
     164             :   else
     165          36 :     _C = nullptr;
     166          72 : }
     167             : 
     168             : void
     169       21600 : KKSPhaseConcentrationMaterial::initQpStatefulProperties()
     170             : {
     171       64800 :   for (const auto m : make_range(_num_c * 2))
     172       43200 :     (*_prop_ci[m])[_qp] = _ci_IC[m];
     173       21600 : }
     174             : 
     175             : void
     176          72 : KKSPhaseConcentrationMaterial::initialSetup()
     177             : {
     178          72 :   _Fa = &getMaterial("fa_name");
     179          72 :   _Fb = &getMaterial("fb_name");
     180          72 :   if (_damped_newton)
     181          36 :     _condition = &getMaterialByName(_condition_name);
     182          72 : }
     183             : 
     184             : void
     185     1884600 : KKSPhaseConcentrationMaterial::computeQpProperties()
     186             : {
     187             :   // parameters for nested Newton iteration
     188     1884600 :   NestedSolve::Value<> solution(_num_c * 2);
     189             : 
     190     5653800 :   for (unsigned int m = 0; m < _num_c * 2; ++m)
     191     3769200 :     solution(m) = (*_ci_old[m])[_qp];
     192             : 
     193     1884600 :   _nested_solve.setAbsoluteTolerance(_abs_tol);
     194     1884600 :   _nested_solve.setRelativeTolerance(_rel_tol);
     195             : 
     196     6047394 :   auto compute = [&](const NestedSolve::Value<> & guess,
     197             :                      NestedSolve::Value<> & residual,
     198             :                      NestedSolve::Jacobian<> & jacobian)
     199             :   {
     200    18142182 :     for (const auto m : make_range(_num_c * 2))
     201    12094788 :       (*_prop_ci[m])[_qp] = guess(m);
     202             : 
     203     6047394 :     _Fa->computePropertiesAtQp(_qp);
     204     6047394 :     _Fb->computePropertiesAtQp(_qp);
     205             : 
     206             :     // assign residual functions
     207    12094788 :     for (const auto m : make_range(_num_c))
     208             :     {
     209     6047394 :       residual(m * 2) = (*_dFidci[m * 2])[_qp] - (*_dFidci[m * 2 + 1])[_qp];
     210     6047394 :       residual(m * 2 + 1) = (1 - _prop_h[_qp]) * (*_prop_ci[m * 2])[_qp] +
     211     6047394 :                             _prop_h[_qp] * (*_prop_ci[m * 2 + 1])[_qp] - (*_prop_c[m])[_qp];
     212             :     }
     213             : 
     214             :     jacobian.setZero();
     215             : 
     216             :     // fill in the non-zero elements in jacobian
     217    12094788 :     for (const auto m : make_range(_num_c))
     218             :     {
     219    12094788 :       for (const auto n : make_range(_num_c))
     220             :       {
     221             :         // equal chemical potential derivative equations
     222     6047394 :         jacobian(m * 2, n * 2) = (*_d2Fidcidbi[0][m][n])[_qp];
     223     6047394 :         jacobian(m * 2, n * 2 + 1) = -(*_d2Fidcidbi[1][m][n])[_qp];
     224             :       }
     225             :       // concentration conservation derivative equations
     226     6047394 :       jacobian(m * 2 + 1, m * 2) = 1 - _prop_h[_qp];
     227     6047394 :       jacobian(m * 2 + 1, m * 2 + 1) = _prop_h[_qp];
     228             :     }
     229     7931994 :   };
     230    23620316 :   auto computeCondition = [&](const NestedSolve::Value<> & guess) -> Real
     231             :   {
     232    70860948 :     for (const auto m : make_range(_num_c * 2))
     233    47240632 :       (*_prop_ci[m])[_qp] = guess(m);
     234    23620316 :     _condition->computePropertiesAtQp(_qp);
     235    23620316 :     return ((*_C)[_qp]);
     236     1884600 :   };
     237             :   // choose between Newton or damped Newton's method
     238     1884600 :   if (!_damped_newton)
     239      934200 :     _nested_solve.nonlinear(solution, compute);
     240             :   else
     241      950400 :     _nested_solve.nonlinearDamped(solution, compute, computeCondition);
     242     1884600 :   _iter[_qp] = _nested_solve.getIterations();
     243             : 
     244     1884600 :   if (_nested_solve.getState() == NestedSolve::State::NOT_CONVERGED)
     245           0 :     mooseException("Nested Newton iteration did not converge.");
     246             : 
     247             :   // assign solution to ci
     248     5653800 :   for (const auto m : make_range(_num_c * 2))
     249     3769200 :     (*_prop_ci[m])[_qp] = solution[m];
     250             : 
     251             :   // assign to the copied parameters to be used in kernels
     252     5653800 :   for (const auto m : make_range(2))
     253     3769200 :     (*_Fi_copy[m])[_qp] = (*_prop_Fi[m])[_qp];
     254             : 
     255     5653800 :   for (const auto m : make_range(_num_c * 2))
     256     3769200 :     (*_dFidci_copy[m])[_qp] = (*_dFidci[m])[_qp];
     257             : 
     258     3769200 :   for (const auto m : make_range(_num_c))
     259             :   {
     260     3769200 :     for (const auto n : make_range(_num_c))
     261     1884600 :       (*_d2Fadc1db1_copy[m][n])[_qp] = (*_d2Fidcidbi[0][m][n])[_qp];
     262             :   }
     263             : 
     264     1884600 :   for (const auto m : make_range(_n_args))
     265             :   {
     266           0 :     (*_dFadarg_copy[m])[_qp] = (*_dFadarg[m])[_qp];
     267           0 :     (*_dFbdarg_copy[m])[_qp] = (*_dFbdarg[m])[_qp];
     268             :   }
     269             : 
     270     1884600 :   for (const auto m : make_range(_n_args))
     271             :   {
     272           0 :     for (const auto n : make_range(_num_c))
     273           0 :       (*_d2Fadcadarg_copy[m][n])[_qp] = (*_d2Fadcadarg[m][n])[_qp];
     274             :   }
     275     1884600 : }

Generated by: LCOV version 1.14