LCOV - code coverage report
Current view: top level - adjmat - ContactMatrixShortcut.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 43 55 78.2 %
Date: 2025-12-04 11:19:34 Functions: 2 3 66.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-2020 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "core/ActionShortcut.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "core/ActionSet.h"
      26             : #include "core/Group.h"
      27             : #include "ContactMatrix.h"
      28             : 
      29             : //+PLUMEDOC MATRIX CONTACT_MATRIX
      30             : /*
      31             : Adjacency matrix in which two atoms are adjacent if they are within a certain cutoff.
      32             : 
      33             : A useful tool for developing complex collective variables is the notion of the
      34             : so called adjacency matrix. An adjacency matrix can be an $N \times N$ matrix in which the $i$th, $j$th element tells you whether
      35             : or not the $i$th and $j$th atoms/molecules from a set of $N$ atoms/molecules are adjacent or not. Alternatively, you can calculate
      36             : an adjacency matrix between a set of $N$ atoms and a second set of $M$ atoms.  For this type of matrix the $i$th, $j$th element tells you
      37             : whether the whether the $i$th atom in the first group and the $j$th atom in the second group are adjacent or not.  The adjacency matrix in this
      38             : case is thus $N \times M$.
      39             : 
      40             : The simplest type of adjacency matrix is a contact matrix.  The elements of a contact matrix are calculated using:
      41             : 
      42             : $$
      43             : a_{ij} = \sigma( r_{ij} )
      44             : $$
      45             : 
      46             : where $r_{ij}$ is the magnitude of the vector connecting atoms $i$ and $j$ and where $\sigma$ is a switching function. If you want to calculate a
      47             : contact matrix for one group of atoms the input would look something like this:
      48             : 
      49             : ```plumed
      50             : c1: CONTACT_MATRIX GROUP=1-7 SWITCH={RATIONAL R_0=2.6 NN=6 MM=12}
      51             : ```
      52             : 
      53             : Alternatively, if you want to calculate the contact matrix between two groups of atoms you would use an input like following:
      54             : 
      55             : ```plumed
      56             : c2: CONTACT_MATRIX GROUPA=1-7 GROUPB=8-20 SWITCH={RATIONAL R_0=2.6 NN=6 MM=12}
      57             : ```
      58             : 
      59             : Once you have calculated a contact matrix you can perform various matrix operations by using the tools in the matrixtools or clusters modules.
      60             : 
      61             : ## Coordination numbers
      62             : 
      63             : If a contact matrix, $\mathbf{C}$, is multiplied from the back by a vector of ones the $i$th element of the resulting matrix tells you the number of atoms that are
      64             : within $r_c$ of atom $i$.  In other words, the coordination numbers of the atoms can be calculated from the contact matrix by doing matrix vector multiplication.
      65             : 
      66             : This realisation about the relationship between the contact map and the coordination number is heavily used in PLUMED.  For example, to calculate
      67             : and print the coordination numbers of the first 7 atoms in the system with themselves you would use an input something like this:
      68             : 
      69             : ```plumed
      70             : c1: CONTACT_MATRIX GROUP=1-7 D_0=0.0 R_0=2.6 NN=6 MM=12
      71             : ones: ONES SIZE=7
      72             : cc: MATRIX_VECTOR_PRODUCT ARG=c1,ones
      73             : PRINT ARG=cc FILE=colvar
      74             : ```
      75             : 
      76             : This input transforms the distances using the RATIONAL switching function that is discussed in the documentation for [LESS_THAN](LESS_THAN.md)
      77             : The parameters for this type of switching function can be specified using `D_0`, `R_0`, `NN` and `MM` keywords as indicated above. However, we would recommend
      78             : using the following syntax instead as this allows you to use the full range of switching function types. Most importantly, if you use the following syntax you can
      79             : set the D_MAX parameter, as shown below. As discussed in the optimization details section below, __if you want good computational performance on large systems it is essential to set the D_MAX parameter.__
      80             : 
      81             : 
      82             : ```plumed
      83             : c1: CONTACT_MATRIX GROUP=1-7 SWITCH={RATIONAL R_0=2.6 NN=6 MM=12 D_MAX=5.0}
      84             : ones: ONES SIZE=7
      85             : cc: MATRIX_VECTOR_PRODUCT ARG=c1,ones
      86             : PRINT ARG=cc FILE=colvar
      87             : ```
      88             : 
      89             : Implmenting the coordination number as a product of a matrix and a vector is useful as there are many different ways to define whether two atoms/molecules and to construct a "contact" matrix based on
      90             : the result.  For example:
      91             : 
      92             : * You could say that two molecules are connected if they are within a certain distance of each other and if they have the same orientation (see [TORSIONS_MATRIX](TORSIONS_MATRIX.md)).
      93             : * You could say that two water molecules are connected if they are hydrogen bonded to each other (see [HBOND_MATRIX](HBOND_MATRIX.md)).
      94             : * You could say that two atoms are connected if they are within a certain distance of each other and if they have similar values for a CV (see [OUTER_PRODUCT](OUTER_PRODUCT.md)).
      95             : 
      96             : When the coordination numbers is implemented in the way described above (by doing the matrix-vector multiplication) you have the flexibility to define the contact matrix that
      97             : is used in the multiplication in whatever way you choose.  In other words, this implementation of the coordination number is much more flexible. For example, suppose you want
      98             : to calculate the number of atoms that have a coordination is greater than 3.0.  You can do this with PLUMED using the following input:
      99             : 
     100             : ```plumed
     101             : # Calculate the contact matrix for the first seven atoms in the system
     102             : c1: CONTACT_MATRIX GROUP=1-7 SWITCH={RATIONAL R_0=2.6 NN=6 MM=12}
     103             : # Calculate the coordination numbers for the first seven atoms in the system
     104             : ones: ONES SIZE=7
     105             : cc: MATRIX_VECTOR_PRODUCT ARG=c1,ones
     106             : # Set the ith element of the vector mtc equal to one if the coordination number of atom i is greater than 3.
     107             : mtc: MORE_THAN ARG=cc SWITCH={RATIONAL D_0=3 R_0=1}
     108             : # Calculate the number of atoms with a coordination number greater than 3.
     109             : s: SUM ARG=mtc PERIODIC=NO
     110             : PRINT ARG=s FILE=colvar
     111             : ```
     112             : 
     113             : Alternatively, consider the CV that was used to study perovskite nucleation in [this paper](https://pubs.acs.org/doi/abs/10.1021/acs.chemmater.9b04259).  The CV here measures the number of
     114             : methylamonium molecules that are attached to at least 5 other methylamoniusm molecules, at least 7 lead atoms and at least 11 ionide ions.  We can calculate something akin to this
     115             : CV and apply a restraint on the resulting quantity by using the following input file:
     116             : 
     117             : ```plumed
     118             : # Lead ions
     119             : Pb: GROUP ATOMS=1-64
     120             : # Iodide atoms
     121             : I: GROUP ATOMS=65-256
     122             : # Methylamonium "atoms" -- in the real CV these are centers of mass rather than single atoms
     123             : cn: GROUP ATOMS=257-320
     124             : 
     125             : ones64: ONES SIZE=64
     126             : # Contact matrix that determines if methylamonium molecules are within 8 A of each other
     127             : cm_cncn: CONTACT_MATRIX GROUP=cn SWITCH={RATIONAL R_0=0.8}
     128             : # Coordination number of methylamounium with methylamonium
     129             : cc_cncn: MATRIX_VECTOR_PRODUCT ARG=cm_cncn,ones64
     130             : # Vector with elements that are one if coordiantion of methylamonium with methylamonium >5
     131             : mt_cncn: MORE_THAN ARG=cc_cncn SWITCH={RATIONAL R_0=5 NN=12 MM=24}
     132             : 
     133             : # Contact matrix that determines if methylamoinium moleulcule and Pb atom are within 7.5 A of each other
     134             : cm_cnpb: CONTACT_MATRIX GROUPA=cn GROUPB=Pb SWITCH={RATIONAL R_0=0.75}
     135             : # Coordination number of methylamonium with Pb
     136             : cc_cnpb: MATRIX_VECTOR_PRODUCT ARG=cm_cnpb,ones64
     137             : # Vector with elements that are one if coordination of methylamounium with lead is >7
     138             : mt_cnpb: MORE_THAN ARG=cc_cnpb SWITCH={RATIONAL R_0=7 NN=12 MM=24}
     139             : 
     140             : ones192: ONES SIZE=192
     141             : # Contact matrix that determines if methylamoinium moleulcule and I atom are within 6.5 A of each other
     142             : cm_cnI: CONTACT_MATRIX GROUPA=cn GROUPB=I SWITCH={RATIONAL R_0=0.65}
     143             : # Coordination number of methylamonium with I
     144             : cc_cnI: MATRIX_VECTOR_PRODUCT ARG=cm_cnI,ones192
     145             : # Vector with elements that are one if coordination of methylamounium with lead is >11
     146             : mt_cnI: MORE_THAN ARG=cc_cnI SWITCH={RATIONAL R_0=11 NN=12 MM=24}
     147             : 
     148             : # Element wise product of these three input vectors.
     149             : # mm[i]==1 if coordination number of corrsponding methylamounium with methylamonium is >5
     150             : # and if coordination of methylamounium with Pb is >7 and if coordination of methylamounium with I > 11
     151             : mm: CUSTOM ARG=mt_cncn,mt_cnpb,mt_cnI FUNC=x*y*z PERIODIC=NO
     152             : 
     153             : # Sum of coordination numbers and thus equal to number of methylamoniums with desired coordination numbers
     154             : ff: SUM ARG=mm PERIODIC=NO
     155             : 
     156             : rr: RESTRAINT ARG=ff AT=62 KAPPA=10
     157             : ```
     158             : 
     159             : ## COMPONENTS flag
     160             : 
     161             : If you add the flag COMPONENTS to the input as shown below:
     162             : 
     163             : ```plumed
     164             : c4: CONTACT_MATRIX GROUP=1-7 COMPONENTS SWITCH={RATIONAL R_0=2.6 NN=6 MM=12}
     165             : ```
     166             : 
     167             : then four matrices with the labels `c4.w`, `c4.x`, `c4.y` and `c4.z` are output by the action. The matrix with the label `c4.w` is the adjacency matrix
     168             : that would be output if you had not added the COMPONENTS flag. The $i,j$ component of the matrices `c4.x`, `c4.y` and `c4.z` contain the $x$, $y$ and $z$
     169             : components of the vector connecting atoms $i$ and $j$. Importantly, however, the components of these vectors are only stored in `c4.x`, `c4.y` and `c4.z`
     170             : if the elements of `c4.w` are non-zero.
     171             : 
     172             : ## NOPBC flag
     173             : 
     174             : By default PLUMED calculates the distances that are transformed by the switching function in the CONTACT_MATRIX action in a way that accounts for the periodic boundary
     175             : conditions.  If for any reason you do not want PLUMED to account for the periodic boundary conditions you can use the NOPBC flag as shown below:
     176             : 
     177             : ```plumed
     178             : c: CONTACT_MATRIX GROUP=1-7 NOPBC SWITCH={RATIONAL R_0=2.6 NN=6 MM=12}
     179             : ```
     180             : 
     181             : If you also add the COMPONENTS flag in this input then the vector connecting each pairs of atoms will also be calculated without taking the periodic boundary conditions into account.
     182             : 
     183             : ## Optimisation details
     184             : 
     185             : Adjacency matrices are sparse.  Each atom is only be connected to a small number of neighbours and the vast majority of the elements of the contact matrix are thus zero.  To reduce
     186             : the amount of memory that PLUMED requires PLUMED uses sparse matrix storage.  Consequently, whenever you calculate and store a contact matrix only the elements of the matrix that are
     187             : non-zero are stored.  The same thing holds for the additional values that are created when you use the COMPONENTS flag. The components of the vectors connecting atoms are only stored
     188             : when the elements of `c4.w` are non-zero.
     189             : 
     190             : We can also use the sparsity of the adjacency matrix to make the time required to compute a contact matrix scale linearly rather than quadratically with the number of atoms. Element
     191             : $i,j$ of the contact matrix is only non-zero if two atoms are within a cutoff, $r_c$. We can determine that many pairs of atoms are further appart than $r_c$ without computing the
     192             : distance between these atoms by using divide and conquer strategies such as linked lists and neighbour lists.  __To turn on these features you need to set the `D_MAX` parameter in the
     193             : switching functions.__ The value you pass to the `D_MAX` keyword is used as the cutoff in the link cell algorithm.
     194             : 
     195             : In theory we could further optimize the implementation of the CONTACT_MATRIX action by exploiting neighbor lists. If we were to do this we would likely add two further keywords as shown
     196             : below:
     197             : 
     198             : ```plumed
     199             : c4: CONTACT_MATRIX GROUP=1-10000 SWITCH={RATIONAL R_0=0.1 NN=6 MM=12 D_MAX=0.5} NL_CUTOFF=0.7 NL_STRIDE=5
     200             : ```
     201             : 
     202             : The `NL_CUTOFF` keyword would be used to specify the cutoff (in nm) to use when constructing neighbor lists.  This value would need to be slightly larger than the D_MAX parameter for the switching function.
     203             : The `NL_STRIDE` keyword would then be used to specify how frequently the neighbour list should be updated.  Thus far we have not found it necessary to implement this algorithm. We have been happy with the
     204             : performance even if we use the linked list algorithm to update the neighbors on every step. If you feel that you need this CV to perform better please get in touch as adding a neighbor list for this action
     205             : should be relatively straightforward.
     206             : 
     207             : ## The MASK keyword
     208             : 
     209             : Suppose that you want to calculate the average coordination number for the atoms that are within a sphere in the center of your simulation box. You can do so by exploiting an input similar to the one shown
     210             : below:
     211             : 
     212             : ```plumed
     213             : # The atoms that are of interest
     214             : ow: GROUP ATOMS=1-16500
     215             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     216             : center: FIXEDATOM AT=2.5,2.5,2.5
     217             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     218             : sphere: INSPHERE ATOMS=ow CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     219             : # Calculates cooordination numbers
     220             : cmap: CONTACT_MATRIX GROUP=ow SWITCH={GAUSSIAN D_0=0.32 R_0=0.01 D_MAX=0.34}
     221             : ones: ONES SIZE=16500
     222             : cc: MATRIX_VECTOR_PRODUCT ARG=cmap,ones
     223             : # Multiply coordination numbers by sphere vector
     224             : prod: CUSTOM ARG=cc,sphere FUNC=x*y PERIODIC=NO
     225             : # Sum of coordination numbers for atoms that are in the sphere of interest
     226             : numer: SUM ARG=prod PERIODIC=NO
     227             : # Number of atoms that are in sphere of interest
     228             : denom: SUM ARG=sphere PERIODIC=NO
     229             : # Average coordination number for atoms in sphere of interest
     230             : av: CUSTOM ARG=prod,sphere FUNC=x/y PERIODIC=NO
     231             : # And print out final CV to a file
     232             : PRINT ARG=av FILE=colvar STRIDE=1
     233             : ```
     234             : 
     235             : This calculation is slow because you have to calculate the coordination numbers of all the atoms even though only a small subset of these quanitties are required to compute the average coordination number in the
     236             : sphere.  To avoid all these unecessary calculations you use the `MASK` keyword as shown below:
     237             : 
     238             : ```plumed
     239             : # The atoms that are of interest
     240             : ow: GROUP ATOMS=1-16500
     241             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     242             : center: FIXEDATOM AT=2.5,2.5,2.5
     243             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     244             : sphere: INSPHERE ATOMS=ow CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     245             : # Calculates cooordination numbers
     246             : cmap: CONTACT_MATRIX GROUP=ow SWITCH={GAUSSIAN D_0=0.32 R_0=0.01 D_MAX=0.34} MASK=sphere
     247             : ones: ONES SIZE=16500
     248             : cc: MATRIX_VECTOR_PRODUCT ARG=cmap,ones
     249             : # Multiply coordination numbers by sphere vector
     250             : prod: CUSTOM ARG=cc,sphere FUNC=x*y PERIODIC=NO
     251             : # Sum of coordination numbers for atoms that are in the sphere of interest
     252             : numer: SUM ARG=prod PERIODIC=NO
     253             : # Number of atoms that are in sphere of interest
     254             : denom: SUM ARG=sphere PERIODIC=NO
     255             : # Average coordination number for atoms in sphere of interest
     256             : av: CUSTOM ARG=prod,sphere FUNC=x/y PERIODIC=NO
     257             : # And print out final CV to a file
     258             : PRINT ARG=av FILE=colvar STRIDE=1
     259             : ```
     260             : 
     261             : Adding the instruction `MASK=sphere` to the CONTACT_MATRIX line in this input tells PLUMED to only calculate the $i$th row in the adjacency matrix if the $i$th element of the vector `sphere` is non-zero.
     262             : In other words, by adding this command we have ensured that we are not calculating coordination numbers for atoms that are not in the sphere that is of interest.  In this way we can thus reduce the computational
     263             : expense of the calculation enormously.
     264             : 
     265             : Notice, that there are other places where we can use this same trick.  For example, we could have used MASK as shown below for our calculation of the CV for perovskite nucleation as shown below:
     266             : 
     267             : ```plumed
     268             : # Lead ions
     269             : Pb: GROUP ATOMS=1-64
     270             : # Iodide atoms
     271             : I: GROUP ATOMS=65-256
     272             : # Methylamonium "atoms" -- in the real CV these are centers of mass rather than single atoms
     273             : cn: GROUP ATOMS=257-320
     274             : 
     275             : ones192: ONES SIZE=192
     276             : # Contact matrix that determines if methylamoinium moleulcule and I atom are within 6.5 A of each other
     277             : cm_cnI: CONTACT_MATRIX GROUPA=cn GROUPB=I SWITCH={RATIONAL R_0=0.65 D_MAX=1.0}
     278             : # Coordination number of methylamonium with I
     279             : cc_cnI: MATRIX_VECTOR_PRODUCT ARG=cm_cnI,ones192
     280             : # Vector with elements that are one if coordination of methylamounium with lead is >11
     281             : mt_cnI: MORE_THAN ARG=cc_cnI SWITCH={RATIONAL R_0=11 NN=12 MM=24}
     282             : 
     283             : ones64: ONES SIZE=64
     284             : # Contact matrix that determines if methylamoinium moleulcule and Pb atom are within 7.5 A of each other
     285             : cm_cnpb: CONTACT_MATRIX GROUPA=cn GROUPB=Pb SWITCH={RATIONAL R_0=0.75 D_MAX=1.5} MASK=mt_cnI
     286             : # Coordination number of methylamonium with Pb
     287             : cc_cnpb: MATRIX_VECTOR_PRODUCT ARG=cm_cnpb,ones64
     288             : # Vector with elements that are one if coordination of methylamounium with lead is >7
     289             : mt_cnpb: MORE_THAN ARG=cc_cnpb SWITCH={RATIONAL R_0=7 NN=12 MM=24}
     290             : 
     291             : # Contact matrix that determines if methylamonium molecules are within 8 A of each other
     292             : cm_cncn: CONTACT_MATRIX GROUP=cn SWITCH={RATIONAL R_0=0.8 D_MAX=1.75} MASK=mt_cnI
     293             : # Coordination number of methylamounium with methylamonium
     294             : cc_cncn: MATRIX_VECTOR_PRODUCT ARG=cm_cncn,ones64
     295             : # Vector with elements that are one if coordiantion of methylamonium with methylamonium >5
     296             : mt_cncn: MORE_THAN ARG=cc_cncn SWITCH={RATIONAL R_0=5 NN=12 MM=24}
     297             : 
     298             : # Element wise product of these three input vectors.
     299             : # mm[i]==1 if coordination number of corrsponding methylamounium with methylamonium is >5
     300             : # and if coordination of methylamounium with Pb is >7 and if coordination of methylamounium with I > 11
     301             : mm: CUSTOM ARG=mt_cncn,mt_cnpb,mt_cnI FUNC=x*y*z PERIODIC=NO
     302             : 
     303             : # Sum of coordination numbers and thus equal to number of methylamoniums with desired coordination numbers
     304             : ff: SUM ARG=mm PERIODIC=NO
     305             : 
     306             : rr: RESTRAINT ARG=ff AT=62 KAPPA=10
     307             : ```
     308             : 
     309             : This trick works here because when we find that there are methylamoinium moleulcules with fewer than 11 lead atoms in there first coordination sphere we know that there
     310             : is no point calculating the second two coordination numbers.
     311             : 
     312             : */
     313             : //+ENDPLUMEDOC
     314             : 
     315             : namespace PLMD {
     316             : namespace adjmat {
     317             : 
     318             : class ContactMatrixShortcut : public ActionShortcut {
     319             : public:
     320             :   static void registerKeywords(Keywords& keys);
     321             :   explicit ContactMatrixShortcut(const ActionOptions&);
     322             : };
     323             : 
     324             : PLUMED_REGISTER_ACTION(ContactMatrixShortcut,"CONTACT_MATRIX")
     325             : 
     326         346 : void ContactMatrixShortcut::registerKeywords(Keywords& keys) {
     327         346 :   AdjacencyMatrixBase<ContactMatrix>::registerKeywords( keys );
     328         346 :   keys.remove("GROUP");
     329         692 :   keys.remove("SWITCH");
     330         346 :   keys.add("numbered","GROUP","specifies the list of atoms that should be assumed indistinguishable");
     331         346 :   keys.add("numbered","SWITCH","the input for the switching function that acts upon the distance between each pair of atoms");
     332         692 :   keys.linkActionInDocs("SWITCH","LESS_THAN");
     333         346 :   keys.addActionNameSuffix("_PROPER");
     334         346 :   keys.needsAction("TRANSPOSE");
     335         346 :   keys.needsAction("CONCATENATE");
     336         346 : }
     337             : 
     338         217 : ContactMatrixShortcut::ContactMatrixShortcut(const ActionOptions& ao):
     339             :   Action(ao),
     340         217 :   ActionShortcut(ao) {
     341             :   std::vector<std::string> grp_str;
     342         217 :   std::string atomsstr="";
     343             :   std::vector<std::string> atomsvec;
     344         434 :   parseVector("ATOMS",atomsvec);
     345         217 :   if( atomsvec.size()>0 )  {
     346           0 :     for(unsigned i=0; i<atomsvec.size(); ++i) {
     347           0 :       Group* gg = plumed.getActionSet().selectWithLabel<Group*>( atomsvec[i] );
     348           0 :       if( gg ) {
     349           0 :         grp_str.push_back( atomsvec[i] );
     350             :       }
     351             :     }
     352           0 :     if( grp_str.size()!=atomsvec.size() ) {
     353           0 :       grp_str.resize(0);
     354           0 :       atomsstr = " ATOMS=" + atomsvec[0];
     355           0 :       for(unsigned i=1; i<atomsvec.size(); ++i) {
     356           0 :         atomsstr += "," + atomsvec[i];
     357             :       }
     358             :     }
     359             :   } else {
     360             :     std::string grp_inpt;
     361           2 :     for(unsigned i=1;; ++i) {
     362         438 :       if( !parseNumbered("GROUP",i,grp_inpt) ) {
     363             :         break;
     364             :       }
     365           2 :       grp_str.push_back( grp_inpt );
     366             :     }
     367             :   }
     368         217 :   if( grp_str.size()>9 ) {
     369           0 :     error("cannot handle more than 9 groups");
     370             :   }
     371         217 :   if( grp_str.size()==0 )  {
     372         432 :     readInputLine( getShortcutLabel() + ": CONTACT_MATRIX_PROPER " + atomsstr + " " + convertInputLineToString() );
     373             :     return;
     374             :   }
     375             : 
     376           3 :   for(unsigned i=0; i<grp_str.size(); ++i) {
     377             :     std::string sw_str, num;
     378           2 :     Tools::convert( i+1, num );
     379           4 :     parseNumbered("SWITCH", (i+1)*10 + 1 + i,  sw_str );
     380           2 :     if( sw_str.length()==0 ) {
     381           0 :       error("missing SWITCH" + num + num + " keyword");
     382             :     }
     383           4 :     readInputLine( getShortcutLabel() + num +  num + ": CONTACT_MATRIX_PROPER GROUP=" + grp_str[i] + " SWITCH={" + sw_str + "}" );
     384           3 :     for(unsigned j=0; j<i; ++j) {
     385             :       std::string sw_str2, jnum;
     386           1 :       Tools::convert( j+1, jnum );
     387           2 :       parseNumbered("SWITCH", (j+1)*10 + 1 + i, sw_str2);
     388           1 :       if( sw_str2.length()==0 ) {
     389           0 :         error("missing SWITCH" + jnum + num + " keyword");
     390             :       }
     391           2 :       readInputLine( getShortcutLabel() + jnum + num + ": CONTACT_MATRIX_PROPER GROUPA=" + grp_str[j] + " GROUPB=" + grp_str[i] + " SWITCH={" + sw_str2 +"}");
     392           2 :       readInputLine( getShortcutLabel() + num +  jnum + ": TRANSPOSE ARG=" + getShortcutLabel() + jnum + num );
     393             :     }
     394             :   }
     395           1 :   std::string join_matrices = getShortcutLabel() + ": CONCATENATE";
     396           3 :   for(unsigned i=0; i<grp_str.size(); ++i) {
     397             :     std::string inum;
     398           2 :     Tools::convert(i+1,inum);
     399           6 :     for(unsigned j=0; j<grp_str.size(); ++j) {
     400             :       std::string jnum;
     401           4 :       Tools::convert(j+1,jnum);
     402           4 :       if( i>j ) {
     403           2 :         join_matrices += " MATRIX" + inum + jnum + "=" + getShortcutLabel() + inum +  jnum;
     404             :       } else {
     405           6 :         join_matrices += " MATRIX" + inum + jnum + "=" + getShortcutLabel() + inum +  jnum;
     406             :       }
     407             :     }
     408             :   }
     409           1 :   readInputLine( join_matrices );
     410         434 : }
     411             : 
     412             : }
     413             : }

Generated by: LCOV version 1.16