LCOV - code coverage report
Current view: top level - src/materials - ElectrochemicalSinteringMaterial.C (source / functions) Hit Total Coverage
Test: idaholab/moose phase_field: #31405 (292dce) with base fef103 Lines: 166 199 83.4 %
Date: 2025-09-04 07:55:36 Functions: 3 3 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 "ElectrochemicalSinteringMaterial.h"
      11             : #include "libmesh/quadrature.h"
      12             : #include "libmesh/utility.h"
      13             : 
      14             : registerMooseObject("PhaseFieldApp", ElectrochemicalSinteringMaterial);
      15             : 
      16             : InputParameters
      17          47 : ElectrochemicalSinteringMaterial::validParams()
      18             : {
      19          47 :   InputParameters params = Material::validParams();
      20          47 :   params.addClassDescription("Includes switching and thermodynamic properties for the grand "
      21             :                              "potential sintering model coupled with electrochemistry");
      22          94 :   params.addRequiredCoupledVarWithAutoBuild(
      23             :       "etas", "var_name_base", "op_num", "Array of order parameters that describe solid phase");
      24          94 :   params.addRequiredCoupledVar("chemical_potentials",
      25             :                                "The name of the chemical potential variables for defects");
      26          94 :   params.addRequiredCoupledVar("void_op", "The name of the void phase order parameter");
      27          94 :   params.addRequiredCoupledVar("Temperature", "Name of the temperature variable with units of K");
      28          94 :   params.addRequiredCoupledVar("electric_potential",
      29             :                                "Name of the electric potential variable with units of V");
      30          94 :   params.addParam<std::vector<MaterialPropertyName>>(
      31             :       "solid_energy_coefficients",
      32             :       "Vector of parabolic solid energy coefficients (energy*volume) for "
      33             :       "each defect species. Only used for parabolic energy. Place in same order as "
      34             :       "chemical_potentials!");
      35          94 :   params.addRequiredParam<std::vector<MaterialPropertyName>>(
      36             :       "void_energy_coefficients",
      37             :       "Vector of parabolic void energy coefficients (energy*volume) for each defect species. Place "
      38             :       "in same order as chemical_potentials!");
      39          94 :   params.addParam<Real>("surface_energy", 19.7, "Surface energy in units of problem (energy/area)");
      40          94 :   params.addParam<Real>(
      41          94 :       "grainboundary_energy", 9.86, "Grain boundary energy in units of problem (energy/area)");
      42          94 :   params.addParam<Real>("int_width", 1, "Interface width in units of problem (length)");
      43         141 :   params.addRangeCheckedParam<Real>(
      44             :       "surface_switch_value",
      45          94 :       0.3,
      46             :       "surface_switch_value >= 0 & surface_switch_value <= 1.0",
      47             :       "Value between 0 and 1 that determines when the interface begins to switch "
      48             :       "from surface to GB. Small values give less error while large values "
      49             :       "converge better.");
      50          94 :   params.addRequiredParam<std::vector<MaterialPropertyName>>(
      51             :       "min_vacancy_concentrations_solid",
      52             :       "Vector of names of materials that determine the minimum in energy wrt defect concentrations "
      53             :       "in the solid phase. Place in same order as chemical_potentials!");
      54          94 :   params.addRequiredParam<std::vector<Real>>("min_vacancy_concentrations_void",
      55             :                                              "Vector of minima in energy wrt defect concentrations "
      56             :                                              "in the void phase. Place in same order as "
      57             :                                              "chemical_potentials!");
      58          94 :   MooseEnum solid_energy_model("PARABOLIC DILUTE IDEAL", "PARABOLIC");
      59          94 :   params.addParam<MooseEnum>("solid_energy_model",
      60             :                              solid_energy_model,
      61             :                              "Type of energy function to use for the solid phase.");
      62          94 :   params.addRequiredParam<std::vector<int>>(
      63             :       "defect_charges",
      64             :       "Vector of charges of defect species. Place in same order as chemical_potentials!");
      65          94 :   params.addRequiredParam<Real>("solid_relative_permittivity",
      66             :                                 "Solid phase relative permittivity (dimensionless)");
      67          94 :   params.addParam<Real>("voltage_scale", 1, "Voltage scale (default is for voltage in V)");
      68          47 :   return params;
      69          47 : }
      70             : 
      71          36 : ElectrochemicalSinteringMaterial::ElectrochemicalSinteringMaterial(
      72          36 :     const InputParameters & parameters)
      73             :   : DerivativeMaterialInterface<Material>(parameters),
      74          36 :     _neta(coupledComponents("etas")),
      75          36 :     _eta(_neta),
      76          36 :     _eta_name(_neta),
      77          36 :     _ndefects(coupledComponents("chemical_potentials")),
      78          36 :     _w(coupledValues("chemical_potentials")),
      79          36 :     _w_name(_ndefects),
      80          36 :     _phi(coupledValue("void_op")),
      81          36 :     _phi_name(coupledName("void_op", 0)),
      82          72 :     _ns_min_names(getParam<std::vector<MaterialPropertyName>>("min_vacancy_concentrations_solid")),
      83          36 :     _ns_min(_ndefects),
      84          36 :     _dns_min(_ndefects),
      85          36 :     _d2ns_min(_ndefects),
      86          36 :     _temp(coupledValue("Temperature")),
      87          36 :     _v(coupledValue("electric_potential")),
      88          36 :     _grad_V(coupledGradient("electric_potential")),
      89          72 :     _kv_names(getParam<std::vector<MaterialPropertyName>>("void_energy_coefficients")),
      90          36 :     _kv(_ndefects),
      91          72 :     _ks_names(getParam<std::vector<MaterialPropertyName>>("solid_energy_coefficients")),
      92          36 :     _ks(_ndefects),
      93          36 :     _hv(declareProperty<Real>("hv")),
      94          36 :     _dhv(declarePropertyDerivative<Real>("hv", _phi_name)),
      95          36 :     _d2hv(declarePropertyDerivative<Real>("hv", _phi_name, _phi_name)),
      96          36 :     _hs(declareProperty<Real>("hs")),
      97          36 :     _dhs(declarePropertyDerivative<Real>("hs", _phi_name)),
      98          36 :     _d2hs(declarePropertyDerivative<Real>("hs", _phi_name, _phi_name)),
      99          36 :     _omegav(declareProperty<Real>("omegav")),
     100          36 :     _domegavdw(_ndefects),
     101          36 :     _d2omegavdw2(_ndefects),
     102          36 :     _omegas(declareProperty<Real>("omegas")),
     103          36 :     _domegasdw(_ndefects),
     104          36 :     _d2omegasdw2(_ndefects),
     105          36 :     _domegasdeta(_neta),
     106          36 :     _d2omegasdwdeta(_ndefects),
     107          36 :     _d2omegasdetadeta(_neta),
     108          36 :     _mu(declareProperty<Real>("mu")),
     109          36 :     _dmu(declarePropertyDerivative<Real>("mu", _phi_name)),
     110          36 :     _d2mu(declarePropertyDerivative<Real>("mu", _phi_name, _phi_name)),
     111          36 :     _kappa(declareProperty<Real>("kappa")),
     112          36 :     _dkappa(declarePropertyDerivative<Real>("kappa", _phi_name)),
     113          36 :     _d2kappa(declarePropertyDerivative<Real>("kappa", _phi_name, _phi_name)),
     114          36 :     _gamma(declareProperty<Real>("gamma")),
     115          72 :     _sigma_s(getParam<Real>("surface_energy")),
     116          72 :     _sigma_gb(getParam<Real>("grainboundary_energy")),
     117          72 :     _int_width(getParam<Real>("int_width")),
     118          72 :     _switch(getParam<Real>("surface_switch_value")),
     119          72 :     _solid_energy(getParam<MooseEnum>("solid_energy_model")),
     120          36 :     _mu_s(6.0 * _sigma_s / _int_width),
     121          36 :     _mu_gb(6.0 * _sigma_gb / _int_width),
     122          36 :     _kappa_s(0.75 * _sigma_s * _int_width),
     123          36 :     _kappa_gb(0.75 * _sigma_gb * _int_width),
     124          36 :     _kB(8.617343e-5), // eV/K
     125          36 :     _e(1.0),          // to put energy units in eV
     126          72 :     _z(getParam<std::vector<int>>("defect_charges")),
     127          72 :     _v_scale(getParam<Real>("voltage_scale")),
     128          72 :     _eps_r(getParam<Real>("solid_relative_permittivity")),
     129         108 :     _nv_min(getParam<std::vector<Real>>("min_vacancy_concentrations_void"))
     130             : {
     131          36 :   if (_ndefects != _z.size())
     132           0 :     paramError("defect_charges", "Need to pass in as many defect_charges as chemical_potentials");
     133          36 :   if (_ndefects != _ns_min_names.size())
     134           0 :     paramError("min_vacancy_concentrations_solid",
     135             :                "Need to pass in as many min_vacancy_concentrations_solid as chemical_potentials");
     136          36 :   if (_ndefects != _nv_min.size())
     137           0 :     paramError("min_vacancy_concentrations_void",
     138             :                "Need to pass in as many min_vacancy_concentrations_void as chemical_potentials");
     139          36 :   if (_ndefects != _kv_names.size())
     140           0 :     paramError("void_energy_coefficients",
     141             :                "Need to pass in as many void_energy_coefficients as chemical_potentials");
     142          36 :   if (_ndefects != _ks_names.size())
     143           0 :     paramError("solid_energy_coefficients",
     144             :                "Need to pass in as many solid_energy_coefficients as chemical_potentials");
     145             : 
     146         108 :   for (unsigned int i = 0; i < _ndefects; ++i)
     147             :   {
     148          72 :     _w_name[i] = coupledName("chemical_potentials", i);
     149          72 :     _ns_min[i] = &getMaterialPropertyByName<Real>(_ns_min_names[i]);
     150          72 :     _dns_min[i].resize(_neta);
     151          72 :     _d2ns_min[i].resize(_neta);
     152          72 :     _d2omegasdwdeta[i].resize(_neta);
     153          72 :     _kv[i] = &getMaterialPropertyByName<Real>(_kv_names[i]);
     154          72 :     _ks[i] = &getMaterialPropertyByName<Real>(_ks_names[i]);
     155          72 :     _domegavdw[i] = &declarePropertyDerivative<Real>("omegav", _w_name[i]);
     156          72 :     _d2omegavdw2[i] = &declarePropertyDerivative<Real>("omegav", _w_name[i], _w_name[i]);
     157          72 :     _domegasdw[i] = &declarePropertyDerivative<Real>("omegas", _w_name[i]);
     158          72 :     _d2omegasdw2[i] = &declarePropertyDerivative<Real>("omegas", _w_name[i], _w_name[i]);
     159         216 :     for (unsigned int j = 0; j < _neta; ++j)
     160             :     {
     161         144 :       _dns_min[i][j] = &getMaterialPropertyDerivativeByName<Real>(_ns_min_names[i], _eta_name[j]);
     162         144 :       _d2ns_min[i][j].resize(_neta);
     163             : 
     164         432 :       for (unsigned int k = 0; k < _neta; ++k)
     165         288 :         _d2ns_min[i][j][k] = &getMaterialPropertyDerivativeByName<Real>(
     166         288 :             _ns_min_names[i], _eta_name[j], _eta_name[k]);
     167             :     }
     168             :   }
     169             : 
     170         108 :   for (unsigned int i = 0; i < _neta; ++i)
     171             :   {
     172          72 :     _eta[i] = &coupledValue("etas", i);
     173          72 :     _eta_name[i] = coupledName("etas", i);
     174          72 :     _domegasdeta[i] = &declarePropertyDerivative<Real>("omegas", _eta_name[i]);
     175          72 :     _d2omegasdetadeta[i].resize(_neta);
     176             : 
     177         180 :     for (unsigned int j = 0; j <= i; ++j)
     178         108 :       _d2omegasdetadeta[j][i] = _d2omegasdetadeta[i][j] =
     179         216 :           &declarePropertyDerivative<Real>("omegas", _eta_name[j], _eta_name[i]);
     180             :   }
     181             : 
     182         108 :   for (unsigned int i = 0; i < _ndefects; ++i)
     183         216 :     for (unsigned int j = 0; j < _neta; ++j)
     184         144 :       _d2omegasdwdeta[i][j] = &declarePropertyDerivative<Real>("omegas", _w_name[i], _eta_name[j]);
     185          36 : }
     186             : 
     187             : void
     188      441600 : ElectrochemicalSinteringMaterial::computeQpProperties()
     189             : {
     190             :   // Calculate phase switching functions
     191      441600 :   _hv[_qp] = 0.0;
     192      441600 :   _dhv[_qp] = 0.0;
     193      441600 :   _d2hv[_qp] = 0.0;
     194             : 
     195      441600 :   if (_phi[_qp] >= 1.0)
     196           0 :     _hv[_qp] = 1.0;
     197      441600 :   else if (_phi[_qp] > 0.0)
     198             :   {
     199           0 :     _hv[_qp] = _phi[_qp] * _phi[_qp] * _phi[_qp] * (10.0 + _phi[_qp] * (-15.0 + _phi[_qp] * 6.0));
     200           0 :     _dhv[_qp] = 30.0 * _phi[_qp] * _phi[_qp] * (_phi[_qp] - 1.0) * (_phi[_qp] - 1.0);
     201           0 :     _d2hv[_qp] = 60.0 * _phi[_qp] * (2.0 * _phi[_qp] - 1.0) * (_phi[_qp] - 1.0);
     202             :   }
     203             : 
     204      441600 :   _hs[_qp] = 1.0 - _hv[_qp];
     205      441600 :   _dhs[_qp] = -_dhv[_qp];
     206      441600 :   _d2hs[_qp] = -_d2hv[_qp];
     207             : 
     208             :   // Calculate interface switching function
     209      441600 :   Real phi = _phi[_qp] / _switch;
     210             :   Real f = 0.0;
     211             :   Real df = 0.0;
     212             :   Real d2f = 0.0;
     213      441600 :   if (phi >= 1.0)
     214             :     f = 1.0;
     215      441600 :   else if (phi > 0.0)
     216             :   {
     217           0 :     f = phi * phi * phi * (10.0 + phi * (-15.0 + phi * 6.0));
     218           0 :     df = 30.0 / _switch * phi * phi * (phi - 1.0) * (phi - 1.0);
     219           0 :     d2f = 60.0 * phi / (_switch * _switch) * (2.0 * phi - 1.0) * (phi - 1.0);
     220             :   }
     221             : 
     222             :   // Permittivity in the void phase (permittivity of free space)
     223      441600 :   Real eps_0 = 5.52e-2 * _v_scale * _v_scale; // units eV/V^2/nm, change with V_scale if needed
     224             : 
     225             :   // Calculate grand potential of void phase
     226             :   Real sum_omega_v = 0.0;
     227     1324800 :   for (unsigned int i = 0; i < _ndefects; ++i)
     228      883200 :     sum_omega_v += -0.5 / (*_kv[i])[_qp] * ((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale) *
     229      883200 :                        ((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale) -
     230      883200 :                    _nv_min[i] * ((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale);
     231             : 
     232      441600 :   _omegav[_qp] = sum_omega_v - 0.5 * eps_0 * _grad_V[_qp] * _grad_V[_qp];
     233             : 
     234             :   // Calculate derivatives of grand potential wrt chemical potential
     235     1324800 :   for (unsigned int i = 0; i < _ndefects; ++i)
     236             :   {
     237      883200 :     (*_domegavdw[i])[_qp] =
     238      883200 :         -((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale) / (*_kv[i])[_qp] - _nv_min[i];
     239      883200 :     (*_d2omegavdw2[i])[_qp] = -1.0 / (*_kv[i])[_qp];
     240             :   }
     241             : 
     242             :   // Calculate solid phase density and potential
     243      441600 :   switch (_solid_energy)
     244             :   {
     245             :     case 0: // PARABOLIC
     246             :     {
     247             :       // Calculate grand potential of solid phase and derivatives wrt chemical potentials
     248             :       Real sum_omega_s = 0.0;
     249           0 :       for (unsigned int i = 0; i < _ndefects; ++i)
     250             :       {
     251           0 :         sum_omega_s += -0.5 / (*_ks[i])[_qp] * ((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale) *
     252           0 :                            ((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale) -
     253           0 :                        (*_ns_min[i])[_qp] * ((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale);
     254           0 :         (*_domegasdw[i])[_qp] =
     255           0 :             -((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale) / (*_ks[i])[_qp] -
     256             :             (*_ns_min[i])[_qp];
     257           0 :         (*_d2omegasdw2[i])[_qp] = -1.0 / (*_ks[i])[_qp];
     258             :       }
     259             : 
     260           0 :       _omegas[_qp] = sum_omega_s - 0.5 * _eps_r * eps_0 * _grad_V[_qp] * _grad_V[_qp];
     261             : 
     262             :       // Calculate derivatives of grand potential wrt order parameters and mixed derivatives
     263           0 :       for (unsigned int i = 0; i < _neta; ++i)
     264             :       {
     265             :         Real sum_domegasdeta = 0.0;
     266           0 :         for (unsigned int j = 0; j < _ndefects; ++j)
     267             :         {
     268           0 :           sum_domegasdeta +=
     269           0 :               -(*_dns_min[j][i])[_qp] * ((*_w[j])[_qp] - _z[j] * _e * _v[_qp] * _v_scale);
     270           0 :           (*_d2omegasdwdeta[j][i])[_qp] = -(*_dns_min[j][i])[_qp];
     271             :         }
     272           0 :         (*_domegasdeta[i])[_qp] = sum_domegasdeta;
     273             : 
     274           0 :         for (unsigned int j = i; j < _neta; ++j)
     275             :         {
     276             :           Real sum_d2omegadeta2 = 0.0;
     277           0 :           for (unsigned int k = 0; k < _ndefects; ++k)
     278           0 :             sum_d2omegadeta2 +=
     279           0 :                 -(*_d2ns_min[k][i][j])[_qp] * ((*_w[k])[_qp] - _z[k] * _e * _v[_qp] * _v_scale);
     280             : 
     281           0 :           (*_d2omegasdetadeta[i][j])[_qp] = (*_d2omegasdetadeta[j][i])[_qp] = sum_d2omegadeta2;
     282             :         }
     283             :       }
     284             :       break;
     285             :     }       // case 0; // PARABOLIC
     286             :     case 1: // DILUTE
     287             :     {
     288             :       // Calculate grand potential of solid phase and derivatives wrt chemical potentials
     289             :       Real sum_omega_s = 0.0;
     290     1324800 :       for (unsigned int i = 0; i < _ndefects; ++i)
     291             :       {
     292      883200 :         Real n_exp = std::exp(((*_w[i])[_qp] - _z[i] * _e * _v[_qp] * _v_scale) / _kB / _temp[_qp]);
     293      883200 :         sum_omega_s += -_kB * _temp[_qp] * (*_ns_min[i])[_qp] * n_exp;
     294      883200 :         (*_domegasdw[i])[_qp] = -(*_ns_min[i])[_qp] * n_exp;
     295      883200 :         (*_d2omegasdw2[i])[_qp] = -(*_ns_min[i])[_qp] * n_exp / _kB / _temp[_qp];
     296             :       }
     297             : 
     298      441600 :       _omegas[_qp] = sum_omega_s - 0.5 * _eps_r * eps_0 * _grad_V[_qp] * _grad_V[_qp];
     299             : 
     300             :       // Calculate derivatives of grand potential wrt order parameters and mixed derivatives
     301     1324800 :       for (unsigned int i = 0; i < _neta; ++i)
     302             :       {
     303             :         Real sum_domegasdeta = 0.0;
     304     2649600 :         for (unsigned int j = 0; j < _ndefects; ++j)
     305             :         {
     306             :           Real n_exp =
     307     1766400 :               std::exp(((*_w[j])[_qp] - _z[j] * _e * _v[_qp] * _v_scale) / _kB / _temp[_qp]);
     308     1766400 :           sum_domegasdeta += -_kB / _temp[_qp] * (*_dns_min[j][i])[_qp] * n_exp;
     309     1766400 :           (*_d2omegasdwdeta[j][i])[_qp] = -(*_dns_min[j][i])[_qp] * n_exp;
     310             :         }
     311      883200 :         (*_domegasdeta[i])[_qp] = sum_domegasdeta;
     312             : 
     313     2208000 :         for (unsigned int j = i; j < _neta; ++j)
     314             :         {
     315             :           Real sum_d2omegadeta2 = 0.0;
     316     3974400 :           for (unsigned int k = 0; k < _ndefects; ++k)
     317             :           {
     318             :             Real n_exp =
     319     2649600 :                 std::exp(((*_w[k])[_qp] - _z[k] * _e * _v[_qp] * _v_scale) / _kB / _temp[_qp]);
     320     2649600 :             sum_d2omegadeta2 += -_kB / _temp[_qp] * (*_d2ns_min[k][i][j])[_qp] * n_exp;
     321             :           }
     322             : 
     323     1324800 :           (*_d2omegasdetadeta[i][j])[_qp] = (*_d2omegasdetadeta[j][i])[_qp] = sum_d2omegadeta2;
     324             :         }
     325             :       }
     326             : 
     327             :       break;
     328             :     }       // case 1: // DILUTE
     329           0 :     case 2: // IDEAL
     330             :     {
     331           0 :       mooseError(
     332             :           "Ideal solution in solid is not yet supported in ElectrochemicalSinteringMaterial");
     333             :       break;
     334             :     } // case 2: // IDEAL
     335             :   }   // switch (_solid_energy)
     336             : 
     337             :   // thermodynamic parameters
     338      441600 :   _mu[_qp] = _mu_gb + (_mu_s - _mu_gb) * f;
     339      441600 :   _kappa[_qp] = _kappa_gb + (_kappa_s - _kappa_gb) * f;
     340      441600 :   _dmu[_qp] = (_mu_s - _mu_gb) * df;
     341      441600 :   _dkappa[_qp] = (_kappa_s - _kappa_gb) * df;
     342      441600 :   _d2mu[_qp] = (_mu_s - _mu_gb) * d2f;
     343      441600 :   _d2kappa[_qp] = (_kappa_s - _kappa_gb) * d2f;
     344      441600 :   _gamma[_qp] = 1.5;
     345      441600 : } // void ElectrochemicalSinteringMaterial::computeQpProperties()

Generated by: LCOV version 1.14