diff --git a/framework/include/materials/DerivativeMaterialInterface.h b/framework/include/materials/DerivativeMaterialInterface.h index 5f6f51c1a764..be663c35e6e1 100644 --- a/framework/include/materials/DerivativeMaterialInterface.h +++ b/framework/include/materials/DerivativeMaterialInterface.h @@ -16,6 +16,7 @@ #include "Material.h" #include "MaterialProperty.h" +#include "KernelBase.h" #include "FEProblem.h" #include "BlockRestrictable.h" #include "BoundaryRestrictable.h" @@ -37,7 +38,7 @@ void mooseSetToZero(T & v) v = 0; } template -void mooseSetToZero(T* & ) +void mooseSetToZero(T* &) { mooseError("Cannot use pointer types for MaterialProperty derivatives."); } @@ -104,6 +105,19 @@ class DerivativeMaterialInterface : const MaterialProperty & getMaterialPropertyDerivativeByName(const MaterialPropertyName & base, const VariableName & c1, const VariableName & c2 = "", const VariableName & c3 = ""); ///@} + ///@{ + /** + * check if derivatives of the passed in material property exist w.r.t a variable + * that is _not_ coupled in to the current object + */ + template + void validateCoupling(const MaterialPropertyName & base, const std::vector & c, bool validate_aux = true); + template + void validateCoupling(const MaterialPropertyName & base, const VariableName & c1 = "", const VariableName & c2 = "", const VariableName & c3 = ""); + template + void validateNonlinearCoupling(const MaterialPropertyName & base, const VariableName & c1 = "", const VariableName & c2 = "", const VariableName & c3 = ""); + ///@} + private: /// Return a constant zero property template @@ -113,6 +127,16 @@ class DerivativeMaterialInterface : template bool haveMaterialProperty(const std::string & prop_name); + /// helper method to combine multiple VariableNames into a vector (if they are != "") + std::vector buildVariableVector(const VariableName & c1, const VariableName & c2, const VariableName & c3); + + /// helper method to compile list of missing coupled variables for a given system + template + void validateCouplingHelper(const MaterialPropertyName & base, const std::vector & c, const System & system, std::vector & missing); + + // check if the speciified variable name is not the variable this kernel is acting on (always true for any other type of object) + bool isNotKernelVariable(const VariableName & name); + /// Reference to FEProblem FEProblem & _dmi_fe_problem; @@ -305,4 +329,116 @@ DerivativeMaterialInterface::getMaterialPropertyDerivativeByName(const Materi return getDefaultMaterialPropertyByName(propertyNameFirst(base, c1)); } +template +template +void +DerivativeMaterialInterface::validateCouplingHelper(const MaterialPropertyName & base, const std::vector & c, const System & system, std::vector & missing) +{ + unsigned int ncoupled = this->_coupled_moose_vars.size(); + + // iterate over all variables in the current system (in groups) + for (unsigned int i = 0; i < system.n_variable_groups(); ++i) + { + const VariableGroup & vg = system.variable_group(i); + for (unsigned int j = 0; j < vg.n_variables(); ++j) + { + std::vector cj(c); + VariableName jname = vg.name(j); + cj.push_back(jname); + + // if the derivative exists make sure the variable is coupled + if (haveMaterialProperty(propertyName(base, cj))) + { + // kernels to not have the variable they are acting on in coupled_moose_vars + bool is_missing = isNotKernelVariable(jname); + + for (unsigned int k = 0; k < ncoupled; ++k) + if (this->_coupled_moose_vars[k]->name() == jname) + { + is_missing = false; + break; + } + + if (is_missing) + missing.push_back(jname); + } + } + } +} + +template +template +void +DerivativeMaterialInterface::validateCoupling(const MaterialPropertyName & base, const std::vector & c, bool validate_aux) +{ + // get the base property name + std::string prop_name = this->deducePropertyName(base); + // list of potentially missing coupled variables + std::vector missing; + + // iterate over all variables in the both the non-linear and auxiliary system (optional) + validateCouplingHelper(prop_name, c, _dmi_fe_problem.getNonlinearSystem().system(), missing); + if (validate_aux) + validateCouplingHelper(prop_name, c, _dmi_fe_problem.getAuxiliarySystem().system(), missing); + + if (missing.size() > 0) + { + // join list of missing variable names + std::string list = missing[0]; + for (unsigned int i = 1; i < missing.size(); ++i) + list += ", " + missing[i]; + + mooseError("Missing coupled variables {" << list << "} (add them to args parameter of " << this->name() << ")"); + } +} + +template +std::vector +DerivativeMaterialInterface::buildVariableVector(const VariableName & c1, const VariableName & c2, const VariableName & c3) +{ + std::vector c; + if (c1 != "") + { + c.push_back(c1); + if (c2 != "") + { + c.push_back(c2); + if (c3 != "") + c.push_back(c3); + } + } + return c; +} + +template +template +void +DerivativeMaterialInterface::validateCoupling(const MaterialPropertyName & base, const VariableName & c1, const VariableName & c2, const VariableName & c3) +{ + validateCoupling(base, buildVariableVector(c1, c2, c3), true); +} + +template +template +void +DerivativeMaterialInterface::validateNonlinearCoupling(const MaterialPropertyName & base, const VariableName & c1, const VariableName & c2, const VariableName & c3) +{ + validateCoupling(base, buildVariableVector(c1, c2, c3), false); +} + +template +inline bool +DerivativeMaterialInterface::isNotKernelVariable(const VariableName & name) +{ + // try to cast this to a Kernel pointer + KernelBase * k = dynamic_cast(this); + + // This interface is not templated on a class derived from Kernel + if (k == NULL) + return true; + + // We are templated on a kernel class, so we check if the kernel variable + return k->variable().name() != name; +} + #endif //DERIVATIVEMATERIALINTERFACE_H diff --git a/modules/phase_field/include/kernels/ACBulk.h b/modules/phase_field/include/kernels/ACBulk.h index c386fe2c41f9..cdd1cd38f4cc 100644 --- a/modules/phase_field/include/kernels/ACBulk.h +++ b/modules/phase_field/include/kernels/ACBulk.h @@ -21,6 +21,8 @@ class ACBulk : public DerivativeMaterialInterface public: ACBulk(const InputParameters & parameters); + virtual void initialSetup(); + protected: /// Enum used with computeDFDOP function diff --git a/modules/phase_field/include/kernels/ACInterface.h b/modules/phase_field/include/kernels/ACInterface.h index ce93c78b9412..df8f5c066c80 100644 --- a/modules/phase_field/include/kernels/ACInterface.h +++ b/modules/phase_field/include/kernels/ACInterface.h @@ -21,6 +21,8 @@ class ACInterface : public DerivativeMaterialInterface CahnHilliardBase(const InputParameters & parameters); static InputParameters validParams(); + virtual void initialSetup(); protected: typedef typename CHBulk::PFFunctionType PFFunctionType; @@ -77,6 +78,18 @@ CahnHilliardBase::CahnHilliardBase(const InputParameters & parameters) : } } +template +void +CahnHilliardBase:: initialSetup() +{ + /** + * Check if both the non-linear as well as the auxiliary variables variables + * are coupled. Derivatives with respect to both types of variables contribute + * the residual. + */ + this->template validateCoupling("f_name", this->_var.name()); +} + template RealGradient CahnHilliardBase::computeGradDFDCons(PFFunctionType type) diff --git a/modules/phase_field/include/kernels/SplitCHParsed.h b/modules/phase_field/include/kernels/SplitCHParsed.h index e1a8df198cc2..d68deee7a24a 100644 --- a/modules/phase_field/include/kernels/SplitCHParsed.h +++ b/modules/phase_field/include/kernels/SplitCHParsed.h @@ -28,6 +28,8 @@ class SplitCHParsed : public DerivativeMaterialInterface("mob_name", _coupled_moose_vars[i]->name()); } +void +ACBulk::initialSetup() +{ + validateNonlinearCoupling("mob_name"); +} + Real ACBulk::precomputeQpResidual() { @@ -65,4 +71,3 @@ ACBulk::computeQpOffDiagJacobian(unsigned int jvar) // Set off-diagonal Jacobian term from mobility derivatives return (*_dLdarg[cvar])[_qp] * _phi[_j][_qp] * computeDFDOP(Residual) * _test[_i][_qp]; } - diff --git a/modules/phase_field/src/kernels/ACInterface.C b/modules/phase_field/src/kernels/ACInterface.C index 1593d412c7b1..4b5caf3d0652 100644 --- a/modules/phase_field/src/kernels/ACInterface.C +++ b/modules/phase_field/src/kernels/ACInterface.C @@ -34,6 +34,12 @@ ACInterface::ACInterface(const InputParameters & parameters) : _dLdarg[i] = &getMaterialPropertyDerivative("mob_name", _coupled_moose_vars[i]->name()); } +void +ACInterface::initialSetup() +{ + validateNonlinearCoupling("mob_name"); +} + RealGradient ACInterface::precomputeQpResidual() { @@ -59,4 +65,3 @@ ACInterface::computeQpOffDiagJacobian(unsigned int jvar) // Set off-diagonal jaocbian terms from mobility dependence return _kappa[_qp] * (*_dLdarg[cvar])[_qp] * _phi[_j][_qp] * _grad_u[_qp] * _grad_test[_i][_qp]; } - diff --git a/modules/phase_field/src/kernels/ACParsed.C b/modules/phase_field/src/kernels/ACParsed.C index d96a3de5800b..74c149208f54 100644 --- a/modules/phase_field/src/kernels/ACParsed.C +++ b/modules/phase_field/src/kernels/ACParsed.C @@ -29,6 +29,13 @@ ACParsed::ACParsed(const InputParameters & parameters) : _d2FdEtadarg[i] = &getMaterialPropertyDerivative("f_name", _var.name(), _coupled_moose_vars[i]->name()); } +void +ACParsed::initialSetup() +{ + ACBulk::initialSetup(); + validateNonlinearCoupling("f_name"); +} + Real ACParsed::computeDFDOP(PFFunctionType type) { @@ -55,4 +62,3 @@ ACParsed::computeQpOffDiagJacobian(unsigned int jvar) return ACBulk::computeQpOffDiagJacobian(jvar) + _L[_qp] * (*_d2FdEtadarg[cvar])[_qp] * _phi[_j][_qp] * _test[_i][_qp]; } - diff --git a/modules/phase_field/src/kernels/SplitCHParsed.C b/modules/phase_field/src/kernels/SplitCHParsed.C index 0a03c045fe6d..35ebbea44b25 100644 --- a/modules/phase_field/src/kernels/SplitCHParsed.C +++ b/modules/phase_field/src/kernels/SplitCHParsed.C @@ -30,6 +30,17 @@ SplitCHParsed::SplitCHParsed(const InputParameters & parameters) : _d2Fdcdarg[i] = &getMaterialPropertyDerivative("f_name", _var.name(), _coupled_moose_vars[i]->name()); } +void +SplitCHParsed::initialSetup() +{ + /** + * We are only interested if the necessary non-linear variables are coupled, + * as those are thge ones used in constructing the Jacobian. The AuxVariables + * do not have Jacobian entries. + */ + validateNonlinearCoupling("f_name", _var.name()); +} + Real SplitCHParsed::computeDFDC(PFFunctionType type) { @@ -58,4 +69,3 @@ SplitCHParsed::computeQpOffDiagJacobian(unsigned int jvar) return (*_d2Fdcdarg[cvar])[_qp] * _phi[_j][_qp] * _test[_i][_qp]; } - diff --git a/modules/phase_field/src/materials/DerivativeMultiPhaseBase.C b/modules/phase_field/src/materials/DerivativeMultiPhaseBase.C index 26e9713acd93..f078c510797e 100644 --- a/modules/phase_field/src/materials/DerivativeMultiPhaseBase.C +++ b/modules/phase_field/src/materials/DerivativeMultiPhaseBase.C @@ -115,6 +115,13 @@ DerivativeMultiPhaseBase::DerivativeMultiPhaseBase(const InputParameters & param } } +void +DerivativeMultiPhaseBase::initialSetup() +{ + for (unsigned int n = 0; n < _num_fi; ++n) + validateCoupling(_fi_names[n]); +} + Real DerivativeMultiPhaseBase::computeF() { @@ -123,4 +130,3 @@ DerivativeMultiPhaseBase::computeF() F += (*_hi[n])[_qp] * (*_prop_Fi[n])[_qp]; return F + _W * _g[_qp]; } - diff --git a/modules/phase_field/src/materials/DerivativeSumMaterial.C b/modules/phase_field/src/materials/DerivativeSumMaterial.C index e105aa70bccb..4914e3f0fff7 100644 --- a/modules/phase_field/src/materials/DerivativeSumMaterial.C +++ b/modules/phase_field/src/materials/DerivativeSumMaterial.C @@ -81,6 +81,13 @@ DerivativeSumMaterial::DerivativeSumMaterial(const InputParameters & parameters) } } +void +DerivativeSumMaterial::initialSetup() +{ + for (unsigned int n = 0; n < _num_materials; ++n) + validateCoupling(_sum_materials[n]); +} + void DerivativeSumMaterial::computeProperties() { @@ -127,4 +134,3 @@ DerivativeSumMaterial::computeProperties() } } } - diff --git a/modules/phase_field/src/materials/DerivativeTwoPhaseMaterial.C b/modules/phase_field/src/materials/DerivativeTwoPhaseMaterial.C index c9b6f96f4cbd..6675fb9406a7 100644 --- a/modules/phase_field/src/materials/DerivativeTwoPhaseMaterial.C +++ b/modules/phase_field/src/materials/DerivativeTwoPhaseMaterial.C @@ -87,6 +87,13 @@ DerivativeTwoPhaseMaterial::DerivativeTwoPhaseMaterial(const InputParameters & p } } +void +DerivativeTwoPhaseMaterial::initialSetup() +{ + validateCoupling("fa_name"); + validateCoupling("fb_name"); +} + Real DerivativeTwoPhaseMaterial::computeF() { @@ -148,4 +155,3 @@ DerivativeTwoPhaseMaterial::computeD3F(unsigned int i_var, unsigned int j_var, u return _h[_qp] * (*_prop_d3Fb[i][j][k])[_qp] + (1.0 - _h[_qp]) * (*_prop_d3Fa[i][j][k])[_qp]; } - diff --git a/modules/phase_field/tests/MultiPhase/derivativetwophasematerial.i b/modules/phase_field/tests/MultiPhase/derivativetwophasematerial.i index dde8a9193308..dab07297d83d 100644 --- a/modules/phase_field/tests/MultiPhase/derivativetwophasematerial.i +++ b/modules/phase_field/tests/MultiPhase/derivativetwophasematerial.i @@ -52,6 +52,7 @@ [./ACBulk] type = ACParsed variable = eta + args = c f_name = F [../] [./ACInterface]