From 10e2afc64ab565e19b5f71eb0547c6b288e22ea7 Mon Sep 17 00:00:00 2001
From: Carlo Camilloni <carlo.camilloni@gmail.com>
Date: Wed, 29 Nov 2023 16:19:57 +0100
Subject: [PATCH 1/4] closes #963

the solution is not optimal, but I think making something general is not
trivial
---
 regtest/secondarystructure/rt32/plumed.dat        | 4 ++--
 src/secondarystructure/AntibetaRMSD.cpp           | 6 +++---
 src/secondarystructure/ParabetaRMSD.cpp           | 8 ++++----
 src/secondarystructure/SecondaryStructureRMSD.cpp | 3 ++-
 4 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/regtest/secondarystructure/rt32/plumed.dat b/regtest/secondarystructure/rt32/plumed.dat
index 180a6c5e80..aaf56d6e0b 100644
--- a/regtest/secondarystructure/rt32/plumed.dat
+++ b/regtest/secondarystructure/rt32/plumed.dat
@@ -1,12 +1,12 @@
 MOLINFO STRUCTURE=helix.pdb
-ALPHARMSD RESIDUES=all TYPE=DRMSD LESS_THAN={RATIONAL R_0=0.08 NN=8 MM=12 NOSTRETCH} LABEL=a
+ALPHARMSD RESIDUES=ALL TYPE=DRMSD LESS_THAN={RATIONAL R_0=0.08 NN=8 MM=12 NOSTRETCH} LABEL=a
 ANTIBETARMSD RESIDUES=all TYPE=DRMSD STRANDS_CUTOFF=1.0 LESS_THAN={RATIONAL R_0=0.08 NN=8 MM=12 NOSTRETCH} LABEL=b
 PARABETARMSD RESIDUES=all TYPE=DRMSD STRANDS_CUTOFF=1.0 LESS_THAN={RATIONAL R_0=0.08 NN=8 MM=12 NOSTRETCH} LABEL=p
 PRINT ARG=a.*,b.*,p.* STRIDE=1 FILE=colvar FMT=%8.4f
 ALPHARMSD RESIDUES=2-7 TYPE=DRMSD R_0=0.08 NN=8 MM=12 LABEL=a2
 ALPHARMSD RESIDUES=2-7 TYPE=DRMSD R_0=0.08 NN=8 MM=12 NUMERICAL_DERIVATIVES LABEL=a2num
 DUMPDERIVATIVES ARG=a2.*,a2num.*  FILE=derivatives1 FMT=%8.4f STRIDE=1
-ANTIBETARMSD RESIDUES=3-5,8-10 STYLE=inter TYPE=OPTIMAL LESS_THAN={RATIONAL R_0=0.08 NN=8 MM=12 NOSTRETCH} LABEL=b2
+ANTIBETARMSD RESIDUES=3-5,8-10 STYLE=INTER TYPE=OPTIMAL LESS_THAN={RATIONAL R_0=0.08 NN=8 MM=12 NOSTRETCH} LABEL=b2
 ANTIBETARMSD RESIDUES=3-5,8-10 STYLE=inter TYPE=OPTIMAL LESS_THAN={RATIONAL R_0=0.08 NN=8 MM=12 NOSTRETCH} NUMERICAL_DERIVATIVES LABEL=b2num
 DUMPDERIVATIVES ARG=b2.*,b2num.*  FILE=derivatives2 FMT=%8.3f STRIDE=1
 RESTRAINT ARG=b.lessthan,p.lessthan,a.lessthan,a2,b2.lessthan AT=0,1,2,3,4 KAPPA=1,2,3,4,5 SLOPE=5,4,3,2,1
diff --git a/src/secondarystructure/AntibetaRMSD.cpp b/src/secondarystructure/AntibetaRMSD.cpp
index c06abc7e3c..bab794e256 100644
--- a/src/secondarystructure/AntibetaRMSD.cpp
+++ b/src/secondarystructure/AntibetaRMSD.cpp
@@ -112,11 +112,11 @@ AntibetaRMSD::AntibetaRMSD(const ActionOptions&ao):
 
   bool intra_chain(false), inter_chain(false);
   std::string style; parse("STYLE",style);
