From c65a33dfb7d79e65b5b5846c79d782e502e6f525 Mon Sep 17 00:00:00 2001 From: Gareth Aneurin Tribello Date: Sun, 25 Aug 2024 14:49:01 +0100 Subject: [PATCH] Implemented a version of ActionVolume that works when the chains are not used --- regtest/symfunc/rt-q6-lowmem/config | 5 - .../{rt-q6-lowmem => rt-q6-nochain}/Makefile | 0 .../colv.reference | 0 .../colv3.reference | 0 regtest/symfunc/rt-q6-nochain/config | 13 + .../symfunc/rt-q6-nochain/forces.reference | 66 + .../plumed.dat | 1 + regtest/volumes/rt24/Makefile | 1 + regtest/volumes/rt24/colvar.reference | 21 + regtest/volumes/rt24/config | 12 + regtest/volumes/rt24/gentraj.cpp | 47 + regtest/volumes/rt24/plumed.dat | 432 + regtest/volumes/rt24/trajectory.xyz | 8060 +++++++++++++++++ src/adjmat/AdjacencyMatrixBase.cpp | 2 +- src/colvar/MultiColvarTemplate.h | 1 + src/core/ActionWithArguments.cpp | 3 +- src/core/ActionWithMatrix.cpp | 4 + src/core/ActionWithMatrix.h | 2 + src/core/ActionWithVector.cpp | 42 +- src/core/ActionWithVector.h | 2 + src/function/Bessel.cpp | 6 +- src/function/Between.cpp | 4 + src/function/Between.h | 1 + src/function/Custom.cpp | 9 +- src/function/FunctionOfVector.h | 1 + src/function/LessThan.cpp | 5 +- src/function/LessThan.h | 1 + src/function/MoreThan.cpp | 5 +- src/function/MoreThan.h | 1 + 29 files changed, 8732 insertions(+), 15 deletions(-) delete mode 100644 regtest/symfunc/rt-q6-lowmem/config rename regtest/symfunc/{rt-q6-lowmem => rt-q6-nochain}/Makefile (100%) rename regtest/symfunc/{rt-q6-lowmem => rt-q6-nochain}/colv.reference (100%) rename regtest/symfunc/{rt-q6-lowmem => rt-q6-nochain}/colv3.reference (100%) create mode 100644 regtest/symfunc/rt-q6-nochain/config create mode 100644 regtest/symfunc/rt-q6-nochain/forces.reference rename regtest/symfunc/{rt-q6-lowmem => rt-q6-nochain}/plumed.dat (88%) create mode 100644 regtest/volumes/rt24/Makefile create mode 100644 regtest/volumes/rt24/colvar.reference create mode 100644 regtest/volumes/rt24/config create mode 100644 regtest/volumes/rt24/gentraj.cpp create mode 100644 regtest/volumes/rt24/plumed.dat create mode 100644 regtest/volumes/rt24/trajectory.xyz diff --git a/regtest/symfunc/rt-q6-lowmem/config b/regtest/symfunc/rt-q6-lowmem/config deleted file mode 100644 index d07bf37a6e..0000000000 --- a/regtest/symfunc/rt-q6-lowmem/config +++ /dev/null @@ -1,5 +0,0 @@ -type=driver -plumed_modules=symfunc -# this is to test a different name -arg="--plumed plumed.dat --ixyz 64.xyz" -extra_files="../../trajectories/64.xyz" diff --git a/regtest/symfunc/rt-q6-lowmem/Makefile b/regtest/symfunc/rt-q6-nochain/Makefile similarity index 100% rename from regtest/symfunc/rt-q6-lowmem/Makefile rename to regtest/symfunc/rt-q6-nochain/Makefile diff --git a/regtest/symfunc/rt-q6-lowmem/colv.reference b/regtest/symfunc/rt-q6-nochain/colv.reference similarity index 100% rename from regtest/symfunc/rt-q6-lowmem/colv.reference rename to regtest/symfunc/rt-q6-nochain/colv.reference diff --git a/regtest/symfunc/rt-q6-lowmem/colv3.reference b/regtest/symfunc/rt-q6-nochain/colv3.reference similarity index 100% rename from regtest/symfunc/rt-q6-lowmem/colv3.reference rename to regtest/symfunc/rt-q6-nochain/colv3.reference diff --git a/regtest/symfunc/rt-q6-nochain/config b/regtest/symfunc/rt-q6-nochain/config new file mode 100644 index 0000000000..8091b9203e --- /dev/null +++ b/regtest/symfunc/rt-q6-nochain/config @@ -0,0 +1,13 @@ +type=driver +plumed_modules=symfunc +# this is to test a different name +arg="--plumed plumed.dat --ixyz 64.xyz --dump-forces forces --dump-forces-fmt=%8.4f" +extra_files="../../trajectories/64.xyz" + +function plumed_regtest_before() { + export PLUMED_FORBID_CHAINS=yes +} + +function plumed_regtest_after() { + export PLUMED_FORBID_CHAINS=no +} diff --git a/regtest/symfunc/rt-q6-nochain/forces.reference b/regtest/symfunc/rt-q6-nochain/forces.reference new file mode 100644 index 0000000000..d2fd3a470f --- /dev/null +++ b/regtest/symfunc/rt-q6-nochain/forces.reference @@ -0,0 +1,66 @@ +64 + -0.0519 0.1128 0.0399 +X -0.0017 -0.0017 -0.0049 +X 0.0079 -0.0010 0.0191 +X -0.0042 -0.0027 0.0191 +X 0.0045 -0.0010 -0.0068 +X 0.0001 -0.0001 -0.0001 +X 0.0004 0.0072 0.0002 +X 0.0069 0.0003 0.0000 +X 0.0015 0.0003 0.0023 +X -0.0072 0.0040 0.0057 +X -0.0023 0.0078 -0.0012 +X 0.0029 -0.0131 -0.0032 +X -0.0033 0.0012 -0.0079 +X -0.0002 0.0002 0.0054 +X -0.0017 -0.0049 -0.0065 +X 0.0022 -0.0053 -0.0015 +X -0.0113 -0.0021 -0.0040 +X 0.0040 0.0010 0.0038 +X 0.0027 -0.0047 0.0066 +X -0.0000 -0.0066 -0.0076 +X 0.0008 0.0033 0.0064 +X 0.0007 0.0101 0.0022 +X 0.0087 -0.0100 -0.0074 +X 0.0004 -0.0031 0.0076 +X -0.0045 0.0045 0.0019 +X -0.0048 -0.0098 -0.0093 +X 0.0042 0.0076 0.0009 +X -0.0060 0.0106 0.0006 +X 0.0031 0.0031 -0.0004 +X -0.0034 -0.0012 0.0012 +X 0.0002 0.0075 -0.0214 +X -0.0007 -0.0037 -0.0057 +X -0.0081 -0.0026 0.0120 +X -0.0061 -0.0051 0.0013 +X -0.0005 -0.0022 0.0038 +X 0.0045 0.0058 0.0015 +X -0.0149 0.0006 -0.0064 +X -0.0011 -0.0127 0.0015 +X -0.0012 -0.0023 0.0029 +X -0.0085 0.0047 0.0120 +X -0.0090 -0.0005 -0.0119 +X 0.0053 0.0054 -0.0036 +X -0.0026 0.0051 0.0119 +X 0.0028 0.0041 -0.0008 +X -0.0006 0.0136 0.0040 +X 0.0037 -0.0007 0.0078 +X -0.0070 0.0022 -0.0096 +X -0.0094 0.0005 0.0005 +X 0.0022 -0.0025 -0.0113 +X -0.0033 -0.0013 -0.0012 +X 0.0076 -0.0015 0.0030 +X 0.0055 -0.0080 -0.0098 +X 0.0048 -0.0001 0.0062 +X -0.0065 0.0024 0.0151 +X 0.0066 0.0061 0.0043 +X 0.0081 0.0085 -0.0119 +X -0.0063 0.0047 -0.0134 +X -0.0060 -0.0087 -0.0066 +X 0.0009 -0.0114 -0.0004 +X 0.0004 0.0097 -0.0144 +X 0.0117 -0.0026 -0.0020 +X 0.0007 -0.0015 0.0189 +X 0.0024 0.0105 -0.0025 +X 0.0088 0.0002 -0.0007 +X 0.0152 -0.0183 0.0046 diff --git a/regtest/symfunc/rt-q6-lowmem/plumed.dat b/regtest/symfunc/rt-q6-nochain/plumed.dat similarity index 88% rename from regtest/symfunc/rt-q6-lowmem/plumed.dat rename to regtest/symfunc/rt-q6-nochain/plumed.dat index fe0bc7de9f..f90c54fb00 100644 --- a/regtest/symfunc/rt-q6-lowmem/plumed.dat +++ b/regtest/symfunc/rt-q6-nochain/plumed.dat @@ -4,3 +4,4 @@ PRINT ARG=q6.* FILE=colv LOCAL_Q6 SPECIES=q6 SWITCH={RATIONAL D_0=3.0 R_0=1.5} MEAN LABEL=w6 PRINT ARG=w6.* FILE=colv3 +BIASVALUE ARG=w6_mean diff --git a/regtest/volumes/rt24/Makefile b/regtest/volumes/rt24/Makefile new file mode 100644 index 0000000000..3703b27cea --- /dev/null +++ b/regtest/volumes/rt24/Makefile @@ -0,0 +1 @@ +include ../../scripts/test.make diff --git a/regtest/volumes/rt24/colvar.reference b/regtest/volumes/rt24/colvar.reference new file mode 100644 index 0000000000..efcf31c0e0 --- /dev/null +++ b/regtest/volumes/rt24/colvar.reference @@ -0,0 +1,21 @@ +#! FIELDS time a1_lessthan a1_mean a2_lessthan a2_mean a3_lessthan a3_mean + 0.000000 24.6887 0.4844 29.5285 0.4089 26.2692 0.4855 + 0.050000 21.4196 0.4812 24.2911 0.5046 20.4149 0.5235 + 0.100000 24.0789 0.4990 19.5334 0.5008 17.5531 0.5338 + 0.150000 24.7316 0.5294 20.8201 0.4740 18.3199 0.4995 + 0.200000 20.5181 0.5141 20.7127 0.4958 24.2243 0.4777 + 0.250000 14.5411 0.5384 24.0174 0.5035 23.1592 0.4681 + 0.300000 20.7004 0.5109 18.6197 0.5132 21.1682 0.5299 + 0.350000 13.9216 0.5851 22.7805 0.5168 17.5875 0.4531 + 0.400000 23.2710 0.4804 18.4123 0.5179 21.2690 0.4777 + 0.450000 14.0948 0.6024 25.6635 0.4871 19.0694 0.5803 + 0.500000 18.3672 0.4938 21.0006 0.4583 21.0522 0.4942 + 0.550000 16.7623 0.5342 23.0384 0.4970 17.5377 0.4665 + 0.600000 15.8175 0.5281 16.6518 0.4914 16.6425 0.5568 + 0.650000 20.4228 0.5089 17.4494 0.5079 24.4803 0.4642 + 0.700000 24.7885 0.4666 15.1186 0.4913 24.6510 0.4331 + 0.750000 30.4366 0.4697 25.4195 0.4175 27.7407 0.4897 + 0.800000 25.1756 0.4427 23.2958 0.4730 27.8497 0.5077 + 0.850000 22.8584 0.4854 19.4940 0.4654 18.5076 0.5486 + 0.900000 14.8451 0.6134 20.0643 0.5444 18.1854 0.5895 + 0.950000 20.2570 0.4676 23.0313 0.4860 23.2711 0.4263 diff --git a/regtest/volumes/rt24/config b/regtest/volumes/rt24/config new file mode 100644 index 0000000000..906978ac37 --- /dev/null +++ b/regtest/volumes/rt24/config @@ -0,0 +1,12 @@ +plumed_modules=volumes +type=driver +# this is to test a different name +arg="--plumed plumed.dat --trajectory-stride 10 --timestep 0.005 --ixyz trajectory.xyz --dump-forces forces --dump-forces-fmt=%8.4f" + +function plumed_regtest_before() { + export PLUMED_FORBID_CHAINS=yes +} + +function plumed_regtest_after() { + export PLUMED_FORBID_CHAINS=no +} diff --git a/regtest/volumes/rt24/gentraj.cpp b/regtest/volumes/rt24/gentraj.cpp new file mode 100644 index 0000000000..ec8b9c1e8f --- /dev/null +++ b/regtest/volumes/rt24/gentraj.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +double randomn() { return rand()/static_cast(RAND_MAX); } + +int main() { + + std::vector tot(8,0), dtot(8,0); bool dens=false; + unsigned nmols=200, dir; std::vector com(3), dist(3), a1(3), a2(3), len(nmols); + for(unsigned i=0;i<20;++i){ + if(dens) std::cout<<1+nmols< void MultiColvarTemplate::registerKeywords(Keywords& keys ) { T::registerKeywords( keys ); + keys.add("optional","MASK","the label for a sparse matrix that should be used to determine which elements of the matrix should be computed"); unsigned nkeys = keys.size(); for(unsigned i=0; i& } } } - if( readact->keywords.exists("MASKED_INPUT_ALLOWED") || readact->keywords.exists("IS_SHORTCUT") ) return; + if( readact->keywords.exists("MASKED_INPUT_ALLOWED") || readact->keywords.exists("IS_SHORTCUT") || readact->keywords.exists("MASK") ) return; for(unsigned i=0; igetRank()==0 ) continue; ActionWithVector* av=dynamic_cast( arg[i]->getPntrToAction() ); if( av && av->getNumberOfMasks()>0 ) readact->error("cannot use argument " + arg[i]->getName() + " in input as not all elements are computed"); } diff --git a/src/core/ActionWithMatrix.cpp b/src/core/ActionWithMatrix.cpp index db6112dab0..acd44b54f6 100644 --- a/src/core/ActionWithMatrix.cpp +++ b/src/core/ActionWithMatrix.cpp @@ -98,6 +98,10 @@ void ActionWithMatrix::setupMatrixStore() { if( next_action_in_chain ) next_action_in_chain->setupMatrixStore(); } +int ActionWithMatrix::checkTaskIsActive( const unsigned& itask ) const { + return 1; +} + void ActionWithMatrix::calculate() { if( actionInChain() ) return ; // Update all the neighbour lists diff --git a/src/core/ActionWithMatrix.h b/src/core/ActionWithMatrix.h index b5a870ba2c..87881af690 100644 --- a/src/core/ActionWithMatrix.h +++ b/src/core/ActionWithMatrix.h @@ -60,6 +60,8 @@ class ActionWithMatrix : public ActionWithVector { virtual ~ActionWithMatrix(); /// virtual bool isAdjacencyMatrix() const { return false; } +/// + int checkTaskIsActive( const unsigned& itask ) const override ; /// void getAllActionLabelsInMatrixChain( std::vector& mylabels ) const override ; /// Get the first matrix in this chain diff --git a/src/core/ActionWithVector.cpp b/src/core/ActionWithVector.cpp index ab0030b431..4eb2ddb465 100644 --- a/src/core/ActionWithVector.cpp +++ b/src/core/ActionWithVector.cpp @@ -79,8 +79,15 @@ ActionWithVector::ActionWithVector(const ActionOptions&ao): if( keywords.exists("MASK") ) { std::vector mask; parseArgumentList("MASK",mask); if( mask.size()>0 ) { - if( nmask>0 ) error("should not have a mask if you have read the mask keyword"); - if( getPntrToArgument(0)->hasDerivatives() ) error("input for mask should be vector or matrix"); + if( nmask>0 && getNumberOfArguments()==1 ) { + ActionWithVector* av=dynamic_cast( getPntrToArgument(0)->getPntrToAction() ); + plumed_massert( av, "input should be a vector from ActionWithVector" ); unsigned j=0, nargs = av->getNumberOfArguments(); + for(unsigned i=nargs-av->nmask; igetPntrToArgument(i)!=mask[j] ) error("the masks in subsequent actions do not match"); + j++; + } + } else if( nmask>0 ) error("should not have a mask if you have read the mask keyword"); + if( getNumberOfArguments()>0 && getPntrToArgument(0)->hasDerivatives() ) error("input for mask should be vector or matrix"); else if( mask[0]->getRank()==2 ) { if( mask.size()>1 ) error("MASK should only have one argument"); log.printf(" only computing elements of matrix that correspond to non-zero elements of matrix %s \n", mask[0]->getName().c_str() ); @@ -428,10 +435,41 @@ void ActionWithVector::prepare() { active_tasks.resize(0); atomsWereRetrieved=false; } +int ActionWithVector::checkTaskIsActive( const unsigned& itask ) const { + for(unsigned i=0; igetRank()==0 ) continue; + else if( myarg->getRank()==1 && !myarg->hasDerivatives() ) { + if( fabs(myarg->get(itask))>0 ) return 1; + } else plumed_merror("should not be in action " + getName() ); + } + return -1; +} + std::vector& ActionWithVector::getListOfActiveTasks( ActionWithVector* action ) { if( active_tasks.size()>0 ) return active_tasks; unsigned ntasks=0; getNumberOfTasks( ntasks ); + if( getenvChainForbidden()==Option::yes ) { + if( getNumberOfArguments()==0 ) { + active_tasks.resize( ntasks ); + for(unsigned i=0; i taskFlags( ntasks, -1 ); + for(unsigned i=0; i0 ) nt++; + } + active_tasks.resize(nt); nt=0; + for(unsigned i=0; i0 ) { active_tasks[nt]=i; nt++; } + } + return active_tasks; + } + unsigned stride=comm.Get_size(); unsigned rank=comm.Get_rank(); if(serial) { stride=1; rank=0; } diff --git a/src/core/ActionWithVector.h b/src/core/ActionWithVector.h index 25a5b66e63..2f1bc24f93 100644 --- a/src/core/ActionWithVector.h +++ b/src/core/ActionWithVector.h @@ -131,6 +131,8 @@ class ActionWithVector: /// Check if a mask has been set unsigned getNumberOfMasks() const ; void calculateNumericalDerivatives(ActionWithValue* av) override; +/// Determine if a particular task is active based on the values of the input argument + virtual int checkTaskIsActive( const unsigned& itask ) const ; /// Turn off the calculation of the derivatives during the forward pass through a calculation bool doNotCalculateDerivatives() const override ; /// Are we running this command in a chain diff --git a/src/function/Bessel.cpp b/src/function/Bessel.cpp index d853e209b0..1eb0841c76 100644 --- a/src/function/Bessel.cpp +++ b/src/function/Bessel.cpp @@ -72,6 +72,7 @@ class Bessel : public FunctionTemplateBase { bool derivativesImplemented() override { return false; } void registerKeywords( Keywords& keys ) override; void read( ActionWithArguments* action ) override; + bool checkIfMaskAllowed( const std::vector& args ) const override { return args.size()>1; } void calc( const ActionWithArguments* action, const std::vector& args, std::vector& vals, Matrix& derivatives ) const override; }; @@ -88,7 +89,10 @@ void Bessel::registerKeywords(Keywords& keys) { } void Bessel::read( ActionWithArguments* action ) { - if( action->getNumberOfArguments()!=1 ) action->error("should only be one argument to bessel actions"); + if( action->getNumberOfArguments()!=1 ) { + ActionWithVector* av = dynamic_cast( action ); + if( !av || (av && action->getNumberOfArguments()-av->getNumberOfMasks()!=1) ) action->error("should only be one argument to less_than actions"); + } if( action->getPntrToArgument(0)->isPeriodic() ) action->error("cannot use this function on periodic functions"); action->parse("ORDER",order); action->log.printf(" computing %dth order bessel function \n", order ); if( order!=0 ) action->error("only zero order bessel function is implemented"); diff --git a/src/function/Between.cpp b/src/function/Between.cpp index 15667e9460..ffe5ee8cb6 100644 --- a/src/function/Between.cpp +++ b/src/function/Between.cpp @@ -75,6 +75,10 @@ void Between::registerKeywords(Keywords& keys) { } void Between::read( ActionWithArguments* action ) { + if( action->getNumberOfArguments()!=1 ) { + ActionWithVector* av = dynamic_cast( action ); + if( !av || (av && action->getNumberOfArguments()-av->getNumberOfMasks()!=1) ) action->error("should only be one argument to less_than actions"); + } if( action->getNumberOfArguments()!=1 ) action->error("should only be one argument to between actions"); std::string str_min, str_max, tstr_min, tstr_max; diff --git a/src/function/Between.h b/src/function/Between.h index 4375398751..d85eb9cb6d 100644 --- a/src/function/Between.h +++ b/src/function/Between.h @@ -33,6 +33,7 @@ class Between : public FunctionTemplateBase { public: void registerKeywords( Keywords& keys ) override; void read( ActionWithArguments* action ) override; + bool checkIfMaskAllowed( const std::vector& args ) const override { return args.size()>1; } bool getDerivativeZeroIfValueIsZero() const override { return true; } void calc( const ActionWithArguments* action, const std::vector& args, std::vector& vals, Matrix& derivatives ) const override; }; diff --git a/src/function/Custom.cpp b/src/function/Custom.cpp index 8b75e13acf..7bad0f7ca8 100644 --- a/src/function/Custom.cpp +++ b/src/function/Custom.cpp @@ -343,9 +343,12 @@ bool Custom::checkIfMaskAllowed( const std::vector& args ) const { if( found ) continue; ActionWithVector* av=dynamic_cast( args[i]->getPntrToAction() ); if( av && av->getNumberOfMasks()>0 ) { - nomask=false; Value* maskarg = av->getPntrToArgument( av->getNumberOfArguments()-1 ); - for(unsigned j=0; jgetNumberOfArguments(), nm = av->getNumberOfMasks(); + for(unsigned k=nargs-nm; kgetPntrToArgument( k ); + for(unsigned j=0; j::registerKeywords(Keywords& keys ) { } else if( keys.getDisplayName()=="SORT" ) { keys.setValueDescription("a vector that has been sorted into ascending order"); } else if( keys.outputComponentExists(".#!value") ) { + keys.add("optional","MASK","the label for a sparse matrix that should be used to determine which elements of the matrix should be computed"); keys.setValueDescription("the vector obtained by doing an element-wise application of " + keys.getOutputComponentDescription(".#!value") + " to the input vectors"); } } diff --git a/src/function/LessThan.cpp b/src/function/LessThan.cpp index 780ae9a6e1..f25be7ced4 100644 --- a/src/function/LessThan.cpp +++ b/src/function/LessThan.cpp @@ -77,7 +77,10 @@ void LessThan::registerKeywords(Keywords& keys) { } void LessThan::read( ActionWithArguments* action ) { - if( action->getNumberOfArguments()!=1 ) action->error("should only be one argument to less_than actions"); + if( action->getNumberOfArguments()!=1 ) { + ActionWithVector* av = dynamic_cast( action ); + if( !av || (av && action->getNumberOfArguments()-av->getNumberOfMasks()!=1) ) action->error("should only be one argument to less_than actions"); + } if( action->getPntrToArgument(0)->isPeriodic() ) action->error("cannot use this function on periodic functions"); diff --git a/src/function/LessThan.h b/src/function/LessThan.h index 5c342f2220..36eca84e36 100644 --- a/src/function/LessThan.h +++ b/src/function/LessThan.h @@ -34,6 +34,7 @@ class LessThan : public FunctionTemplateBase { public: void registerKeywords( Keywords& keys ) override; void read( ActionWithArguments* action ) override; + bool checkIfMaskAllowed( const std::vector& args ) const override { return args.size()>1; } bool getDerivativeZeroIfValueIsZero() const override { return true; } void calc( const ActionWithArguments* action, const std::vector& args, std::vector& vals, Matrix& derivatives ) const override; }; diff --git a/src/function/MoreThan.cpp b/src/function/MoreThan.cpp index f8ddb55324..cd31c606ff 100644 --- a/src/function/MoreThan.cpp +++ b/src/function/MoreThan.cpp @@ -77,7 +77,10 @@ void MoreThan::registerKeywords(Keywords& keys) { } void MoreThan::read( ActionWithArguments* action ) { - if( action->getNumberOfArguments()!=1 ) action->error("should only be one argument to more_than actions"); + if( action->getNumberOfArguments()!=1 ) { + ActionWithVector* av = dynamic_cast( action ); + if( !av || (av && action->getNumberOfArguments()-av->getNumberOfMasks()!=1) ) action->error("should only be one argument to less_than actions"); + } if( action->getPntrToArgument(0)->isPeriodic() ) action->error("cannot use this function on periodic functions"); diff --git a/src/function/MoreThan.h b/src/function/MoreThan.h index f5e84f273d..b36aeaa61a 100644 --- a/src/function/MoreThan.h +++ b/src/function/MoreThan.h @@ -34,6 +34,7 @@ class MoreThan : public FunctionTemplateBase { public: void registerKeywords( Keywords& keys ) override; void read( ActionWithArguments* action ) override; + bool checkIfMaskAllowed( const std::vector& args ) const override { return args.size()>1; } bool getDerivativeZeroIfValueIsZero() const override { return true; } void calc( const ActionWithArguments* action, const std::vector& args, std::vector& vals, Matrix& derivatives ) const override; };