LCOV - code coverage report
Current view: top level - src/materials - KKSPhaseConcentrationMultiPhaseMaterial.C (source / functions) Hit Total Coverage
Test: idaholab/moose phase_field: #31405 (292dce) with base fef103 Lines: 116 121 95.9 %
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 "KKSPhaseConcentrationMultiPhaseMaterial.h"
      11             : #include "MatrixTools.h"
      12             : 
      13             : registerMooseObject("PhaseFieldApp", KKSPhaseConcentrationMultiPhaseMaterial);
      14             : 
      15             : InputParameters
      16          94 : KKSPhaseConcentrationMultiPhaseMaterial::validParams()
      17             : {
      18          94 :   InputParameters params = DerivativeMaterialInterface<Material>::validParams();
      19          94 :   params.addClassDescription(
      20             :       "Computes the KKS phase concentrations by using a nested Newton iteration "
      21             :       "to solve the equal chemical potential and concentration conservation equations for "
      22             :       "multiphase systems. This class is intented to be used with "
      23             :       "KKSPhaseConcentrationMultiPhaseDerivatives.");
      24         188 :   params.addRequiredCoupledVar("global_cs", "The interpolated concentrations c, b, etc.");
      25         188 :   params.addRequiredCoupledVar("all_etas", "Order parameters.");
      26         188 :   params.addRequiredParam<std::vector<MaterialPropertyName>>(
      27             :       "hj_names", "Switching functions in the same order as all_etas.");
      28         188 :   params.addRequiredParam<std::vector<MaterialName>>(
      29             :       "Fj_names", "Free energy material objects in the same order as all_etas.");
      30         188 :   params.addRequiredParam<std::vector<MaterialPropertyName>>(
      31             :       "ci_names",
      32             :       "Phase concentrations. They must have the same order as Fj_names and global_cs, for "
      33             :       "example, c1, c2, b1, b2.");
      34         188 :   params.addRequiredParam<std::vector<Real>>("ci_IC",
      35             :                                              "Initial values of ci in the same order of ci_names");
      36         188 :   params.addParam<MaterialPropertyName>(
      37             :       "nested_iterations",
      38             :       "The output number of nested Newton iterations at each quadrature point.");
      39         188 :   params.addCoupledVar("args", "The coupled variables of free energies.");
      40         188 :   params.addParam<bool>(
      41         188 :       "damped_Newton", false, "Whether or not to use the damped Newton's method.");
      42         188 :   params.addParam<MaterialName>("conditions",
      43             :                                 "C",
      44             :                                 "Material property that checks bounds and conditions on the "
      45             :                                 "material properties being solved for.");
      46          94 :   params += NestedSolve::validParams();
      47          94 :   return params;
      48           0 : }
      49             : 
      50          72 : KKSPhaseConcentrationMultiPhaseMaterial::KKSPhaseConcentrationMultiPhaseMaterial(
      51          72 :     const InputParameters & parameters)
      52             :   : DerivativeMaterialInterface<Material>(parameters),
      53          72 :     _prop_c(coupledValues("global_cs")),
      54          72 :     _num_c(coupledComponents("global_cs")),
      55          72 :     _num_j(coupledComponents("all_etas")),
      56         144 :     _hj_names(getParam<std::vector<MaterialPropertyName>>("hj_names")),
      57          72 :     _prop_hj(_num_j),
      58         144 :     _Fj_names(getParam<std::vector<MaterialName>>("Fj_names")),
      59          72 :     _prop_Fi(_num_j),
      60         144 :     _ci_names(getParam<std::vector<MaterialPropertyName>>("ci_names")),
      61          72 :     _prop_ci(_num_c * _num_j),
      62          72 :     _ci_old(_num_c * _num_j),
      63         144 :     _ci_IC(getParam<std::vector<Real>>("ci_IC")),
      64          72 :     _dFidci(_num_j),
      65          72 :     _d2Fidcidbi(_num_j),
      66          72 :     _args_names(coupledNames("args")),
      67          72 :     _n_args(coupledComponents("args")),
      68          72 :     _dFidarg(_num_j),
      69          72 :     _d2F1dc1darg(_num_c),
      70          72 :     _iter(declareProperty<Real>("nested_iterations")),
      71         144 :     _abs_tol(getParam<Real>("absolute_tolerance")),
      72         144 :     _rel_tol(getParam<Real>("relative_tolerance")),
      73         144 :     _damped_newton(getParam<bool>("damped_Newton")),
      74          72 :     _condition_name(getParam<MaterialName>("conditions")),
      75         144 :     _nested_solve(NestedSolve(parameters))
      76             : 
      77             : {
      78             :   // phase concentrations
      79         288 :   for (const auto m : make_range(_num_c * _num_j))
      80             :   {
      81         216 :     _ci_old[m] = &getMaterialPropertyOld<Real>(_ci_names[m]);
      82         216 :     _prop_ci[m] = &declareProperty<Real>(_ci_names[m]);
      83             :   }
      84             : 
      85             :   // free energies
      86         288 :   for (const auto m : make_range(_num_j))
      87         216 :     _prop_Fi[m] = &getMaterialPropertyByName<Real>(_Fj_names[m]);
      88             : 
      89             :   // free energy derivatives w.r.t. phase concentrations
      90         288 :   for (const auto m : make_range(_num_j))
      91             :   {
      92         216 :     _prop_hj[m] = &getMaterialPropertyByName<Real>(_hj_names[m]);
      93             : 
      94         216 :     _dFidci[m].resize(_num_c);
      95         216 :     _d2Fidcidbi[m].resize(_num_c);
      96             : 
      97         432 :     for (const auto n : make_range(_num_c))
      98             :     {
      99         216 :       _dFidci[m][n] = &getMaterialPropertyDerivative<Real>(_Fj_names[m], _ci_names[m + n * _num_j]);
     100             : 
     101         216 :       _d2Fidcidbi[m][n].resize(_num_c);
     102             : 
     103         432 :       for (unsigned int l = 0; l < _num_c; ++l)
     104             :       {
     105         216 :         _d2Fidcidbi[m][n][l] = &getMaterialPropertyDerivative<Real>(
     106         216 :             _Fj_names[m], _ci_names[m + n * _num_j], _ci_names[m + l * _num_j]);
     107             :       }
     108             :     }
     109             :   }
     110             : 
     111             :   // derivative of free energies wrt coupled variables
     112         288 :   for (const auto m : make_range(_num_j))
     113             :   {
     114         216 :     _dFidarg[m].resize(_n_args);
     115             : 
     116         216 :     for (const auto n : make_range(_n_args))
     117           0 :       _dFidarg[m][n] = &getMaterialPropertyDerivative<Real>(_Fj_names[m], _args_names[n]);
     118             :   }
     119             : 
     120             :   // second derivatives of F1 wrt c1 and other coupled variables
     121         144 :   for (const auto m : make_range(_num_c))
     122             :   {
     123          72 :     _d2F1dc1darg[m].resize(_n_args);
     124             : 
     125          72 :     for (const auto n : make_range(_n_args))
     126             :     {
     127           0 :       _d2F1dc1darg[m][n] =
     128           0 :           &getMaterialPropertyDerivative<Real>(_Fj_names[0], _ci_names[m * _num_j], _args_names[n]);
     129             :     }
     130             :   }
     131             : 
     132          72 :   if (_damped_newton)
     133          36 :     _C = &getMaterialPropertyByName<Real>(_condition_name);
     134             :   else
     135          36 :     _C = nullptr;
     136          72 : }
     137             : 
     138             : void
     139       38400 : KKSPhaseConcentrationMultiPhaseMaterial::initQpStatefulProperties()
     140             : {
     141      153600 :   for (const auto m : make_range(_num_c * _num_j))
     142      115200 :     (*_prop_ci[m])[_qp] = _ci_IC[m];
     143       38400 : }
     144             : 
     145             : void
     146          72 : KKSPhaseConcentrationMultiPhaseMaterial::initialSetup()
     147             : {
     148          72 :   _Fj_mat.resize(_num_j);
     149             : 
     150         288 :   for (unsigned int m = 0; m < _num_j; ++m)
     151         216 :     _Fj_mat[m] = &getMaterialByName(_Fj_names[m]);
     152          72 :   if (_damped_newton)
     153          36 :     _condition = &getMaterialByName(_condition_name);
     154          72 : }
     155             : 
     156             : void
     157     2454400 : KKSPhaseConcentrationMultiPhaseMaterial::computeQpProperties()
     158             : {
     159             :   // parameters for nested Newton iteration
     160     2454400 :   NestedSolve::Value<> solution(_num_c * _num_j);
     161             : 
     162             :   // initialize first guess using the solution from previous step
     163     9817600 :   for (const auto m : make_range(_num_c * _num_j))
     164     7363200 :     solution(m) = (*_ci_old[m])[_qp];
     165             : 
     166     2454400 :   _nested_solve.setAbsoluteTolerance(_abs_tol);
     167     2454400 :   _nested_solve.setRelativeTolerance(_rel_tol);
     168             : 
     169     8180389 :   auto compute = [&](const NestedSolve::Value<> & guess,
     170             :                      NestedSolve::Value<> & residual,
     171             :                      NestedSolve::Jacobian<> & jacobian)
     172             :   {
     173    32721556 :     for (const auto m : make_range(_num_c * _num_j))
     174    24541167 :       (*_prop_ci[m])[_qp] = guess(m);
     175             : 
     176    32721556 :     for (const auto m : make_range(_num_j))
     177    24541167 :       _Fj_mat[m]->computePropertiesAtQp(_qp);
     178             : 
     179             :     // assign residual functions
     180    16360778 :     for (const auto m : make_range(_num_c))
     181             :     {
     182    24541167 :       for (const auto n : make_range(_num_j - 1))
     183    16360778 :         residual(m * _num_j + n) = (*_dFidci[n][m])[_qp] - (*_dFidci[n + 1][m])[_qp];
     184             : 
     185     8180389 :       residual((m + 1) * _num_j - 1) = -(*_prop_c[m])[_qp];
     186             : 
     187    32721556 :       for (const auto l : make_range(_num_j))
     188    24541167 :         residual((m + 1) * _num_j - 1) += (*_prop_hj[l])[_qp] * (*_prop_ci[m * _num_j + l])[_qp];
     189             :     }
     190             : 
     191             :     jacobian.setZero();
     192             : 
     193             :     // fill in the non-zero terms in jacobian
     194    16360778 :     for (const auto m : make_range(_num_c))
     195             :     {
     196             :       // equal chemical potential derivative equations
     197    24541167 :       for (const auto n : make_range(_num_j - 1))
     198             :       {
     199    32721556 :         for (const auto l : make_range(_num_c))
     200             :         {
     201    16360778 :           jacobian(m * _num_j + n, n + l * _num_j) = (*_d2Fidcidbi[n][m][l])[_qp];
     202    16360778 :           jacobian(m * _num_j + n, n + l * _num_j + 1) = -(*_d2Fidcidbi[n + 1][m][l])[_qp];
     203             :         }
     204             :       }
     205             : 
     206             :       // concentration conservation derivative equations
     207    32721556 :       for (const auto n : make_range(_num_j))
     208    24541167 :         jacobian((m + 1) * _num_j - 1, m * _num_j + n) = (*_prop_hj[n])[_qp];
     209             :     }
     210    10634789 :   };
     211             : 
     212    22344276 :   auto computeCondition = [&](const NestedSolve::Value<> & guess) -> Real
     213             :   {
     214    89377104 :     for (const auto m : make_range(_num_c * _num_j))
     215    67032828 :       (*_prop_ci[m])[_qp] = guess(m);
     216    22344276 :     _condition->computePropertiesAtQp(_qp);
     217    22344276 :     return ((*_C)[_qp]);
     218     2454400 :   };
     219             : 
     220             :   // choose between Newton or damped Newton's method
     221     2454400 :   if (!_damped_newton)
     222     1329600 :     _nested_solve.nonlinear(solution, compute);
     223             :   else
     224     1124800 :     _nested_solve.nonlinearDamped(solution, compute, computeCondition);
     225             : 
     226     2454400 :   _iter[_qp] = _nested_solve.getIterations();
     227             : 
     228     2454400 :   if (_nested_solve.getState() == NestedSolve::State::NOT_CONVERGED)
     229           0 :     mooseException("Nested Newton iteration did not converge.");
     230             : 
     231             :   // assign solution to ci
     232     9817600 :   for (const auto m : make_range(_num_c * _num_j))
     233     7363200 :     (*_prop_ci[m])[_qp] = solution[m];
     234     2454400 : }

Generated by: LCOV version 1.14