diff --git a/framework/include/interfaces/Coupleable.h b/framework/include/interfaces/Coupleable.h index f48712bafc84..9f0a52465f90 100644 --- a/framework/include/interfaces/Coupleable.h +++ b/framework/include/interfaces/Coupleable.h @@ -241,6 +241,28 @@ class Coupleable const GenericVariableValue & coupledGenericDofValue(const std::string & var_name, unsigned int comp = 0) const; + /** + * Returns time derivative of a coupled variable for use in templated automatic differentiation + * classes + * @param var_name Name of coupled variable + * @param comp Component number for vector of coupled variables + * @return Reference to a GenericVariableValue for the coupled variable time derivative + */ + template + const GenericVariableValue & coupledGenericDot(const std::string & var_name, + unsigned int comp = 0) const; + + /** + * Returns the second time derivative of a coupled variable for use in templated automatic + * differentiation classes + * @param var_name Name of coupled variable + * @param comp Component number for vector of coupled variables + * @return Reference to a GenericVariableValue for the coupled variable second time derivative + */ + template + const GenericVariableValue & coupledGenericDotDot(const std::string & var_name, + unsigned int comp = 0) const; + /** * Returns value of a coupled lower-dimensional variable * @param var_name Name of coupled variable diff --git a/framework/include/variables/MooseLinearVariableFV.h b/framework/include/variables/MooseLinearVariableFV.h index fcfe967582e4..437a13dc74fe 100644 --- a/framework/include/variables/MooseLinearVariableFV.h +++ b/framework/include/variables/MooseLinearVariableFV.h @@ -404,6 +404,7 @@ class MooseLinearVariableFV : public MooseVariableField [[noreturn]] virtual const MooseArray & adDofValues() const override; [[noreturn]] virtual const MooseArray & adDofValuesNeighbor() const override; + [[noreturn]] virtual const MooseArray & adDofValuesDot() const override; [[noreturn]] virtual const dof_id_type & nodalDofIndex() const override final; [[noreturn]] virtual const dof_id_type & nodalDofIndexNeighbor() const override final; diff --git a/framework/include/variables/MooseVariableData.h b/framework/include/variables/MooseVariableData.h index 60478194d3ee..33917bae8820 100644 --- a/framework/include/variables/MooseVariableData.h +++ b/framework/include/variables/MooseVariableData.h @@ -428,6 +428,11 @@ class MooseVariableData : public MooseVariableDataBase */ const MooseArray & adDofValues() const; + /** + * Return the AD time derivative values of degrees of freedom + */ + const MooseArray & adDofValuesDot() const; + /////////////////////////////// Increment stuff /////////////////////////////////////// /** @@ -453,7 +458,7 @@ class MooseVariableData : public MooseVariableDataBase * for nodal basis families */ void assignADNodalValue(const ADReal & value, const unsigned int & component); - void fetchADDoFValues(); + void fetchADNodalValues(); const FEType & _fe_type; @@ -726,6 +731,17 @@ MooseVariableData::adDofValues() const return _ad_dof_values; } +template +const MooseArray & +MooseVariableData::adDofValuesDot() const +{ + _need_ad = _need_ad_u_dot = true; + if (!_time_integrator) + // See explanation in adUDot() body + _need_u_dot = true; + return _ad_dofs_dot; +} + template const typename Moose::ADType::type & MooseVariableData::adNodalValue() const diff --git a/framework/include/variables/MooseVariableDataFV.h b/framework/include/variables/MooseVariableDataFV.h index 83bce9314717..86595927db29 100644 --- a/framework/include/variables/MooseVariableDataFV.h +++ b/framework/include/variables/MooseVariableDataFV.h @@ -259,6 +259,11 @@ class MooseVariableDataFV : public MooseVariableDataBase, public Mes */ const MooseArray & adDofValues() const; + /** + * Return the AD dof time derivatives + */ + const MooseArray & adDofValuesDot() const; + /////////////////////////////// Increment stuff /////////////////////////////////////// /** @@ -473,6 +478,14 @@ MooseVariableDataFV::adDofValues() const return _ad_dof_values; } +template +const MooseArray & +MooseVariableDataFV::adDofValuesDot() const +{ + _need_ad = _need_ad_u_dot = true; + return _ad_dofs_dot; +} + template inline bool MooseVariableDataFV::safeToComputeADUDot() const diff --git a/framework/include/variables/MooseVariableFE.h b/framework/include/variables/MooseVariableFE.h index 800cc8d8fddd..2d5807b18b73 100644 --- a/framework/include/variables/MooseVariableFE.h +++ b/framework/include/variables/MooseVariableFE.h @@ -594,15 +594,9 @@ class MooseVariableFE : public MooseVariableField const MooseArray & dofValuesDuDotDotDu() const override; const MooseArray & dofValuesDuDotDotDuNeighbor() const override; - /** - * Return the AD dof values - */ const MooseArray & adDofValues() const override; - - /** - * Return the AD neignbor dof values - */ const MooseArray & adDofValuesNeighbor() const override; + const MooseArray & adDofValuesDot() const override; /** * Compute and store incremental change in solution at QPs based on increment_vec @@ -844,6 +838,13 @@ MooseVariableFE::adDofValuesNeighbor() const return _neighbor_data->adDofValues(); } +template +inline const MooseArray & +MooseVariableFE::adDofValuesDot() const +{ + return _element_data->adDofValuesDot(); +} + template inline const typename Moose::ADType::type & MooseVariableFE::adNodalValue() const diff --git a/framework/include/variables/MooseVariableFV.h b/framework/include/variables/MooseVariableFV.h index 2e092d306e40..6084c33d6482 100644 --- a/framework/include/variables/MooseVariableFV.h +++ b/framework/include/variables/MooseVariableFV.h @@ -423,11 +423,9 @@ class MooseVariableFV : public MooseVariableField const MooseArray & dofValuesDuDotDotDu() const override; const MooseArray & dofValuesDuDotDotDuNeighbor() const override; - /// Returns the AD dof values. const MooseArray & adDofValues() const override; - - /// Returns the AD neighbor dof values const MooseArray & adDofValuesNeighbor() const override; + const MooseArray & adDofValuesDot() const override; /// Note: const monomial is always the case - higher order solns are /// reconstructed - so this is simpler func than FE equivalent. @@ -732,6 +730,13 @@ MooseVariableFV::adDofValuesNeighbor() const return _neighbor_data->adDofValues(); } +template +inline const MooseArray & +MooseVariableFV::adDofValuesDot() const +{ + return _element_data->adDofValuesDot(); +} + template typename MooseVariableFV::ValueType MooseVariableFV::evaluate(const ElemArg & elem_arg, const StateArg & state) const diff --git a/framework/include/variables/MooseVariableField.h b/framework/include/variables/MooseVariableField.h index f304ff0e23d5..16e418cc6000 100644 --- a/framework/include/variables/MooseVariableField.h +++ b/framework/include/variables/MooseVariableField.h @@ -168,6 +168,11 @@ class MooseVariableField : public MooseVariableFieldBase, */ virtual const MooseArray & adDofValuesNeighbor() const = 0; + /** + * Return the AD time derivatives at dofs + */ + virtual const MooseArray & adDofValuesDot() const = 0; + ///@{ /** * Methods for retrieving values of variables at the nodes in a MooseArray for AuxKernelBase diff --git a/framework/src/interfaces/Coupleable.C b/framework/src/interfaces/Coupleable.C index 9f65edb897b1..db3a10da2f40 100644 --- a/framework/src/interfaces/Coupleable.C +++ b/framework/src/interfaces/Coupleable.C @@ -1168,6 +1168,20 @@ Coupleable::coupledDotDot(const std::string & var_name, unsigned int comp) const } } +template <> +const GenericVariableValue & +Coupleable::coupledGenericDotDot(const std::string & var_name, unsigned int comp) const +{ + return coupledDotDot(var_name, comp); +} + +template <> +const GenericVariableValue & +Coupleable::coupledGenericDotDot(const std::string & var_name, unsigned int comp) const +{ + return adCoupledDotDot(var_name, comp); +} + const VariableValue & Coupleable::coupledDotOld(const std::string & var_name, unsigned int comp) const { @@ -2207,12 +2221,18 @@ Coupleable::adCoupledDot(const std::string & var_name, unsigned int comp) const return *getADDefaultValue(var_name); checkFuncType(var_name, VarType::Dot, FuncAge::Curr); - if (_c_nodal) - mooseError("Not implemented"); - if (!_coupleable_neighbor) + { + if (_c_nodal) + return var->adDofValuesDot(); return var->adUDot(); - return var->adUDotNeighbor(); + } + else + { + if (_c_nodal) + mooseError("AD neighbor nodal dof dot not implemented"); + return var->adUDotNeighbor(); + } } const ADVariableValue & @@ -2687,6 +2707,20 @@ Coupleable::adCoupledDots(const std::string & var_name) const return coupledVectorHelper(var_name, func); } +template <> +const GenericVariableValue & +Coupleable::coupledGenericDot(const std::string & var_name, unsigned int comp) const +{ + return coupledDot(var_name, comp); +} + +template <> +const GenericVariableValue & +Coupleable::coupledGenericDot(const std::string & var_name, unsigned int comp) const +{ + return adCoupledDot(var_name, comp); +} + // Explicit instantiations template const Real & Coupleable::getDefaultNodalValue(const std::string & var_name, diff --git a/framework/src/timeintegrators/CentralDifference.C b/framework/src/timeintegrators/CentralDifference.C index 1e77b4957930..7bd1d5ee1fa0 100644 --- a/framework/src/timeintegrators/CentralDifference.C +++ b/framework/src/timeintegrators/CentralDifference.C @@ -46,6 +46,10 @@ CentralDifference::computeADTimeDerivatives(ADReal & ad_u_dot, const dof_id_type & dof, ADReal & ad_u_dotdot) const { + // Seeds ad_u_dotdot with _ad_dof_values and associated derivatives provided via ad_u_dot from + // MooseVariableData + ad_u_dotdot = ad_u_dot; + computeTimeDerivativeHelper(ad_u_dot, ad_u_dotdot, _solution_old(dof), _solution_older(dof)); } diff --git a/framework/src/variables/MooseLinearVariableFV.C b/framework/src/variables/MooseLinearVariableFV.C index 54b5fd0a7df5..ce2439d34d22 100644 --- a/framework/src/variables/MooseLinearVariableFV.C +++ b/framework/src/variables/MooseLinearVariableFV.C @@ -851,6 +851,13 @@ MooseLinearVariableFV::adDofValuesNeighbor() const adError(); } +template +const MooseArray & +MooseLinearVariableFV::adDofValuesDot() const +{ + adError(); +} + template const dof_id_type & MooseLinearVariableFV::nodalDofIndex() const diff --git a/framework/src/variables/MooseVariableData.C b/framework/src/variables/MooseVariableData.C index 28bf58d72e7a..50fb33f2db3d 100644 --- a/framework/src/variables/MooseVariableData.C +++ b/framework/src/variables/MooseVariableData.C @@ -1149,7 +1149,7 @@ template <> void MooseVariableData::computeMonomialValues() { - // Fixeme: will think of optimization later + // FIXME: will think of optimization later computeValues(); } @@ -1271,12 +1271,16 @@ MooseVariableData::computeAD(const unsigned int num_dofs, const unsi } if (_need_ad_u_dot && !_time_integrator) + { for (MooseIndex(nqp) qp = 0; qp < nqp; ++qp) { _ad_u_dot[qp] = _u_dot[qp]; if (_need_ad_u_dotdot) _ad_u_dotdot[qp] = _u_dotdot[qp]; } + for (unsigned int i = 0; i < num_dofs; i++) + _ad_dofs_dot[i] = _dof_values_dot[i]; + } if (_need_ad_grad_u_dot && !_time_integrator) for (MooseIndex(nqp) qp = 0; qp < nqp; ++qp) @@ -1771,7 +1775,7 @@ MooseVariableData::computeNodalValues() assignNodalValue(); if (_need_ad) - fetchADDoFValues(); + fetchADNodalValues(); } else zeroSizeDofValues(); @@ -1779,12 +1783,17 @@ MooseVariableData::computeNodalValues() template void -MooseVariableData::fetchADDoFValues() +MooseVariableData::fetchADNodalValues() { auto n = _dof_indices.size(); libmesh_assert(n); _ad_dof_values.resize(n); + if (_need_ad_u_dot) + _ad_dofs_dot.resize(n); + if (_need_ad_u_dotdot) + _ad_dofs_dotdot.resize(n); + const bool do_derivatives = ADReal::do_derivatives && _sys.number() == _subproblem.currentNlSysNum(); @@ -1794,12 +1803,30 @@ MooseVariableData::fetchADDoFValues() if (do_derivatives) Moose::derivInsert(_ad_dof_values[i].derivatives(), _dof_indices[i], 1.); assignADNodalValue(_ad_dof_values[i], i); + + if (_need_ad_u_dot) + { + if (_time_integrator && _time_integrator->dt()) + { + _ad_dofs_dot[i] = _ad_dof_values[i]; + _time_integrator->computeADTimeDerivatives(_ad_dofs_dot[i], + _dof_indices[i], + _need_ad_u_dotdot ? _ad_dofs_dotdot[i] + : _ad_real_dummy); + } + // Executing something with a time derivative at initial should not put a NaN + else if (_time_integrator && !_time_integrator->dt()) + _ad_dofs_dot[i] = 0; + else + mooseError("AD nodal time derivatives not implemented for variables without a time " + "integrator (auxiliary variables)"); + } } } template <> void -MooseVariableData::fetchADDoFValues() +MooseVariableData::fetchADNodalValues() { mooseError("I do not know how to support AD with array variables"); } diff --git a/framework/src/variables/MooseVariableDataFV.C b/framework/src/variables/MooseVariableDataFV.C index e0840ad3b0ae..b2bd81a85cd2 100644 --- a/framework/src/variables/MooseVariableDataFV.C +++ b/framework/src/variables/MooseVariableDataFV.C @@ -50,9 +50,9 @@ MooseVariableDataFV::MooseVariableDataFV(const MooseVariableFV +class DotCouplingAuxTempl : public AuxKernel { public: static InputParameters validParams(); - DotCouplingAux(const InputParameters & parameters); - virtual ~DotCouplingAux(); + DotCouplingAuxTempl(const InputParameters & parameters); protected: virtual Real computeValue(); - const VariableValue & _v_dot; + const GenericVariableValue & _v_dot; }; + +typedef DotCouplingAuxTempl DotCouplingAux; +typedef DotCouplingAuxTempl ADDotCouplingAux; diff --git a/test/include/materials/CoupledValuesMaterial.h b/test/include/materials/CoupledValuesMaterial.h index fe5cb690afdd..213d52c02c0b 100644 --- a/test/include/materials/CoupledValuesMaterial.h +++ b/test/include/materials/CoupledValuesMaterial.h @@ -14,27 +14,33 @@ /** * A material that couples variables values and stores them into material property * This makes sure that everything is properly resized and can be indexed into. + * @tparam whether to use the AD version, retrieving AD values from variables and storing them in + * AD material properties, or not */ -class CoupledValuesMaterial : public Material +template +class CoupledValuesMaterialTempl : public Material { public: static InputParameters validParams(); - CoupledValuesMaterial(const InputParameters & parameters); + CoupledValuesMaterialTempl(const InputParameters & parameters); protected: virtual void computeQpProperties() override; - const VariableValue & _value; - const VariableValue & _dot; - const VariableValue & _dot_dot; + const GenericVariableValue & _value; + const GenericVariableValue & _dot; + const GenericVariableValue & _dot_dot; const VariableValue & _dot_du; const VariableValue & _dot_dot_du; const std::string & _var_name; - MaterialProperty & _value_prop; - MaterialProperty & _dot_prop; - MaterialProperty & _dot_dot_prop; + GenericMaterialProperty & _value_prop; + GenericMaterialProperty & _dot_prop; + GenericMaterialProperty & _dot_dot_prop; MaterialProperty & _dot_du_prop; MaterialProperty & _dot_dot_du_prop; }; + +typedef CoupledValuesMaterialTempl CoupledValuesMaterial; +typedef CoupledValuesMaterialTempl ADCoupledValuesMaterial; diff --git a/test/src/auxkernels/DotCouplingAux.C b/test/src/auxkernels/DotCouplingAux.C index 3a4a3644e229..5bc7fc7416d5 100644 --- a/test/src/auxkernels/DotCouplingAux.C +++ b/test/src/auxkernels/DotCouplingAux.C @@ -10,9 +10,11 @@ #include "DotCouplingAux.h" registerMooseObject("MooseTestApp", DotCouplingAux); +registerMooseObject("MooseTestApp", ADDotCouplingAux); +template InputParameters -DotCouplingAux::validParams() +DotCouplingAuxTempl::validParams() { InputParameters params = AuxKernel::validParams(); params.addRequiredCoupledVar("v", "Coupled variable"); @@ -20,15 +22,18 @@ DotCouplingAux::validParams() return params; } -DotCouplingAux::DotCouplingAux(const InputParameters & parameters) - : AuxKernel(parameters), _v_dot(coupledDot("v")) +template +DotCouplingAuxTempl::DotCouplingAuxTempl(const InputParameters & parameters) + : AuxKernel(parameters), _v_dot(coupledGenericDot("v")) { } -DotCouplingAux::~DotCouplingAux() {} - +template Real -DotCouplingAux::computeValue() +DotCouplingAuxTempl::computeValue() { - return _v_dot[_qp]; + return MetaPhysicL::raw_value(_v_dot[_qp]); } + +template class DotCouplingAuxTempl; +template class DotCouplingAuxTempl; diff --git a/test/src/materials/CoupledValuesMaterial.C b/test/src/materials/CoupledValuesMaterial.C index 4898834fb680..03f7f19345e1 100644 --- a/test/src/materials/CoupledValuesMaterial.C +++ b/test/src/materials/CoupledValuesMaterial.C @@ -10,34 +10,38 @@ #include "CoupledValuesMaterial.h" registerMooseObject("MooseTestApp", CoupledValuesMaterial); +registerMooseObject("MooseTestApp", ADCoupledValuesMaterial); +template InputParameters -CoupledValuesMaterial::validParams() +CoupledValuesMaterialTempl::validParams() { InputParameters params = Material::validParams(); params.addRequiredCoupledVar("variable", "Coupled variable"); return params; } -CoupledValuesMaterial::CoupledValuesMaterial(const InputParameters & parameters) +template +CoupledValuesMaterialTempl::CoupledValuesMaterialTempl(const InputParameters & parameters) : Material(parameters), - _value(coupledValue("variable")), - _dot(coupledDot("variable")), - _dot_dot(coupledDotDot("variable")), + _value(coupledGenericValue("variable")), + _dot(coupledGenericDot("variable")), + _dot_dot(coupledGenericDotDot("variable")), + // We dont have a generic override for this, and we don't need one yet _dot_du(coupledDotDu("variable")), _dot_dot_du(coupledDotDotDu("variable")), - _var_name(getVar("variable", 0)->name()), - _value_prop(declareProperty(_var_name + "_value")), - _dot_prop(declareProperty(_var_name + "_dot")), - _dot_dot_prop(declareProperty(_var_name + "_dot_dot")), + _value_prop(declareGenericProperty(_var_name + "_value")), + _dot_prop(declareGenericProperty(_var_name + "_dot")), + _dot_dot_prop(declareGenericProperty(_var_name + "_dot_dot")), _dot_du_prop(declareProperty(_var_name + "_dot_du")), _dot_dot_du_prop(declareProperty(_var_name + "_dot_dot_du")) { } +template void -CoupledValuesMaterial::computeQpProperties() +CoupledValuesMaterialTempl::computeQpProperties() { _value_prop[_qp] = _value[_qp]; _dot_prop[_qp] = _dot[_qp]; @@ -45,3 +49,6 @@ CoupledValuesMaterial::computeQpProperties() _dot_du_prop[_qp] = _dot_du[_qp]; _dot_dot_du_prop[_qp] = _dot_dot_du[_qp]; } + +template class CoupledValuesMaterialTempl; +template class CoupledValuesMaterialTempl; diff --git a/test/tests/interfaces/coupleable/coupled_dots.i b/test/tests/interfaces/coupleable/coupled_dots.i new file mode 100644 index 000000000000..b7e92a7c9620 --- /dev/null +++ b/test/tests/interfaces/coupleable/coupled_dots.i @@ -0,0 +1,78 @@ +[Mesh] + [gmg] + type = GeneratedMeshGenerator + dim = 1 + nx = 2 + [] +[] + +[Functions] + [linear] + type = ParsedFunction + expression = 'x + 10*t + 2*t*t' + [] +[] + +[AuxVariables] + [base] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [base_aux] + type = FunctionAux + function = 'linear' + variable = 'base' + [] +[] + +[Executioner] + type = Transient + num_steps = 3 + + [TimeIntegrator] + type = CentralDifference + [] +[] + +[Problem] + solve = false + +[] + +[Materials] + [coupled] + type = CoupledValuesMaterial + variable = 'base' + [] + [ad_coupled] + type = ADCoupledValuesMaterial + variable = 'base' + declare_suffix = 'ad' + [] +[] + +[Postprocessors] + [dot] + type = ElementAverageMaterialProperty + mat_prop = 'base_dot' + [] + [dot_dot] + type = ElementAverageMaterialProperty + mat_prop = 'base_dot_dot' + [] + [ad_dot] + type = ADElementAverageMaterialProperty + mat_prop = 'base_dot_ad' + [] + [ad_dot_dot] + type = ADElementAverageMaterialProperty + mat_prop = 'base_dot_dot_ad' + [] +[] + +[Outputs] + csv = true +[] diff --git a/test/tests/interfaces/coupleable/coupled_dots_nodal.i b/test/tests/interfaces/coupleable/coupled_dots_nodal.i new file mode 100644 index 000000000000..ce53aa902687 --- /dev/null +++ b/test/tests/interfaces/coupleable/coupled_dots_nodal.i @@ -0,0 +1,73 @@ +[Mesh] + [gmg] + type = GeneratedMeshGenerator + dim = 1 + nx = 2 + [] +[] + +[Functions] + [linear] + type = ParsedFunction + expression = 'x + 10*t + 2*t*t' + [] +[] + +[Variables] + [base] + [] +[] + +[Kernels] + [time] + type = TimeDerivative + variable = base + [] + [source] + type = BodyForce + variable = base + function = linear + [] +[] + +[AuxVariables] + [first] + [] + [first_from_ad] + [] +[] + +[AuxKernels] + [set_first] + type = DotCouplingAux + variable = first + v = base + [] + [set_first_from_AD] + type = ADDotCouplingAux + variable = first_from_ad + v = base + [] +[] + +[Executioner] + type = Transient + num_steps = 3 +[] + +[Postprocessors] + [dot] + type = AverageNodalVariableValue + variable = 'first' + [] + [ad_dot] + type = AverageNodalVariableValue + variable = 'first_from_ad' + [] + # Ideally we would test the second derivative here, but it's not implemented yet for nodal variables + # through the coupleable API +[] + +[Outputs] + csv = true +[] diff --git a/test/tests/interfaces/coupleable/gold/coupled_dots_nodal_out.csv b/test/tests/interfaces/coupleable/gold/coupled_dots_nodal_out.csv new file mode 100644 index 000000000000..7414f3487e83 --- /dev/null +++ b/test/tests/interfaces/coupleable/gold/coupled_dots_nodal_out.csv @@ -0,0 +1,5 @@ +time,ad_dot,dot +0,0,0 +1,12.5,12.5 +2,28.500000186692,28.500000186692 +3,48.49999979484,48.49999979484 diff --git a/test/tests/interfaces/coupleable/gold/coupled_dots_out.csv b/test/tests/interfaces/coupleable/gold/coupled_dots_out.csv new file mode 100644 index 000000000000..59c1dcbd7151 --- /dev/null +++ b/test/tests/interfaces/coupleable/gold/coupled_dots_out.csv @@ -0,0 +1,5 @@ +time,ad_dot,ad_dot_dot,dot,dot_dot +0,0,0,0,0 +1,6.25,12.5,6.25,12.5 +2,14.25,3.5,14.25,3.5 +3,18,4,18,4 diff --git a/test/tests/interfaces/coupleable/tests b/test/tests/interfaces/coupleable/tests index 76778a3fd4f8..39b1e0a491fa 100644 --- a/test/tests/interfaces/coupleable/tests +++ b/test/tests/interfaces/coupleable/tests @@ -23,4 +23,20 @@ exodiff = 'array_coupling_by_name_out.e' requirement = 'The system shall be able to couple an array variable with its name directly through the coupling interface.' [] + [time_derivative_coupling] + design = 'Coupleable.md' + issues = '#28' + type = CSVDiff + input = 'coupled_dots.i' + csvdiff = 'coupled_dots_out.csv' + requirement = 'The system shall be able to retrieve the time derivatives of variables through the coupling interface.' + [] + [time_derivative_coupling_nodal] + design = 'Coupleable.md' + issues = '#28' + type = CSVDiff + input = 'coupled_dots_nodal.i' + csvdiff = 'coupled_dots_nodal_out.csv' + requirement = 'The system shall be able to retrieve the time derivatives of nodal variables through the coupling interface.' + [] []