-  if( style=="all" ) {
+  if( Tools::caseInSensStringCompare(style, "all") ) {
     intra_chain=true; inter_chain=true;
-  } else if( style=="inter") {
+  } else if( Tools::caseInSensStringCompare(style, "inter") ) {
     intra_chain=false; inter_chain=true;
-  } else if( style=="intra") {
+  } else if( Tools::caseInSensStringCompare(style, "intra") ) {
     intra_chain=true; inter_chain=false;
   } else {
     error( style + " is not a valid directive for the STYLE keyword");
diff --git a/src/secondarystructure/ParabetaRMSD.cpp b/src/secondarystructure/ParabetaRMSD.cpp
index 9e2e06ea52..1ef9fc9101 100644
--- a/src/secondarystructure/ParabetaRMSD.cpp
+++ b/src/secondarystructure/ParabetaRMSD.cpp
@@ -113,11 +113,11 @@ ParabetaRMSD::ParabetaRMSD(const ActionOptions&ao):
 
   bool intra_chain(false), inter_chain(false);
   std::string style; parse("STYLE",style);
-  if( style=="all" ) {
+  if( Tools::caseInSensStringCompare(style, "all") ) {
     intra_chain=true; inter_chain=true;
-  } else if( style=="inter") {
+  } else if( Tools::caseInSensStringCompare(style, "inter") ) {
     intra_chain=false; inter_chain=true;
-  } else if( style=="intra") {
+  } else if( Tools::caseInSensStringCompare(style, "intra") ) {
     intra_chain=true; inter_chain=false;
   } else {
     error( style + " is not a valid directive for the STYLE keyword");
@@ -148,7 +148,7 @@ ParabetaRMSD::ParabetaRMSD(const ActionOptions&ao):
   }
   // This constructs all conceivable sections of antibeta sheet that form between chains
   if( inter_chain ) {
-    if( chains.size()==1 && style!="all" ) error("there is only one chain defined so cannot use inter_chain option");
+    if( chains.size()==1 && !Tools::caseInSensStringCompare(style, "all") ) error("there is only one chain defined so cannot use inter_chain option");
     std::vector<unsigned> nlist(30);
     for(unsigned ichain=1; ichain<chains.size(); ++ichain) {
       unsigned iprev=0; for(unsigned i=0; i<ichain; ++i) iprev+=chains[i];
diff --git a/src/secondarystructure/SecondaryStructureRMSD.cpp b/src/secondarystructure/SecondaryStructureRMSD.cpp
index 4b6febcefe..10b4c1db8a 100644
--- a/src/secondarystructure/SecondaryStructureRMSD.cpp
+++ b/src/secondarystructure/SecondaryStructureRMSD.cpp
@@ -115,7 +115,8 @@ void SecondaryStructureRMSD::readBackboneAtoms( const std::string& moltype, std:
   std::vector<std::string> resstrings; parseVector( "RESIDUES", resstrings );
   if( !verbose_output ) {
     if(resstrings.size()==0) error("residues are not defined, check the keyword RESIDUES");
-    else if(resstrings[0]=="all") {
+    else if( Tools::caseInSensStringCompare(resstrings[0], "all") ) {
+      resstrings[0]="all";
       log.printf("  examining all possible secondary structure combinations\n");
     } else {
       log.printf("  examining secondary structure in residue positions : %s \n",resstrings[0].c_str() );

From 25abfd876b6260465593b7c277d83e9e74cfc722 Mon Sep 17 00:00:00 2001
From: Carlo Camilloni <carlo.camilloni@gmail.com>
Date: Wed, 29 Nov 2023 16:37:34 +0100
Subject: [PATCH 2/4] Update v2.9.md

---
 CHANGES/v2.9.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGES/v2.9.md b/CHANGES/v2.9.md
index 04f779c25c..9ec0461967 100644
--- a/CHANGES/v2.9.md
+++ b/CHANGES/v2.9.md
@@ -55,5 +55,6 @@ Changes from version 2.8 which are relevant for users:
   - Step number is now stored as a `long long int`. Might facilitate Windows compatibility.
 
 ## Version 2.9.1 (to be released)
+- Includes all fixes up to 2.8.4
 - Plumed now fetches the massses correctly from QEspresso 7.0
   

From 9848506d2cd2add7cd65683b8afcc07adb395c03 Mon Sep 17 00:00:00 2001
From: Carlo Camilloni <carlo.camilloni@gmail.com>
Date: Wed, 29 Nov 2023 16:37:34 +0100
Subject: [PATCH 3/4] Update v2.9.md

---
 CHANGES/v2.9.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGES/v2.9.md b/CHANGES/v2.9.md
index 04f779c25c..9ec0461967 100644
--- a/CHANGES/v2.9.md
+++ b/CHANGES/v2.9.md
@@ -55,5 +55,6 @@ Changes from version 2.8 which are relevant for users:
   - Step number is now stored as a `long long int`. Might facilitate Windows compatibility.
 
 ## Version 2.9.1 (to be released)
+- Includes all fixes up to 2.8.4
 - Plumed now fetches the massses correctly from QEspresso 7.0
   

From 31b39e41e807dff6645aaf8772fcf29bb469d120 Mon Sep 17 00:00:00 2001
From: Michele Invernizzi <inve.michele@gmail.com>
Date: Wed, 29 Nov 2023 17:53:04 +0100
Subject: [PATCH 4/4] OPES: Making parsing not reliant on NaN (#992)

---
 src/opes/ECVlinear.cpp            | 22 +++++++++++++++++++---
 src/opes/ECVmultiThermal.cpp      | 24 ++++++++++++------------
 src/opes/ECVmultiThermalBaric.cpp | 24 ++++++++++++++++++++----
 3 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/src/opes/ECVlinear.cpp b/src/opes/ECVlinear.cpp
index 56e895e3c3..be573ed5e2 100644
--- a/src/opes/ECVlinear.cpp
+++ b/src/opes/ECVlinear.cpp
@@ -122,6 +122,22 @@ ECVlinear::ECVlinear(const ActionOptions&ao)
   if(dimensionless)
     beta0_=1;
 
+//workaround needed for intel compiler
+  bool nan_support=true;
+  const double my_nan_value=-42;
+  if(!std::isnan(std::numeric_limits<double>::quiet_NaN()))
+  {
+    nan_support=false;
+    log.printf(" +++ WARNING +++ do not set LAMBDA_MIN/MAX=%g, see https://github.com/plumed/plumed2/pull/990\n", my_nan_value);
+  }
+  auto isNone=[nan_support,my_nan_value](const double value)
+  {
+    if(nan_support)
+      return std::isnan(value);
+    else
+      return value==my_nan_value;
+  };
+
 //parse lambda info
   parse("LAMBDA",lambda0_);
   double lambda_min=std::numeric_limits<double>::quiet_NaN();
@@ -140,7 +156,7 @@ ECVlinear::ECVlinear(const ActionOptions&ao)
   if(lambdas.size()>0)
   {
     plumed_massert(lambda_steps==0,"cannot set both LAMBDA_STEPS and LAMBDA_SET_ALL");
-    plumed_massert(std::isnan(lambda_min) && std::isnan(lambda_max),"cannot set both LAMBDA_SET_ALL and LAMBDA_MIN/MAX");
+    plumed_massert(isNone(lambda_min) && isNone(lambda_max),"cannot set both LAMBDA_SET_ALL and LAMBDA_MIN/MAX");
     plumed_massert(lambdas.size()>=2,"set at least 2 lambdas with LAMBDA_SET_ALL");
     for(unsigned k=0; k<lambdas.size()-1; k++)
       plumed_massert(lambdas[k]<=lambdas[k+1],"LAMBDA_SET_ALL must be properly ordered");
@@ -152,12 +168,12 @@ ECVlinear::ECVlinear(const ActionOptions&ao)
   }
   else
   { //get LAMBDA_MIN and LAMBDA_MAX
-    if(std::isnan(lambda_min))
+    if(isNone(lambda_min))
     {
       lambda_min=0;
       log.printf("  no LAMBDA_MIN provided, using LAMBDA_MIN = %g\n",lambda_min);
     }
-    if(std::isnan(lambda_max))
+    if(isNone(lambda_max))
     {
       lambda_max=1;
       log.printf("  no LAMBDA_MAX provided, using LAMBDA_MAX = %g\n",lambda_max);
diff --git a/src/opes/ECVmultiThermal.cpp b/src/opes/ECVmultiThermal.cpp
index df19c5ca0b..d714eba64e 100644
--- a/src/opes/ECVmultiThermal.cpp
+++ b/src/opes/ECVmultiThermal.cpp
@@ -79,7 +79,7 @@ Notice that \f$p=0.06022140857\f$ corresponds to 1 bar when using the default PL
 */
 //+ENDPLUMEDOC
 
-class ECVmultiCanonical :
+class ECVmultiThermal :
   public ExpansionCVs
 {
 private:
@@ -90,7 +90,7 @@ class ECVmultiCanonical :
   void initECVs();
 
 public:
-  explicit ECVmultiCanonical(const ActionOptions&);
+  explicit ECVmultiThermal(const ActionOptions&);
   static void registerKeywords(Keywords& keys);
   void calculateECVs(const double *) override;
   const double * getPntrToECVs(unsigned) override;
@@ -100,9 +100,9 @@ class ECVmultiCanonical :
   void initECVs_restart(const std::vector<std::string>&) override;
 };
 
-PLUMED_REGISTER_ACTION(ECVmultiCanonical,"ECV_MULTITHERMAL")
+PLUMED_REGISTER_ACTION(ECVmultiThermal,"ECV_MULTITHERMAL")
 
-void ECVmultiCanonical::registerKeywords(Keywords& keys)
+void ECVmultiThermal::registerKeywords(Keywords& keys)
 {
   ExpansionCVs::registerKeywords(keys);
   keys.remove("ARG");
@@ -114,7 +114,7 @@ void ECVmultiCanonical::registerKeywords(Keywords& keys)
   keys.addFlag("NO_GEOM_SPACING",false,"do not use geometrical spacing in temperature, but instead linear spacing in inverse temperature");
 }
 
-ECVmultiCanonical::ECVmultiCanonical(const ActionOptions&ao)
+ECVmultiThermal::ECVmultiThermal(const ActionOptions&ao)
   : Action(ao)
   , ExpansionCVs(ao)
   , todoAutomatic_(false)
@@ -188,28 +188,28 @@ ECVmultiCanonical::ECVmultiCanonical(const ActionOptions&ao)
     log.printf(" -- NO_GEOM_SPACING: inverse temperatures will be linearly spaced\n");
 }
 
-void ECVmultiCanonical::calculateECVs(const double * ene)
+void ECVmultiThermal::calculateECVs(const double * ene)
 {
   for(unsigned k=0; k<derECVs_.size(); k++)
     ECVs_[k]=derECVs_[k]*ene[0];
 // derivatives never change: derECVs_k=(beta_k-beta0)
 }
 
-const double * ECVmultiCanonical::getPntrToECVs(unsigned j)
+const double * ECVmultiThermal::getPntrToECVs(unsigned j)
 {
   plumed_massert(isReady_,"cannot access ECVs before initialization");
   plumed_massert(j==0,getName()+" has only one CV, the ENERGY");
   return &ECVs_[0];
 }
 
-const double * ECVmultiCanonical::getPntrToDerECVs(unsigned j)
+const double * ECVmultiThermal::getPntrToDerECVs(unsigned j)
 {
   plumed_massert(isReady_,"cannot access ECVs before initialization");
   plumed_massert(j==0,getName()+" has only one CV, the ENERGY");
   return &derECVs_[0];
 }
 
-std::vector<std::string> ECVmultiCanonical::getLambdas() const
+std::vector<std::string> ECVmultiThermal::getLambdas() const
 {
   plumed_massert(!todoAutomatic_,"cannot access lambdas before initializing them");
   const double temp0=kbt_/plumed.getAtoms().getKBoltzmann();
@@ -223,7 +223,7 @@ std::vector<std::string> ECVmultiCanonical::getLambdas() const
   return lambdas;
 }
 
-void ECVmultiCanonical::initECVs()
+void ECVmultiThermal::initECVs()
 {
   plumed_massert(!isReady_,"initialization should not be called twice");
   plumed_massert(!todoAutomatic_,"this should not happen");
@@ -233,7 +233,7 @@ void ECVmultiCanonical::initECVs()
   log.printf("  *%4lu temperatures for %s\n",derECVs_.size(),getName().c_str());
 }
 
-void ECVmultiCanonical::initECVs_observ(const std::vector<double>& all_obs_cvs,const unsigned ncv,const unsigned index_j)
+void ECVmultiThermal::initECVs_observ(const std::vector<double>& all_obs_cvs,const unsigned ncv,const unsigned index_j)
 {
   if(todoAutomatic_) //estimate the steps in beta from observations
   {
@@ -250,7 +250,7 @@ void ECVmultiCanonical::initECVs_observ(const std::vector<double>& all_obs_cvs,c
   calculateECVs(&all_obs_cvs[index_j]);
 }
 
-void ECVmultiCanonical::initECVs_restart(const std::vector<std::string>& lambdas)
+void ECVmultiThermal::initECVs_restart(const std::vector<std::string>& lambdas)
 {
   std::size_t pos=lambdas[0].find("_");
   plumed_massert(pos==std::string::npos,"this should not happen, only one CV is used in "+getName());
diff --git a/src/opes/ECVmultiThermalBaric.cpp b/src/opes/ECVmultiThermalBaric.cpp
index 17b3d07e31..5ede40c3b0 100644
--- a/src/opes/ECVmultiThermalBaric.cpp
+++ b/src/opes/ECVmultiThermalBaric.cpp
@@ -146,6 +146,22 @@ ECVmultiThermalBaric::ECVmultiThermalBaric(const ActionOptions&ao)
   const double kB=plumed.getAtoms().getKBoltzmann();
   const double temp0=kbt_/kB;
 
+//workaround needed for intel compiler
+  bool nan_support=true;
+  const double my_nan_value=-42;
+  if(!std::isnan(std::numeric_limits<double>::quiet_NaN()))
+  {
+    nan_support=false;
+    log.printf(" +++ WARNING +++ do not set PRESSURE_MIN/MAX=%g, see https://github.com/plumed/plumed2/pull/990\n", my_nan_value);
+  }
+  auto isNone=[nan_support,my_nan_value](const double value)
+  {
+    if(nan_support)
+      return std::isnan(value);
+    else
+      return value==my_nan_value;
+  };
+
 //parse temp range
   double temp_min=-1;
   double temp_max=-1;
@@ -181,7 +197,7 @@ ECVmultiThermalBaric::ECVmultiThermalBaric(const ActionOptions&ao)
     plumed_massert(temp_steps==0,"cannot set both SET_ALL_TEMP_PRESSURE and TEMP_STEPS");
     plumed_massert(pres_steps==0,"cannot set both SET_ALL_TEMP_PRESSURE and PRESSURE_STEPS");
     plumed_massert(temp_min==-1 && temp_max==-1,"cannot set both SET_ALL_TEMP_PRESSURE and TEMP_MIN/MAX");
-    plumed_massert(std::isnan(pres_min) && std::isnan(pres_max),"cannot set both SET_ALL_TEMP_PRESSURE and PRESSURE_MIN/MAX");
+    plumed_massert(isNone(pres_min) && isNone(pres_max),"cannot set both SET_ALL_TEMP_PRESSURE and PRESSURE_MIN/MAX");
     plumed_massert(cut_corner.size()==0,"cannot set both SET_ALL_TEMP_PRESSURE and CUT_CORNER");
 //setup the target temperature-pressure grid
     derECVs_beta_.resize(custom_lambdas_.size());
@@ -257,7 +273,7 @@ ECVmultiThermalBaric::ECVmultiThermalBaric(const ActionOptions&ao)
     if(pres_.size()>0)
     {
       plumed_massert(pres_steps==0,"cannot set both PRESSURE_STEPS and PRESSURE_SET_ALL");
-      plumed_massert(std::isnan(pres_min) && std::isnan(pres_max),"cannot set both PRESSURE_SET_ALL and PRESSURE_MIN/MAX");
+      plumed_massert(isNone(pres_min) && isNone(pres_max),"cannot set both PRESSURE_SET_ALL and PRESSURE_MIN/MAX");
       plumed_massert(pres_.size()>=2,"set at least 2 pressures");
       for(unsigned kk=0; kk<pres_.size()-1; kk++)
         plumed_massert(pres_[kk]<=pres_[kk+1],"PRESSURE_SET_ALL must be properly ordered");
@@ -266,12 +282,12 @@ ECVmultiThermalBaric::ECVmultiThermalBaric(const ActionOptions&ao)
     }
     else
     { //get PRESSURE_MIN and PRESSURE_MAX
-      if(std::isnan(pres_min))
+      if(isNone(pres_min))
       {
         pres_min=pres0_;
         log.printf("  no PRESSURE_MIN provided, using PRESSURE_MIN=PRESSURE\n");
       }
-      if(std::isnan(pres_max))
+      if(isNone(pres_max))
       {
         pres_max=pres0_;
         log.printf("  no PRESSURE_MAX provided, using PRESSURE_MAX=PRESSURE\n");