hillclimber 0.1.6__cp313-cp313-macosx_15_0_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (468) hide show
  1. hillclimber/__init__.py +41 -0
  2. hillclimber/actions.py +53 -0
  3. hillclimber/analysis.py +590 -0
  4. hillclimber/biases.py +293 -0
  5. hillclimber/calc.py +22 -0
  6. hillclimber/cvs.py +1070 -0
  7. hillclimber/interfaces.py +133 -0
  8. hillclimber/metadynamics.py +416 -0
  9. hillclimber/nodes.py +6 -0
  10. hillclimber/opes.py +359 -0
  11. hillclimber/pycv.py +362 -0
  12. hillclimber/selectors.py +230 -0
  13. hillclimber/virtual_atoms.py +341 -0
  14. hillclimber-0.1.6.dist-info/METADATA +325 -0
  15. hillclimber-0.1.6.dist-info/RECORD +468 -0
  16. hillclimber-0.1.6.dist-info/WHEEL +6 -0
  17. hillclimber-0.1.6.dist-info/entry_points.txt +8 -0
  18. hillclimber-0.1.6.dist-info/licenses/LICENSE +165 -0
  19. plumed/__init__.py +104 -0
  20. plumed/_lib/bin/plumed +0 -0
  21. plumed/_lib/bin/plumed-config +9 -0
  22. plumed/_lib/bin/plumed-patch +9 -0
  23. plumed/_lib/include/plumed/adjmat/AdjacencyMatrixBase.h +659 -0
  24. plumed/_lib/include/plumed/adjmat/ContactMatrix.h +59 -0
  25. plumed/_lib/include/plumed/asmjit/arch.h +228 -0
  26. plumed/_lib/include/plumed/asmjit/arm.h +43 -0
  27. plumed/_lib/include/plumed/asmjit/asmjit.h +69 -0
  28. plumed/_lib/include/plumed/asmjit/asmjit_apibegin.h +143 -0
  29. plumed/_lib/include/plumed/asmjit/asmjit_apiend.h +93 -0
  30. plumed/_lib/include/plumed/asmjit/asmjit_build.h +971 -0
  31. plumed/_lib/include/plumed/asmjit/assembler.h +183 -0
  32. plumed/_lib/include/plumed/asmjit/base.h +56 -0
  33. plumed/_lib/include/plumed/asmjit/codebuilder.h +944 -0
  34. plumed/_lib/include/plumed/asmjit/codecompiler.h +767 -0
  35. plumed/_lib/include/plumed/asmjit/codeemitter.h +528 -0
  36. plumed/_lib/include/plumed/asmjit/codeholder.h +777 -0
  37. plumed/_lib/include/plumed/asmjit/constpool.h +286 -0
  38. plumed/_lib/include/plumed/asmjit/cpuinfo.h +402 -0
  39. plumed/_lib/include/plumed/asmjit/func.h +1327 -0
  40. plumed/_lib/include/plumed/asmjit/globals.h +370 -0
  41. plumed/_lib/include/plumed/asmjit/inst.h +137 -0
  42. plumed/_lib/include/plumed/asmjit/logging.h +317 -0
  43. plumed/_lib/include/plumed/asmjit/misc_p.h +103 -0
  44. plumed/_lib/include/plumed/asmjit/moved_string.h +318 -0
  45. plumed/_lib/include/plumed/asmjit/operand.h +1599 -0
  46. plumed/_lib/include/plumed/asmjit/osutils.h +207 -0
  47. plumed/_lib/include/plumed/asmjit/regalloc_p.h +597 -0
  48. plumed/_lib/include/plumed/asmjit/runtime.h +227 -0
  49. plumed/_lib/include/plumed/asmjit/simdtypes.h +1104 -0
  50. plumed/_lib/include/plumed/asmjit/utils.h +1387 -0
  51. plumed/_lib/include/plumed/asmjit/vmem.h +183 -0
  52. plumed/_lib/include/plumed/asmjit/x86.h +45 -0
  53. plumed/_lib/include/plumed/asmjit/x86assembler.h +125 -0
  54. plumed/_lib/include/plumed/asmjit/x86builder.h +117 -0
  55. plumed/_lib/include/plumed/asmjit/x86compiler.h +322 -0
  56. plumed/_lib/include/plumed/asmjit/x86emitter.h +5149 -0
  57. plumed/_lib/include/plumed/asmjit/x86globals.h +535 -0
  58. plumed/_lib/include/plumed/asmjit/x86inst.h +2547 -0
  59. plumed/_lib/include/plumed/asmjit/x86instimpl_p.h +74 -0
  60. plumed/_lib/include/plumed/asmjit/x86internal_p.h +108 -0
  61. plumed/_lib/include/plumed/asmjit/x86logging_p.h +92 -0
  62. plumed/_lib/include/plumed/asmjit/x86misc.h +417 -0
  63. plumed/_lib/include/plumed/asmjit/x86operand.h +1133 -0
  64. plumed/_lib/include/plumed/asmjit/x86regalloc_p.h +734 -0
  65. plumed/_lib/include/plumed/asmjit/zone.h +1157 -0
  66. plumed/_lib/include/plumed/bias/Bias.h +82 -0
  67. plumed/_lib/include/plumed/bias/ReweightBase.h +58 -0
  68. plumed/_lib/include/plumed/blas/blas.h +253 -0
  69. plumed/_lib/include/plumed/blas/def_external.h +61 -0
  70. plumed/_lib/include/plumed/blas/def_internal.h +97 -0
  71. plumed/_lib/include/plumed/blas/real.h +49 -0
  72. plumed/_lib/include/plumed/cltools/CLTool.h +32 -0
  73. plumed/_lib/include/plumed/clusters/ClusteringBase.h +70 -0
  74. plumed/_lib/include/plumed/colvar/Colvar.h +32 -0
  75. plumed/_lib/include/plumed/colvar/ColvarInput.h +68 -0
  76. plumed/_lib/include/plumed/colvar/ColvarShortcut.h +81 -0
  77. plumed/_lib/include/plumed/colvar/CoordinationBase.h +52 -0
  78. plumed/_lib/include/plumed/colvar/MultiColvarTemplate.h +333 -0
  79. plumed/_lib/include/plumed/colvar/PathMSDBase.h +101 -0
  80. plumed/_lib/include/plumed/colvar/RMSDVector.h +78 -0
  81. plumed/_lib/include/plumed/config/Config.h +118 -0
  82. plumed/_lib/include/plumed/config/version.h +9 -0
  83. plumed/_lib/include/plumed/contour/ContourFindingObject.h +87 -0
  84. plumed/_lib/include/plumed/contour/DistanceFromContourBase.h +82 -0
  85. plumed/_lib/include/plumed/contour/FindContour.h +67 -0
  86. plumed/_lib/include/plumed/core/Action.h +540 -0
  87. plumed/_lib/include/plumed/core/ActionAnyorder.h +48 -0
  88. plumed/_lib/include/plumed/core/ActionAtomistic.h +343 -0
  89. plumed/_lib/include/plumed/core/ActionForInterface.h +99 -0
  90. plumed/_lib/include/plumed/core/ActionPilot.h +57 -0
  91. plumed/_lib/include/plumed/core/ActionRegister.h +124 -0
  92. plumed/_lib/include/plumed/core/ActionSet.h +163 -0
  93. plumed/_lib/include/plumed/core/ActionSetup.h +48 -0
  94. plumed/_lib/include/plumed/core/ActionShortcut.h +73 -0
  95. plumed/_lib/include/plumed/core/ActionToGetData.h +59 -0
  96. plumed/_lib/include/plumed/core/ActionToPutData.h +101 -0
  97. plumed/_lib/include/plumed/core/ActionWithArguments.h +140 -0
  98. plumed/_lib/include/plumed/core/ActionWithMatrix.h +87 -0
  99. plumed/_lib/include/plumed/core/ActionWithValue.h +258 -0
  100. plumed/_lib/include/plumed/core/ActionWithVector.h +94 -0
  101. plumed/_lib/include/plumed/core/ActionWithVirtualAtom.h +123 -0
  102. plumed/_lib/include/plumed/core/CLTool.h +177 -0
  103. plumed/_lib/include/plumed/core/CLToolMain.h +102 -0
  104. plumed/_lib/include/plumed/core/CLToolRegister.h +108 -0
  105. plumed/_lib/include/plumed/core/Colvar.h +115 -0
  106. plumed/_lib/include/plumed/core/DataPassingObject.h +94 -0
  107. plumed/_lib/include/plumed/core/DataPassingTools.h +54 -0
  108. plumed/_lib/include/plumed/core/DomainDecomposition.h +120 -0
  109. plumed/_lib/include/plumed/core/ExchangePatterns.h +47 -0
  110. plumed/_lib/include/plumed/core/FlexibleBin.h +63 -0
  111. plumed/_lib/include/plumed/core/GREX.h +61 -0
  112. plumed/_lib/include/plumed/core/GenericMolInfo.h +89 -0
  113. plumed/_lib/include/plumed/core/Group.h +41 -0
  114. plumed/_lib/include/plumed/core/ModuleMap.h +30 -0
  115. plumed/_lib/include/plumed/core/ParallelTaskManager.h +1023 -0
  116. plumed/_lib/include/plumed/core/PbcAction.h +61 -0
  117. plumed/_lib/include/plumed/core/PlumedMain.h +632 -0
  118. plumed/_lib/include/plumed/core/PlumedMainInitializer.h +118 -0
  119. plumed/_lib/include/plumed/core/RegisterBase.h +340 -0
  120. plumed/_lib/include/plumed/core/TargetDist.h +48 -0
  121. plumed/_lib/include/plumed/core/Value.h +547 -0
  122. plumed/_lib/include/plumed/core/WithCmd.h +93 -0
  123. plumed/_lib/include/plumed/dimred/SMACOF.h +55 -0
  124. plumed/_lib/include/plumed/drr/DRR.h +383 -0
  125. plumed/_lib/include/plumed/drr/colvar_UIestimator.h +777 -0
  126. plumed/_lib/include/plumed/fisst/legendre_rule_fast.h +44 -0
  127. plumed/_lib/include/plumed/function/Custom.h +54 -0
  128. plumed/_lib/include/plumed/function/Function.h +85 -0
  129. plumed/_lib/include/plumed/function/FunctionOfMatrix.h +368 -0
  130. plumed/_lib/include/plumed/function/FunctionOfScalar.h +135 -0
  131. plumed/_lib/include/plumed/function/FunctionOfVector.h +296 -0
  132. plumed/_lib/include/plumed/function/FunctionSetup.h +180 -0
  133. plumed/_lib/include/plumed/function/FunctionShortcut.h +130 -0
  134. plumed/_lib/include/plumed/function/FunctionWithSingleArgument.h +165 -0
  135. plumed/_lib/include/plumed/gridtools/ActionWithGrid.h +43 -0
  136. plumed/_lib/include/plumed/gridtools/EvaluateGridFunction.h +99 -0
  137. plumed/_lib/include/plumed/gridtools/FunctionOfGrid.h +295 -0
  138. plumed/_lib/include/plumed/gridtools/GridCoordinatesObject.h +179 -0
  139. plumed/_lib/include/plumed/gridtools/GridSearch.h +135 -0
  140. plumed/_lib/include/plumed/gridtools/Interpolator.h +45 -0
  141. plumed/_lib/include/plumed/gridtools/KDE.h +455 -0
  142. plumed/_lib/include/plumed/gridtools/RDF.h +40 -0
  143. plumed/_lib/include/plumed/gridtools/SumOfKernels.h +219 -0
  144. plumed/_lib/include/plumed/isdb/MetainferenceBase.h +398 -0
  145. plumed/_lib/include/plumed/lapack/def_external.h +207 -0
  146. plumed/_lib/include/plumed/lapack/def_internal.h +388 -0
  147. plumed/_lib/include/plumed/lapack/lapack.h +899 -0
  148. plumed/_lib/include/plumed/lapack/lapack_limits.h +79 -0
  149. plumed/_lib/include/plumed/lapack/real.h +50 -0
  150. plumed/_lib/include/plumed/lepton/CompiledExpression.h +164 -0
  151. plumed/_lib/include/plumed/lepton/CustomFunction.h +143 -0
  152. plumed/_lib/include/plumed/lepton/Exception.h +93 -0
  153. plumed/_lib/include/plumed/lepton/ExpressionProgram.h +137 -0
  154. plumed/_lib/include/plumed/lepton/ExpressionTreeNode.h +145 -0
  155. plumed/_lib/include/plumed/lepton/Lepton.h +85 -0
  156. plumed/_lib/include/plumed/lepton/MSVC_erfc.h +123 -0
  157. plumed/_lib/include/plumed/lepton/Operation.h +1302 -0
  158. plumed/_lib/include/plumed/lepton/ParsedExpression.h +165 -0
  159. plumed/_lib/include/plumed/lepton/Parser.h +111 -0
  160. plumed/_lib/include/plumed/lepton/windowsIncludes.h +73 -0
  161. plumed/_lib/include/plumed/mapping/Path.h +44 -0
  162. plumed/_lib/include/plumed/mapping/PathProjectionCalculator.h +57 -0
  163. plumed/_lib/include/plumed/matrixtools/MatrixOperationBase.h +54 -0
  164. plumed/_lib/include/plumed/matrixtools/MatrixTimesMatrix.h +309 -0
  165. plumed/_lib/include/plumed/matrixtools/MatrixTimesVectorBase.h +365 -0
  166. plumed/_lib/include/plumed/matrixtools/OuterProduct.h +238 -0
  167. plumed/_lib/include/plumed/maze/Core.h +65 -0
  168. plumed/_lib/include/plumed/maze/Loss.h +86 -0
  169. plumed/_lib/include/plumed/maze/Member.h +66 -0
  170. plumed/_lib/include/plumed/maze/Memetic.h +799 -0
  171. plumed/_lib/include/plumed/maze/Optimizer.h +357 -0
  172. plumed/_lib/include/plumed/maze/Random_MT.h +156 -0
  173. plumed/_lib/include/plumed/maze/Tools.h +183 -0
  174. plumed/_lib/include/plumed/metatomic/vesin.h +188 -0
  175. plumed/_lib/include/plumed/molfile/Gromacs.h +2013 -0
  176. plumed/_lib/include/plumed/molfile/endianswap.h +217 -0
  177. plumed/_lib/include/plumed/molfile/fastio.h +683 -0
  178. plumed/_lib/include/plumed/molfile/largefiles.h +78 -0
  179. plumed/_lib/include/plumed/molfile/libmolfile_plugin.h +77 -0
  180. plumed/_lib/include/plumed/molfile/molfile_plugin.h +1034 -0
  181. plumed/_lib/include/plumed/molfile/periodic_table.h +248 -0
  182. plumed/_lib/include/plumed/molfile/readpdb.h +447 -0
  183. plumed/_lib/include/plumed/molfile/vmdplugin.h +236 -0
  184. plumed/_lib/include/plumed/multicolvar/MultiColvarShortcuts.h +45 -0
  185. plumed/_lib/include/plumed/opes/ExpansionCVs.h +79 -0
  186. plumed/_lib/include/plumed/sasa/Sasa.h +32 -0
  187. plumed/_lib/include/plumed/secondarystructure/SecondaryStructureBase.h +372 -0
  188. plumed/_lib/include/plumed/setup/ActionSetup.h +25 -0
  189. plumed/_lib/include/plumed/small_vector/small_vector.h +6114 -0
  190. plumed/_lib/include/plumed/symfunc/CoordinationNumbers.h +41 -0
  191. plumed/_lib/include/plumed/tools/Angle.h +52 -0
  192. plumed/_lib/include/plumed/tools/AtomDistribution.h +138 -0
  193. plumed/_lib/include/plumed/tools/AtomNumber.h +152 -0
  194. plumed/_lib/include/plumed/tools/BiasRepresentation.h +106 -0
  195. plumed/_lib/include/plumed/tools/BitmaskEnum.h +167 -0
  196. plumed/_lib/include/plumed/tools/Brent1DRootSearch.h +159 -0
  197. plumed/_lib/include/plumed/tools/CheckInRange.h +44 -0
  198. plumed/_lib/include/plumed/tools/Citations.h +74 -0
  199. plumed/_lib/include/plumed/tools/ColvarOutput.h +118 -0
  200. plumed/_lib/include/plumed/tools/Communicator.h +316 -0
  201. plumed/_lib/include/plumed/tools/ConjugateGradient.h +80 -0
  202. plumed/_lib/include/plumed/tools/DLLoader.h +79 -0
  203. plumed/_lib/include/plumed/tools/ERMSD.h +73 -0
  204. plumed/_lib/include/plumed/tools/Exception.h +406 -0
  205. plumed/_lib/include/plumed/tools/File.h +28 -0
  206. plumed/_lib/include/plumed/tools/FileBase.h +153 -0
  207. plumed/_lib/include/plumed/tools/FileTools.h +37 -0
  208. plumed/_lib/include/plumed/tools/ForwardDecl.h +54 -0
  209. plumed/_lib/include/plumed/tools/Grid.h +638 -0
  210. plumed/_lib/include/plumed/tools/HistogramBead.h +136 -0
  211. plumed/_lib/include/plumed/tools/IFile.h +117 -0
  212. plumed/_lib/include/plumed/tools/KernelFunctions.h +113 -0
  213. plumed/_lib/include/plumed/tools/Keywords.h +380 -0
  214. plumed/_lib/include/plumed/tools/LatticeReduction.h +66 -0
  215. plumed/_lib/include/plumed/tools/LeptonCall.h +64 -0
  216. plumed/_lib/include/plumed/tools/LinkCells.h +126 -0
  217. plumed/_lib/include/plumed/tools/Log.h +41 -0
  218. plumed/_lib/include/plumed/tools/LoopUnroller.h +163 -0
  219. plumed/_lib/include/plumed/tools/Matrix.h +721 -0
  220. plumed/_lib/include/plumed/tools/MatrixSquareBracketsAccess.h +138 -0
  221. plumed/_lib/include/plumed/tools/MergeVectorTools.h +153 -0
  222. plumed/_lib/include/plumed/tools/Minimise1DBrent.h +244 -0
  223. plumed/_lib/include/plumed/tools/MinimiseBase.h +120 -0
  224. plumed/_lib/include/plumed/tools/MolDataClass.h +51 -0
  225. plumed/_lib/include/plumed/tools/NeighborList.h +112 -0
  226. plumed/_lib/include/plumed/tools/OFile.h +286 -0
  227. plumed/_lib/include/plumed/tools/OpenACC.h +180 -0
  228. plumed/_lib/include/plumed/tools/OpenMP.h +75 -0
  229. plumed/_lib/include/plumed/tools/PDB.h +154 -0
  230. plumed/_lib/include/plumed/tools/Pbc.h +139 -0
  231. plumed/_lib/include/plumed/tools/PlumedHandle.h +105 -0
  232. plumed/_lib/include/plumed/tools/RMSD.h +493 -0
  233. plumed/_lib/include/plumed/tools/Random.h +80 -0
  234. plumed/_lib/include/plumed/tools/RootFindingBase.h +79 -0
  235. plumed/_lib/include/plumed/tools/Stopwatch.h +475 -0
  236. plumed/_lib/include/plumed/tools/Subprocess.h +142 -0
  237. plumed/_lib/include/plumed/tools/SwitchingFunction.h +208 -0
  238. plumed/_lib/include/plumed/tools/Tensor.h +724 -0
  239. plumed/_lib/include/plumed/tools/TokenizedLine.h +123 -0
  240. plumed/_lib/include/plumed/tools/Tools.h +638 -0
  241. plumed/_lib/include/plumed/tools/Torsion.h +55 -0
  242. plumed/_lib/include/plumed/tools/TrajectoryParser.h +118 -0
  243. plumed/_lib/include/plumed/tools/Tree.h +61 -0
  244. plumed/_lib/include/plumed/tools/TypesafePtr.h +463 -0
  245. plumed/_lib/include/plumed/tools/Units.h +167 -0
  246. plumed/_lib/include/plumed/tools/Vector.h +433 -0
  247. plumed/_lib/include/plumed/tools/View.h +296 -0
  248. plumed/_lib/include/plumed/tools/View2D.h +100 -0
  249. plumed/_lib/include/plumed/tools/h36.h +39 -0
  250. plumed/_lib/include/plumed/vatom/ActionWithVirtualAtom.h +32 -0
  251. plumed/_lib/include/plumed/ves/BasisFunctions.h +380 -0
  252. plumed/_lib/include/plumed/ves/CoeffsBase.h +310 -0
  253. plumed/_lib/include/plumed/ves/CoeffsMatrix.h +220 -0
  254. plumed/_lib/include/plumed/ves/CoeffsVector.h +251 -0
  255. plumed/_lib/include/plumed/ves/FermiSwitchingFunction.h +74 -0
  256. plumed/_lib/include/plumed/ves/GridIntegrationWeights.h +50 -0
  257. plumed/_lib/include/plumed/ves/GridLinearInterpolation.h +81 -0
  258. plumed/_lib/include/plumed/ves/GridProjWeights.h +61 -0
  259. plumed/_lib/include/plumed/ves/LinearBasisSetExpansion.h +303 -0
  260. plumed/_lib/include/plumed/ves/Optimizer.h +444 -0
  261. plumed/_lib/include/plumed/ves/TargetDistModifer.h +53 -0
  262. plumed/_lib/include/plumed/ves/TargetDistribution.h +266 -0
  263. plumed/_lib/include/plumed/ves/VesBias.h +545 -0
  264. plumed/_lib/include/plumed/ves/VesTools.h +142 -0
  265. plumed/_lib/include/plumed/ves/WaveletGrid.h +75 -0
  266. plumed/_lib/include/plumed/volumes/ActionVolume.h +268 -0
  267. plumed/_lib/include/plumed/volumes/VolumeShortcut.h +147 -0
  268. plumed/_lib/include/plumed/wrapper/Plumed.h +5025 -0
  269. plumed/_lib/include/plumed/xdrfile/xdrfile.h +663 -0
  270. plumed/_lib/include/plumed/xdrfile/xdrfile_trr.h +89 -0
  271. plumed/_lib/include/plumed/xdrfile/xdrfile_xtc.h +90 -0
  272. plumed/_lib/lib/PythonCVInterface.dylib +0 -0
  273. plumed/_lib/lib/libplumed.dylib +0 -0
  274. plumed/_lib/lib/libplumedKernel.dylib +0 -0
  275. plumed/_lib/lib/libplumedWrapper.a +0 -0
  276. plumed/_lib/lib/pkgconfig/plumed.pc +13 -0
  277. plumed/_lib/lib/pkgconfig/plumedInternals.pc +13 -0
  278. plumed/_lib/lib/pkgconfig/plumedWrapper.pc +13 -0
  279. plumed/_lib/lib/plumed/fortran/plumed.f90 +879 -0
  280. plumed/_lib/lib/plumed/fortran/plumed_f08.f90 +2625 -0
  281. plumed/_lib/lib/plumed/modulefile +69 -0
  282. plumed/_lib/lib/plumed/patches/gromacs-2022.5.config +43 -0
  283. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/CMakeLists.txt +543 -0
  284. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/CMakeLists.txt.preplumed +540 -0
  285. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdlib/expanded.cpp +1628 -0
  286. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdlib/expanded.cpp.preplumed +1590 -0
  287. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdlib/expanded.h +103 -0
  288. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdlib/expanded.h.preplumed +99 -0
  289. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdlib/sim_util.cpp +2527 -0
  290. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdlib/sim_util.cpp.preplumed +2513 -0
  291. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp +208 -0
  292. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp.preplumed +175 -0
  293. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.h +408 -0
  294. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.h.preplumed +394 -0
  295. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp +2348 -0
  296. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp.preplumed +2091 -0
  297. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/minimize.cpp +3573 -0
  298. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/minimize.cpp.preplumed +3495 -0
  299. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/replicaexchange.cpp +1506 -0
  300. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/replicaexchange.cpp.preplumed +1402 -0
  301. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/replicaexchange.h +114 -0
  302. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/replicaexchange.h.preplumed +106 -0
  303. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/rerun.cpp +997 -0
  304. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/rerun.cpp.preplumed +906 -0
  305. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp +2780 -0
  306. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp.preplumed +2738 -0
  307. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/modularsimulator/expandedensembleelement.cpp +224 -0
  308. plumed/_lib/lib/plumed/patches/gromacs-2022.5.diff/src/gromacs/modularsimulator/expandedensembleelement.cpp.preplumed +222 -0
  309. plumed/_lib/lib/plumed/patches/gromacs-2023.5.config +43 -0
  310. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/CMakeLists.txt +549 -0
  311. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/CMakeLists.txt.preplumed +546 -0
  312. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdlib/expanded.cpp +1632 -0
  313. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdlib/expanded.cpp.preplumed +1594 -0
  314. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdlib/expanded.h +104 -0
  315. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdlib/expanded.h.preplumed +100 -0
  316. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdlib/sim_util.cpp +2624 -0
  317. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdlib/sim_util.cpp.preplumed +2610 -0
  318. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp +208 -0
  319. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp.preplumed +175 -0
  320. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.h +409 -0
  321. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.h.preplumed +395 -0
  322. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp +2419 -0
  323. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp.preplumed +2164 -0
  324. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/minimize.cpp +3546 -0
  325. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/minimize.cpp.preplumed +3468 -0
  326. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/replicaexchange.cpp +1513 -0
  327. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/replicaexchange.cpp.preplumed +1409 -0
  328. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/replicaexchange.h +114 -0
  329. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/replicaexchange.h.preplumed +106 -0
  330. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/rerun.cpp +991 -0
  331. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/rerun.cpp.preplumed +900 -0
  332. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp +2895 -0
  333. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp.preplumed +2849 -0
  334. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/modularsimulator/expandedensembleelement.cpp +224 -0
  335. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/modularsimulator/expandedensembleelement.cpp.preplumed +222 -0
  336. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/taskassignment/decidegpuusage.cpp +886 -0
  337. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/taskassignment/decidegpuusage.cpp.preplumed +880 -0
  338. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/taskassignment/include/gromacs/taskassignment/decidegpuusage.h +347 -0
  339. plumed/_lib/lib/plumed/patches/gromacs-2023.5.diff/src/gromacs/taskassignment/include/gromacs/taskassignment/decidegpuusage.h.preplumed +345 -0
  340. plumed/_lib/lib/plumed/patches/gromacs-2024.3.config +43 -0
  341. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/CMakeLists.txt +575 -0
  342. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/CMakeLists.txt.preplumed +572 -0
  343. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdlib/expanded.cpp +1632 -0
  344. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdlib/expanded.cpp.preplumed +1594 -0
  345. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdlib/expanded.h +104 -0
  346. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdlib/expanded.h.preplumed +100 -0
  347. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdlib/sim_util.cpp +2564 -0
  348. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdlib/sim_util.cpp.preplumed +2550 -0
  349. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/legacymdrunoptions.cpp +208 -0
  350. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/legacymdrunoptions.cpp.preplumed +175 -0
  351. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/legacymdrunoptions.h +410 -0
  352. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/legacymdrunoptions.h.preplumed +396 -0
  353. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp +2435 -0
  354. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp.preplumed +2187 -0
  355. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/minimize.cpp +3592 -0
  356. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/minimize.cpp.preplumed +3514 -0
  357. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/replicaexchange.cpp +1513 -0
  358. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/replicaexchange.cpp.preplumed +1409 -0
  359. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/replicaexchange.h +114 -0
  360. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/replicaexchange.h.preplumed +106 -0
  361. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/rerun.cpp +958 -0
  362. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/rerun.cpp.preplumed +929 -0
  363. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/runner.cpp +2987 -0
  364. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/mdrun/runner.cpp.preplumed +2941 -0
  365. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/modularsimulator/expandedensembleelement.cpp +224 -0
  366. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/modularsimulator/expandedensembleelement.cpp.preplumed +222 -0
  367. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/taskassignment/decidegpuusage.cpp +904 -0
  368. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/taskassignment/decidegpuusage.cpp.preplumed +898 -0
  369. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/taskassignment/include/gromacs/taskassignment/decidegpuusage.h +353 -0
  370. plumed/_lib/lib/plumed/patches/gromacs-2024.3.diff/src/gromacs/taskassignment/include/gromacs/taskassignment/decidegpuusage.h.preplumed +351 -0
  371. plumed/_lib/lib/plumed/patches/gromacs-2025.0.config +39 -0
  372. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/cmake/gmxManagePlumed.cmake +82 -0
  373. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/cmake/gmxManagePlumed.cmake.preplumed +82 -0
  374. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedMDModule.cpp +162 -0
  375. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedMDModule.cpp.preplumed +154 -0
  376. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedOptions.cpp +107 -0
  377. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedOptions.cpp.preplumed +99 -0
  378. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedOptions.h +120 -0
  379. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedOptions.h.preplumed +111 -0
  380. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedforceprovider.cpp +215 -0
  381. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedforceprovider.cpp.preplumed +197 -0
  382. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedforceprovider.h +87 -0
  383. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/applied_forces/plumed/plumedforceprovider.h.preplumed +86 -0
  384. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/mdrun/runner.cpp +2971 -0
  385. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/mdrun/runner.cpp.preplumed +2970 -0
  386. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/mdrunutility/mdmodulesnotifiers.h +430 -0
  387. plumed/_lib/lib/plumed/patches/gromacs-2025.0.diff/src/gromacs/mdrunutility/mdmodulesnotifiers.h.preplumed +429 -0
  388. plumed/_lib/lib/plumed/patches/namd-2.12.config +30 -0
  389. plumed/_lib/lib/plumed/patches/namd-2.12.diff +267 -0
  390. plumed/_lib/lib/plumed/patches/namd-2.13.config +30 -0
  391. plumed/_lib/lib/plumed/patches/namd-2.13.diff +267 -0
  392. plumed/_lib/lib/plumed/patches/namd-2.14.config +30 -0
  393. plumed/_lib/lib/plumed/patches/namd-2.14.diff +268 -0
  394. plumed/_lib/lib/plumed/patches/patch.sh +500 -0
  395. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.config +25 -0
  396. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/forces.f90 +368 -0
  397. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/forces.f90.preplumed +366 -0
  398. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/plugin_forces.f90 +71 -0
  399. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/plugin_forces.f90.preplumed +24 -0
  400. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/plugin_initialization.f90 +62 -0
  401. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/plugin_initialization.f90.preplumed +21 -0
  402. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/pwscf.f90 +189 -0
  403. plumed/_lib/lib/plumed/patches/qespresso-5.0.2.diff/PW/src/pwscf.f90.preplumed +185 -0
  404. plumed/_lib/lib/plumed/patches/qespresso-6.2.config +26 -0
  405. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/forces.f90 +422 -0
  406. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/forces.f90.preplumed +420 -0
  407. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/plugin_ext_forces.f90 +70 -0
  408. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/plugin_ext_forces.f90.preplumed +23 -0
  409. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/plugin_initialization.f90 +62 -0
  410. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/plugin_initialization.f90.preplumed +21 -0
  411. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/run_pwscf.f90 +233 -0
  412. plumed/_lib/lib/plumed/patches/qespresso-6.2.diff/PW/src/run_pwscf.f90.preplumed +230 -0
  413. plumed/_lib/lib/plumed/patches/qespresso-7.0.config +28 -0
  414. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/Modules/Makefile +175 -0
  415. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/Modules/Makefile.preplumed +171 -0
  416. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/forces.f90 +486 -0
  417. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/forces.f90.preplumed +484 -0
  418. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/plugin_ext_forces.f90 +74 -0
  419. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/plugin_ext_forces.f90.preplumed +23 -0
  420. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/plugin_initialization.f90 +64 -0
  421. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/plugin_initialization.f90.preplumed +21 -0
  422. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/run_pwscf.f90 +532 -0
  423. plumed/_lib/lib/plumed/patches/qespresso-7.0.diff/PW/src/run_pwscf.f90.preplumed +518 -0
  424. plumed/_lib/lib/plumed/patches/qespresso-7.2.config +28 -0
  425. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/Modules/Makefile +249 -0
  426. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/Modules/Makefile.preplumed +244 -0
  427. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/forces.f90 +532 -0
  428. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/forces.f90.preplumed +535 -0
  429. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/plugin_ext_forces.f90 +74 -0
  430. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/plugin_ext_forces.f90.preplumed +23 -0
  431. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/plugin_initialization.f90 +64 -0
  432. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/plugin_initialization.f90.preplumed +21 -0
  433. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/run_pwscf.f90 +569 -0
  434. plumed/_lib/lib/plumed/patches/qespresso-7.2.diff/PW/src/run_pwscf.f90.preplumed +560 -0
  435. plumed/_lib/lib/plumed/plumed-config +9 -0
  436. plumed/_lib/lib/plumed/plumed-mklib +9 -0
  437. plumed/_lib/lib/plumed/plumed-newcv +9 -0
  438. plumed/_lib/lib/plumed/plumed-partial_tempering +9 -0
  439. plumed/_lib/lib/plumed/plumed-patch +9 -0
  440. plumed/_lib/lib/plumed/plumed-runtime +0 -0
  441. plumed/_lib/lib/plumed/plumed-selector +9 -0
  442. plumed/_lib/lib/plumed/plumed-vim2html +9 -0
  443. plumed/_lib/lib/plumed/scripts/config.sh +126 -0
  444. plumed/_lib/lib/plumed/scripts/mklib.sh +175 -0
  445. plumed/_lib/lib/plumed/scripts/newcv.sh +26 -0
  446. plumed/_lib/lib/plumed/scripts/partial_tempering.sh +319 -0
  447. plumed/_lib/lib/plumed/scripts/patch.sh +4 -0
  448. plumed/_lib/lib/plumed/scripts/selector.sh +234 -0
  449. plumed/_lib/lib/plumed/scripts/vim2html.sh +190 -0
  450. plumed/_lib/lib/plumed/src/colvar/Template.cpp +116 -0
  451. plumed/_lib/lib/plumed/src/config/compile_options.sh +3 -0
  452. plumed/_lib/lib/plumed/src/config/config.txt +181 -0
  453. plumed/_lib/lib/plumed/src/lib/Plumed.cmake +6 -0
  454. plumed/_lib/lib/plumed/src/lib/Plumed.cmake.runtime +5 -0
  455. plumed/_lib/lib/plumed/src/lib/Plumed.cmake.shared +5 -0
  456. plumed/_lib/lib/plumed/src/lib/Plumed.cmake.static +3 -0
  457. plumed/_lib/lib/plumed/src/lib/Plumed.inc +6 -0
  458. plumed/_lib/lib/plumed/src/lib/Plumed.inc.runtime +5 -0
  459. plumed/_lib/lib/plumed/src/lib/Plumed.inc.shared +5 -0
  460. plumed/_lib/lib/plumed/src/lib/Plumed.inc.static +3 -0
  461. plumed/_lib/lib/plumed/vim/scripts.vim +6 -0
  462. plumed/_plumed_core.cpython-311-darwin.so +0 -0
  463. plumed/_plumed_core.cpython-312-darwin.so +0 -0
  464. plumed/_plumed_core.cpython-313-darwin.so +0 -0
  465. plumedCommunications.cpython-311-darwin.so +0 -0
  466. plumedCommunications.cpython-312-darwin.so +0 -0
  467. plumedCommunications.cpython-313-darwin.so +0 -0
  468. plumedCommunications.pyi +431 -0
hillclimber/cvs.py ADDED
@@ -0,0 +1,1070 @@
1
+ # --- IMPORTS ---
2
+ # Standard library
3
+ from __future__ import annotations
4
+
5
+ import dataclasses
6
+ from dataclasses import dataclass
7
+ from typing import Dict, List, Literal, Optional, Tuple, Union
8
+
9
+ # Third-party
10
+ import molify
11
+ from ase import Atoms
12
+ from PIL import Image
13
+ from rdkit import Chem
14
+ from rdkit.Chem import Draw
15
+
16
+ # Local
17
+ from hillclimber.interfaces import AtomSelector, CollectiveVariable
18
+ from hillclimber.virtual_atoms import VirtualAtom
19
+
20
+ # --- TYPE HINTS ---
21
+ GroupReductionStrategyType = Literal[
22
+ "com", "cog", "first", "all", "com_per_group", "cog_per_group"
23
+ ]
24
+ SiteIdentifier = Union[str, List[int]]
25
+ ColorTuple = Tuple[float, float, float]
26
+ AtomHighlightMap = Dict[int, ColorTuple]
27
+
28
+
29
+ # --- BASE CLASS FOR SHARED LOGIC ---
30
+ class _BasePlumedCV(CollectiveVariable):
31
+ """An abstract base class for PLUMED CVs providing shared utilities."""
32
+
33
+ prefix: str
34
+
35
+ def _get_atom_highlights(
36
+ self, atoms: Atoms, **kwargs
37
+ ) -> Optional[AtomHighlightMap]:
38
+ """
39
+ Get atom indices and colors for visualization.
40
+
41
+ This abstract method must be implemented by subclasses to define which atoms
42
+ to highlight and with which colors.
43
+
44
+ Args:
45
+ atoms: The ASE Atoms object.
46
+ **kwargs: Additional keyword arguments for specific implementations.
47
+
48
+ Returns:
49
+ A dictionary mapping global atom indices to their RGB highlight color,
50
+ or None if selection fails.
51
+ """
52
+ raise NotImplementedError
53
+
54
+ def get_img(self, atoms: Atoms, **kwargs) -> Image.Image:
55
+ """
56
+ Generates an image of the molecule(s) with selected atoms highlighted.
57
+
58
+ This method uses RDKit to render the image. It automatically identifies
59
+ molecular fragments containing highlighted atoms and draws them in a row.
60
+
61
+ Args:
62
+ atoms: The ASE Atoms object to visualize.
63
+ **kwargs: Additional arguments passed to _get_atom_highlights.
64
+
65
+ Returns:
66
+ A PIL Image object of the visualization.
67
+ """
68
+ highlight_map = self._get_atom_highlights(atoms, **kwargs)
69
+ try:
70
+ mol = molify.ase2rdkit(atoms)
71
+ except ValueError:
72
+ # Bond determination failed (e.g., jittered positions without connectivity)
73
+ # Return a placeholder image
74
+ return Image.new("RGB", (400, 400), color=(255, 255, 255))
75
+
76
+ if not highlight_map:
77
+ return Draw.MolsToGridImage(
78
+ [mol],
79
+ molsPerRow=1,
80
+ subImgSize=(400, 400),
81
+ useSVG=False,
82
+ )
83
+
84
+ mol_frags = Chem.GetMolFrags(mol, asMols=True)
85
+ frag_indices_list = Chem.GetMolFrags(mol, asMols=False)
86
+
87
+ mols_to_draw, highlights_to_draw, colors_to_draw = [], [], []
88
+ seen_molecules = set()
89
+
90
+ for frag_mol, frag_indices in zip(mol_frags, frag_indices_list):
91
+ local_idx_map = {
92
+ global_idx: local_idx
93
+ for local_idx, global_idx in enumerate(frag_indices)
94
+ }
95
+ current_highlights = {
96
+ local_idx_map[g_idx]: color
97
+ for g_idx, color in highlight_map.items()
98
+ if g_idx in local_idx_map
99
+ }
100
+
101
+ if current_highlights:
102
+ # Create unique identifier: canonical SMILES + highlighted local indices
103
+ canonical_smiles = Chem.MolToSmiles(frag_mol)
104
+ highlighted_local_indices = tuple(sorted(current_highlights.keys()))
105
+ molecule_signature = (canonical_smiles, highlighted_local_indices)
106
+
107
+ if molecule_signature not in seen_molecules:
108
+ seen_molecules.add(molecule_signature)
109
+ mols_to_draw.append(frag_mol)
110
+ highlights_to_draw.append(list(current_highlights.keys()))
111
+ colors_to_draw.append(current_highlights)
112
+
113
+ if not mols_to_draw:
114
+ return Draw.MolsToGridImage(
115
+ [mol],
116
+ molsPerRow=1,
117
+ subImgSize=(400, 400),
118
+ useSVG=False,
119
+ )
120
+
121
+ return Draw.MolsToGridImage(
122
+ mols_to_draw,
123
+ molsPerRow=len(mols_to_draw),
124
+ subImgSize=(400, 400),
125
+ highlightAtomLists=highlights_to_draw,
126
+ highlightAtomColors=colors_to_draw,
127
+ useSVG=False,
128
+ )
129
+
130
+ @staticmethod
131
+ def _extract_labels(commands: List[str], prefix: str, cv_keyword: str) -> List[str]:
132
+ """Extracts generated CV labels from a list of PLUMED commands."""
133
+ return [
134
+ cmd.split(":", 1)[0].strip()
135
+ for cmd in commands
136
+ if cv_keyword in cmd and cmd.strip().startswith((prefix, f"{prefix}_"))
137
+ ]
138
+
139
+ @staticmethod
140
+ def _create_virtual_site_command(
141
+ group: List[int], strategy: Literal["com", "cog"], label: str
142
+ ) -> str:
143
+ """Creates a PLUMED command for a COM or CENTER virtual site."""
144
+ if not group:
145
+ raise ValueError("Cannot create a virtual site for an empty group.")
146
+ atom_list = ",".join(str(idx + 1) for idx in group)
147
+ cmd_keyword = "COM" if strategy == "com" else "CENTER"
148
+ return f"{label}: {cmd_keyword} ATOMS={atom_list}"
149
+
150
+
151
+ # --- REFACTORED CV CLASSES ---
152
+ @dataclass
153
+ class DistanceCV(_BasePlumedCV):
154
+ """
155
+ PLUMED DISTANCE collective variable.
156
+
157
+ Calculates the distance between two atoms, groups of atoms, or virtual sites.
158
+ Supports flexible flattening and pairing strategies for multiple groups.
159
+
160
+ Parameters
161
+ ----------
162
+ x1 : AtomSelector | VirtualAtom
163
+ First atom/group or virtual site.
164
+ x2 : AtomSelector | VirtualAtom
165
+ Second atom/group or virtual site.
166
+ prefix : str
167
+ Label prefix for generated PLUMED commands.
168
+ flatten : bool, default=True
169
+ For AtomSelectors only: If True, flatten all groups into single atom list.
170
+ If False, create PLUMED GROUP for each group. VirtualAtoms are never flattened.
171
+ pairwise : {"all", "diagonal", "none"}, default="all"
172
+ Strategy for pairing multiple groups:
173
+ - "all": Create all N×M pair combinations (can create many CVs!)
174
+ - "diagonal": Pair corresponding indices only (creates min(N,M) CVs)
175
+ - "none": Error if both sides have multiple groups (safety check)
176
+
177
+ Examples
178
+ --------
179
+ >>> # Distance between two specific atoms
180
+ >>> dist = hc.DistanceCV(
181
+ ... x1=ethanol_sel[0][0], # First atom of first ethanol
182
+ ... x2=water_sel[0][0], # First atom of first water
183
+ ... prefix="d_atoms"
184
+ ... )
185
+
186
+ >>> # Distance between molecule COMs
187
+ >>> dist = hc.DistanceCV(
188
+ ... x1=hc.VirtualAtom(ethanol_sel[0], "com"),
189
+ ... x2=hc.VirtualAtom(water_sel[0], "com"),
190
+ ... prefix="d_com"
191
+ ... )
192
+
193
+ >>> # One-to-many: First ethanol COM to all water COMs
194
+ >>> dist = hc.DistanceCV(
195
+ ... x1=hc.VirtualAtom(ethanol_sel[0], "com"),
196
+ ... x2=hc.VirtualAtom(water_sel, "com"),
197
+ ... prefix="d",
198
+ ... pairwise="all" # Creates 3 CVs
199
+ ... )
200
+
201
+ >>> # Diagonal pairing (avoid explosion)
202
+ >>> dist = hc.DistanceCV(
203
+ ... x1=hc.VirtualAtom(water_sel, "com"), # 3 waters
204
+ ... x2=hc.VirtualAtom(ethanol_sel, "com"), # 2 ethanols
205
+ ... prefix="d",
206
+ ... pairwise="diagonal" # Creates only 2 CVs: d_0, d_1
207
+ ... )
208
+
209
+ Resources
210
+ ---------
211
+ - https://www.plumed.org/doc-master/user-doc/html/DISTANCE.html
212
+
213
+ Notes
214
+ -----
215
+ For backwards compatibility, old parameters are still supported but deprecated:
216
+ - `group_reduction` → Use VirtualAtom instead
217
+ - `multi_group` → Use `pairwise` parameter
218
+ """
219
+
220
+ x1: AtomSelector | VirtualAtom
221
+ x2: AtomSelector | VirtualAtom
222
+ prefix: str
223
+ flatten: bool = True
224
+ pairwise: Literal["all", "diagonal", "none"] = "all"
225
+
226
+ def _get_atom_highlights(
227
+ self, atoms: Atoms, **kwargs
228
+ ) -> Optional[AtomHighlightMap]:
229
+ """Get atom highlights for visualization."""
230
+ # Skip for VirtualAtom inputs
231
+ if isinstance(self.x1, VirtualAtom) or isinstance(self.x2, VirtualAtom):
232
+ return None
233
+
234
+ groups1 = self.x1.select(atoms)
235
+ groups2 = self.x2.select(atoms)
236
+
237
+ if not groups1 or not groups2:
238
+ return None
239
+
240
+ # Highlight all atoms from both selections
241
+ indices1 = {idx for group in groups1 for idx in group}
242
+ indices2 = {idx for group in groups2 for idx in group}
243
+
244
+ if not indices1 and not indices2:
245
+ return None
246
+
247
+ # Color atoms based on group membership
248
+ highlights: AtomHighlightMap = {}
249
+ red, blue, purple = (1.0, 0.2, 0.2), (0.2, 0.2, 1.0), (1.0, 0.2, 1.0)
250
+ for idx in indices1.union(indices2):
251
+ in1, in2 = idx in indices1, idx in indices2
252
+ if in1 and in2:
253
+ highlights[idx] = purple
254
+ elif in1:
255
+ highlights[idx] = red
256
+ elif in2:
257
+ highlights[idx] = blue
258
+ return highlights
259
+
260
+ def to_plumed(self, atoms: Atoms) -> Tuple[List[str], List[str]]:
261
+ """Generate PLUMED input strings for the DISTANCE CV.
262
+
263
+ Returns
264
+ -------
265
+ labels : list[str]
266
+ List of CV labels generated.
267
+ commands : list[str]
268
+ List of PLUMED command strings.
269
+ """
270
+ commands = []
271
+
272
+ # Process x1
273
+ labels1, cmds1 = self._process_input(self.x1, atoms, "x1")
274
+ commands.extend(cmds1)
275
+
276
+ # Process x2
277
+ labels2, cmds2 = self._process_input(self.x2, atoms, "x2")
278
+ commands.extend(cmds2)
279
+
280
+ # Check for empty selections
281
+ if not labels1 or not labels2:
282
+ raise ValueError(f"Empty selection for distance CV '{self.prefix}'")
283
+
284
+ # Generate distance CVs based on pairwise strategy
285
+ cv_labels, cv_commands = self._generate_distance_cvs(labels1, labels2)
286
+ commands.extend(cv_commands)
287
+
288
+ return cv_labels, commands
289
+
290
+ def _process_input(
291
+ self, input_obj: AtomSelector | VirtualAtom, atoms: Atoms, label_prefix: str
292
+ ) -> Tuple[List[str], List[str]]:
293
+ """Process an input (AtomSelector or VirtualAtom) and return labels and commands.
294
+
295
+ Returns
296
+ -------
297
+ labels : list[str]
298
+ List of labels for this input (either virtual site labels or GROUP labels).
299
+ commands : list[str]
300
+ PLUMED commands to create the labels.
301
+ """
302
+ if isinstance(input_obj, VirtualAtom):
303
+ # VirtualAtom: set deterministic label if not already set
304
+ if input_obj.label is None:
305
+ # Set label based on prefix and label_prefix (x1 or x2)
306
+ labeled_va = dataclasses.replace(
307
+ input_obj, label=f"{self.prefix}_{label_prefix}"
308
+ )
309
+ return labeled_va.to_plumed(atoms)
310
+ else:
311
+ return input_obj.to_plumed(atoms)
312
+ else:
313
+ # AtomSelector: handle based on flatten parameter
314
+ groups = input_obj.select(atoms)
315
+ if not groups:
316
+ return [], []
317
+
318
+ if self.flatten:
319
+ # Flatten all groups into single list
320
+ flat_atoms = [idx for group in groups for idx in group]
321
+ atom_list = ",".join(str(idx + 1) for idx in flat_atoms)
322
+ # Return as pseudo-label (will be used directly in DISTANCE command)
323
+ return [atom_list], []
324
+ else:
325
+ # Smart GROUP creation: only create GROUP for multi-atom groups
326
+ labels = []
327
+ commands = []
328
+ for i, group in enumerate(groups):
329
+ if len(group) == 1:
330
+ # Single atom: use directly (no GROUP needed)
331
+ labels.append(str(group[0] + 1))
332
+ else:
333
+ # Multi-atom group: create GROUP
334
+ group_label = f"{self.prefix}_{label_prefix}_g{i}"
335
+ atom_list = ",".join(str(idx + 1) for idx in group)
336
+ commands.append(f"{group_label}: GROUP ATOMS={atom_list}")
337
+ labels.append(group_label)
338
+ return labels, commands
339
+
340
+ def _generate_distance_cvs(
341
+ self, labels1: List[str], labels2: List[str]
342
+ ) -> Tuple[List[str], List[str]]:
343
+ """Generate DISTANCE CV commands based on pairwise strategy."""
344
+ n1, n2 = len(labels1), len(labels2)
345
+
346
+ # Determine which pairs to create based on pairwise strategy
347
+ if n1 == 1 and n2 == 1:
348
+ # One-to-one: always create single CV
349
+ pairs = [(0, 0)]
350
+ elif n1 == 1:
351
+ # One-to-many: pair first of x1 with all of x2
352
+ pairs = [(0, j) for j in range(n2)]
353
+ elif n2 == 1:
354
+ # Many-to-one: pair all of x1 with first of x2
355
+ pairs = [(i, 0) for i in range(n1)]
356
+ else:
357
+ # Many-to-many: apply pairwise strategy
358
+ if self.pairwise == "all":
359
+ pairs = [(i, j) for i in range(n1) for j in range(n2)]
360
+ elif self.pairwise == "diagonal":
361
+ n_pairs = min(n1, n2)
362
+ pairs = [(i, i) for i in range(n_pairs)]
363
+ elif self.pairwise == "none":
364
+ raise ValueError(
365
+ f"Both x1 and x2 have multiple groups ({n1} and {n2}). "
366
+ f"Use pairwise='all' or 'diagonal', or select specific groups with indexing."
367
+ )
368
+ else:
369
+ raise ValueError(f"Unknown pairwise strategy: {self.pairwise}")
370
+
371
+ # Generate DISTANCE commands
372
+ cv_labels = []
373
+ commands = []
374
+ for idx, (i, j) in enumerate(pairs):
375
+ if len(pairs) == 1:
376
+ label = self.prefix
377
+ else:
378
+ label = f"{self.prefix}_{idx}"
379
+
380
+ # Create DISTANCE command
381
+ cmd = f"{label}: DISTANCE ATOMS={labels1[i]},{labels2[j]}"
382
+ commands.append(cmd)
383
+ cv_labels.append(label)
384
+
385
+ return cv_labels, commands
386
+
387
+
388
+ @dataclass
389
+ class AngleCV(_BasePlumedCV):
390
+ """
391
+ PLUMED ANGLE collective variable.
392
+
393
+ Calculates the angle formed by three atoms or groups of atoms using the new
394
+ VirtualAtom API. The angle is computed as the angle between the vectors
395
+ (x1-x2) and (x3-x2), where x2 is the vertex of the angle.
396
+
397
+ Parameters
398
+ ----------
399
+ x1 : AtomSelector | VirtualAtom
400
+ First position. Can be an AtomSelector or VirtualAtom.
401
+ x2 : AtomSelector | VirtualAtom
402
+ Vertex position (center of the angle). Can be an AtomSelector or VirtualAtom.
403
+ x3 : AtomSelector | VirtualAtom
404
+ Third position. Can be an AtomSelector or VirtualAtom.
405
+ prefix : str
406
+ Label prefix for the generated PLUMED commands.
407
+ flatten : bool, default=True
408
+ How to handle AtomSelector inputs:
409
+ - True: Flatten all groups into a single list
410
+ - False: Create GROUP for each selector group (not typically used for ANGLE)
411
+ strategy : {"first", "all", "diagonal", "none"}, default="first"
412
+ Strategy for creating multiple angles from multiple groups:
413
+ - "first": Use first group from each selector (1 angle)
414
+ - "all": All combinations (N×M×P angles)
415
+ - "diagonal": Pair by index (min(N,M,P) angles)
416
+ - "none": Raise error if any selector has multiple groups
417
+
418
+ Resources
419
+ ---------
420
+ - https://www.plumed.org/doc-master/user-doc/html/ANGLE/
421
+ """
422
+
423
+ x1: AtomSelector | VirtualAtom
424
+ x2: AtomSelector | VirtualAtom
425
+ x3: AtomSelector | VirtualAtom
426
+ prefix: str
427
+ flatten: bool = True
428
+ strategy: Literal["first", "all", "diagonal", "none"] = "first"
429
+
430
+ def _get_atom_highlights(
431
+ self, atoms: Atoms, **kwargs
432
+ ) -> Optional[AtomHighlightMap]:
433
+ """Get atom highlights for visualization."""
434
+ # Skip for VirtualAtom inputs
435
+ if (
436
+ isinstance(self.x1, VirtualAtom)
437
+ or isinstance(self.x2, VirtualAtom)
438
+ or isinstance(self.x3, VirtualAtom)
439
+ ):
440
+ return None
441
+
442
+ groups1 = self.x1.select(atoms)
443
+ groups2 = self.x2.select(atoms)
444
+ groups3 = self.x3.select(atoms)
445
+
446
+ if not groups1 or not groups2 or not groups3:
447
+ return None
448
+
449
+ # Highlight all atoms from all three selections
450
+ indices1 = {idx for group in groups1 for idx in group}
451
+ indices2 = {idx for group in groups2 for idx in group}
452
+ indices3 = {idx for group in groups3 for idx in group}
453
+
454
+ if not indices1 and not indices2 and not indices3:
455
+ return None
456
+
457
+ # Color atoms: red for x1, green for x2 (vertex), blue for x3
458
+ highlights: AtomHighlightMap = {}
459
+ red, green, blue = (1.0, 0.2, 0.2), (0.2, 1.0, 0.2), (0.2, 0.2, 1.0)
460
+
461
+ # Handle overlaps by prioritizing vertex (x2) coloring
462
+ all_indices = indices1.union(indices2).union(indices3)
463
+ for idx in all_indices:
464
+ in1, in2, in3 = idx in indices1, idx in indices2, idx in indices3
465
+ if in2: # Vertex gets priority
466
+ highlights[idx] = green
467
+ elif in1 and in3: # Overlap between x1 and x3
468
+ highlights[idx] = (0.5, 0.2, 0.6) # Purple
469
+ elif in1:
470
+ highlights[idx] = red
471
+ elif in3:
472
+ highlights[idx] = blue
473
+ return highlights
474
+
475
+ def to_plumed(self, atoms: Atoms) -> Tuple[List[str], List[str]]:
476
+ """Generate PLUMED ANGLE command(s).
477
+
478
+ Returns
479
+ -------
480
+ labels : list[str]
481
+ List of CV labels created.
482
+ commands : list[str]
483
+ List of PLUMED commands.
484
+
485
+ Raises
486
+ ------
487
+ ValueError
488
+ If any selector returns empty selection.
489
+ """
490
+ # Process all three inputs
491
+ labels1, cmds1 = self._process_input(self.x1, atoms, "x1")
492
+ labels2, cmds2 = self._process_input(self.x2, atoms, "x2")
493
+ labels3, cmds3 = self._process_input(self.x3, atoms, "x3")
494
+
495
+ # Check for empty selections
496
+ if not labels1 or not labels2 or not labels3:
497
+ raise ValueError(f"Empty selection for angle CV '{self.prefix}'")
498
+
499
+ commands = []
500
+ commands.extend(cmds1)
501
+ commands.extend(cmds2)
502
+ commands.extend(cmds3)
503
+
504
+ # Generate ANGLE commands
505
+ cv_labels, cv_commands = self._generate_angle_cvs(labels1, labels2, labels3)
506
+ commands.extend(cv_commands)
507
+
508
+ return cv_labels, commands
509
+
510
+ def _process_input(
511
+ self, input_obj: AtomSelector | VirtualAtom, atoms: Atoms, label_prefix: str
512
+ ) -> Tuple[List[str], List[str]]:
513
+ """Process input (AtomSelector or VirtualAtom) and return labels and commands.
514
+
515
+ Same as DistanceCV._process_input() method.
516
+
517
+ Returns
518
+ -------
519
+ labels : list[str]
520
+ List of labels for this input (either virtual site labels or atom lists).
521
+ commands : list[str]
522
+ PLUMED commands to create the labels.
523
+ """
524
+ if isinstance(input_obj, VirtualAtom):
525
+ # VirtualAtom: set deterministic label if not already set
526
+ if input_obj.label is None:
527
+ labeled_va = dataclasses.replace(
528
+ input_obj, label=f"{self.prefix}_{label_prefix}"
529
+ )
530
+ return labeled_va.to_plumed(atoms)
531
+ else:
532
+ return input_obj.to_plumed(atoms)
533
+ else:
534
+ # AtomSelector: handle based on flatten parameter
535
+ groups = input_obj.select(atoms)
536
+ if not groups:
537
+ return [], []
538
+
539
+ if self.flatten:
540
+ # Flatten all groups into single list
541
+ flat_atoms = [idx for group in groups for idx in group]
542
+ atom_list = ",".join(str(idx + 1) for idx in flat_atoms)
543
+ # Return as pseudo-label (will be used directly in ANGLE command)
544
+ return [atom_list], []
545
+ else:
546
+ # Smart GROUP creation: only create GROUP for multi-atom groups
547
+ labels = []
548
+ commands = []
549
+ for i, group in enumerate(groups):
550
+ if len(group) == 1:
551
+ # Single atom: use directly (no GROUP needed)
552
+ labels.append(str(group[0] + 1))
553
+ else:
554
+ # Multi-atom group: create GROUP
555
+ group_label = f"{self.prefix}_{label_prefix}_g{i}"
556
+ atom_list = ",".join(str(idx + 1) for idx in group)
557
+ commands.append(f"{group_label}: GROUP ATOMS={atom_list}")
558
+ labels.append(group_label)
559
+ return labels, commands
560
+
561
+ def _generate_angle_cvs(
562
+ self, labels1: List[str], labels2: List[str], labels3: List[str]
563
+ ) -> Tuple[List[str], List[str]]:
564
+ """Generate ANGLE CV commands based on strategy.
565
+
566
+ Parameters
567
+ ----------
568
+ labels1, labels2, labels3 : list[str]
569
+ Labels or atom lists for the three angle positions.
570
+
571
+ Returns
572
+ -------
573
+ cv_labels : list[str]
574
+ Labels for the ANGLE CVs created.
575
+ commands : list[str]
576
+ ANGLE command strings.
577
+ """
578
+ n1, n2, n3 = len(labels1), len(labels2), len(labels3)
579
+
580
+ # Determine which triplets to create based on strategy
581
+ if n1 == 1 and n2 == 1 and n3 == 1:
582
+ # One-to-one-to-one: always create single CV
583
+ triplets = [(0, 0, 0)]
584
+ elif n1 == 1 and n2 == 1:
585
+ # One-one-to-many: pair first of x1/x2 with all of x3
586
+ triplets = [(0, 0, k) for k in range(n3)]
587
+ elif n1 == 1 and n3 == 1:
588
+ # One-many-to-one: pair first of x1/x3 with all of x2
589
+ triplets = [(0, j, 0) for j in range(n2)]
590
+ elif n2 == 1 and n3 == 1:
591
+ # Many-to-one-one: pair all of x1 with first of x2/x3
592
+ triplets = [(i, 0, 0) for i in range(n1)]
593
+ else:
594
+ # Multi-way: apply strategy
595
+ if self.strategy == "first":
596
+ triplets = [(0, 0, 0)] if n1 > 0 and n2 > 0 and n3 > 0 else []
597
+ elif self.strategy == "all":
598
+ triplets = [
599
+ (i, j, k) for i in range(n1) for j in range(n2) for k in range(n3)
600
+ ]
601
+ elif self.strategy == "diagonal":
602
+ n_triplets = min(n1, n2, n3)
603
+ triplets = [(i, i, i) for i in range(n_triplets)]
604
+ elif self.strategy == "none":
605
+ raise ValueError(
606
+ f"Multiple groups in x1/x2/x3 ({n1}, {n2}, {n3}). "
607
+ f"Use strategy='all' or 'diagonal', or select specific groups with indexing."
608
+ )
609
+ else:
610
+ raise ValueError(f"Unknown strategy: {self.strategy}")
611
+
612
+ # Generate ANGLE commands
613
+ cv_labels = []
614
+ commands = []
615
+ for idx, (i, j, k) in enumerate(triplets):
616
+ if len(triplets) == 1:
617
+ label = self.prefix
618
+ else:
619
+ label = f"{self.prefix}_{i}_{j}_{k}"
620
+
621
+ # Create ANGLE command (ATOMS=x1,x2,x3 where x2 is vertex)
622
+ cmd = f"{label}: ANGLE ATOMS={labels1[i]},{labels2[j]},{labels3[k]}"
623
+ commands.append(cmd)
624
+ cv_labels.append(label)
625
+
626
+ return cv_labels, commands
627
+
628
+
629
+ @dataclass
630
+ class CoordinationNumberCV(_BasePlumedCV):
631
+ """
632
+ PLUMED COORDINATION collective variable.
633
+
634
+ Calculates a coordination number based on a switching function using the new
635
+ VirtualAtom API. The coordination number is computed between two groups of atoms
636
+ using a switching function.
637
+
638
+ Parameters
639
+ ----------
640
+ x1 : AtomSelector | VirtualAtom
641
+ First group of atoms. Can be an AtomSelector or VirtualAtom.
642
+ x2 : AtomSelector | VirtualAtom
643
+ Second group of atoms. Can be an AtomSelector or VirtualAtom.
644
+ prefix : str
645
+ Label prefix for the generated PLUMED commands.
646
+ r_0 : float
647
+ Reference distance for the switching function (in Angstroms).
648
+ nn : int, default=6
649
+ Exponent for the switching function numerator.
650
+ mm : int, default=0
651
+ Exponent for the switching function denominator.
652
+ d_0 : float, default=0.0
653
+ Offset for the switching function (in Angstroms).
654
+ flatten : bool, default=True
655
+ How to handle AtomSelector inputs:
656
+ - True: Flatten all groups into a single GROUP
657
+ - False: Create a GROUP for each selector group
658
+ pairwise : {"all", "diagonal", "none"}, default="all"
659
+ Strategy for pairing multiple groups:
660
+ - "all": All pairwise combinations (N×M CVs)
661
+ - "diagonal": Pair by index (min(N,M) CVs)
662
+ - "none": Raise error if both have multiple groups
663
+
664
+ Resources
665
+ ---------
666
+ - https://www.plumed.org/doc-master/user-doc/html/COORDINATION
667
+ - https://www.plumed.org/doc-master/user-doc/html/GROUP
668
+ """
669
+
670
+ x1: AtomSelector | VirtualAtom
671
+ x2: AtomSelector | VirtualAtom
672
+ prefix: str
673
+ r_0: float
674
+ nn: int = 6
675
+ mm: int = 0
676
+ d_0: float = 0.0
677
+ flatten: bool = True
678
+ pairwise: Literal["all", "diagonal", "none"] = "all"
679
+
680
+ def _get_atom_highlights(
681
+ self, atoms: Atoms, **kwargs
682
+ ) -> Optional[AtomHighlightMap]:
683
+ """Get atom highlights for visualization."""
684
+ # Skip for VirtualAtom inputs
685
+ if isinstance(self.x1, VirtualAtom) or isinstance(self.x2, VirtualAtom):
686
+ return None
687
+
688
+ groups1 = self.x1.select(atoms)
689
+ groups2 = self.x2.select(atoms)
690
+
691
+ if not groups1 or not groups2:
692
+ return None
693
+
694
+ # Highlight all atoms from both selections
695
+ indices1 = {idx for group in groups1 for idx in group}
696
+ indices2 = {idx for group in groups2 for idx in group}
697
+
698
+ if not indices1 and not indices2:
699
+ return None
700
+
701
+ # Color atoms based on group membership
702
+ highlights: AtomHighlightMap = {}
703
+ red, blue, purple = (1.0, 0.2, 0.2), (0.2, 0.2, 1.0), (1.0, 0.2, 1.0)
704
+ for idx in indices1.union(indices2):
705
+ in1, in2 = idx in indices1, idx in indices2
706
+ if in1 and in2:
707
+ highlights[idx] = purple
708
+ elif in1:
709
+ highlights[idx] = red
710
+ elif in2:
711
+ highlights[idx] = blue
712
+ return highlights
713
+
714
+ def to_plumed(self, atoms: Atoms) -> Tuple[List[str], List[str]]:
715
+ """Generate PLUMED COORDINATION command(s).
716
+
717
+ Returns
718
+ -------
719
+ labels : list[str]
720
+ List of CV labels created.
721
+ commands : list[str]
722
+ List of PLUMED commands.
723
+ """
724
+ # Process both inputs to get group labels
725
+ labels1, cmds1 = self._process_coordination_input(self.x1, atoms, "x1")
726
+ labels2, cmds2 = self._process_coordination_input(self.x2, atoms, "x2")
727
+
728
+ commands = []
729
+ commands.extend(cmds1)
730
+ commands.extend(cmds2)
731
+
732
+ # Generate COORDINATION commands
733
+ cv_labels, cv_commands = self._generate_coordination_cvs(labels1, labels2)
734
+ commands.extend(cv_commands)
735
+
736
+ return cv_labels, commands
737
+
738
+ def _process_coordination_input(
739
+ self, input_obj: AtomSelector | VirtualAtom, atoms: Atoms, label_prefix: str
740
+ ) -> Tuple[List[str], List[str]]:
741
+ """Process input for COORDINATION and return group labels/commands.
742
+
743
+ For COORDINATION, we need groups (not individual points), so the processing
744
+ is different from DistanceCV:
745
+ - VirtualAtom with multiple sites → create GROUP of those sites
746
+ - VirtualAtom with single site → use site directly
747
+ - AtomSelector with flatten=True → create single group with all atoms
748
+ - AtomSelector with flatten=False → create GROUP for each selector group
749
+
750
+ Returns
751
+ -------
752
+ labels : list[str]
753
+ Group labels that can be used in COORDINATION GROUPA/GROUPB.
754
+ commands : list[str]
755
+ PLUMED commands to create those groups.
756
+ """
757
+ if isinstance(input_obj, VirtualAtom):
758
+ # Set deterministic label if not already set
759
+ if input_obj.label is None:
760
+ labeled_va = dataclasses.replace(
761
+ input_obj, label=f"{self.prefix}_{label_prefix}"
762
+ )
763
+ else:
764
+ labeled_va = input_obj
765
+
766
+ # Get virtual site labels
767
+ vsite_labels, vsite_commands = labeled_va.to_plumed(atoms)
768
+
769
+ # If multiple virtual sites, create a GROUP of them
770
+ if len(vsite_labels) > 1:
771
+ group_label = f"{self.prefix}_{label_prefix}_group"
772
+ group_cmd = f"{group_label}: GROUP ATOMS={','.join(vsite_labels)}"
773
+ return [group_label], vsite_commands + [group_cmd]
774
+ else:
775
+ # Single virtual site, use directly
776
+ return vsite_labels, vsite_commands
777
+ else:
778
+ # AtomSelector: create group(s) based on flatten parameter
779
+ groups = input_obj.select(atoms)
780
+ if not groups:
781
+ return [], []
782
+
783
+ if self.flatten:
784
+ # Flatten all groups into single group
785
+ flat_atoms = [idx for group in groups for idx in group]
786
+ # Return as list of atom indices (will be formatted in COORDINATION command)
787
+ return [flat_atoms], []
788
+ else:
789
+ # Smart GROUP creation: only create GROUP for multi-atom groups
790
+ labels = []
791
+ commands = []
792
+ for i, group in enumerate(groups):
793
+ if len(group) == 1:
794
+ # Single atom: use directly (no GROUP needed)
795
+ labels.append(str(group[0] + 1))
796
+ else:
797
+ # Multi-atom group: create GROUP
798
+ group_label = f"{self.prefix}_{label_prefix}_g{i}"
799
+ atom_list = ",".join(str(idx + 1) for idx in group)
800
+ commands.append(f"{group_label}: GROUP ATOMS={atom_list}")
801
+ labels.append(group_label)
802
+
803
+ # If multiple groups, create a parent GROUP
804
+ if len(labels) > 1:
805
+ parent_label = f"{self.prefix}_{label_prefix}_group"
806
+ parent_cmd = f"{parent_label}: GROUP ATOMS={','.join(labels)}"
807
+ return [parent_label], commands + [parent_cmd]
808
+ else:
809
+ return labels, commands
810
+
811
+ def _generate_coordination_cvs(
812
+ self, labels1: List[str | List[int]], labels2: List[str | List[int]]
813
+ ) -> Tuple[List[str], List[str]]:
814
+ """Generate COORDINATION CV commands.
815
+
816
+ Parameters
817
+ ----------
818
+ labels1, labels2 : list[str | list[int]]
819
+ Group labels or atom index lists for GROUPA and GROUPB.
820
+
821
+ Returns
822
+ -------
823
+ cv_labels : list[str]
824
+ Labels for the COORDINATION CVs created.
825
+ commands : list[str]
826
+ COORDINATION command strings.
827
+ """
828
+ n1, n2 = len(labels1), len(labels2)
829
+
830
+ # Determine which pairs to create based on pairwise strategy
831
+ if n1 == 1 and n2 == 1:
832
+ # One-to-one: always create single CV
833
+ pairs = [(0, 0)]
834
+ elif n1 == 1:
835
+ # One-to-many: pair first of x1 with all of x2
836
+ pairs = [(0, j) for j in range(n2)]
837
+ elif n2 == 1:
838
+ # Many-to-one: pair all of x1 with first of x2
839
+ pairs = [(i, 0) for i in range(n1)]
840
+ else:
841
+ # Many-to-many: apply pairwise strategy
842
+ if self.pairwise == "all":
843
+ pairs = [(i, j) for i in range(n1) for j in range(n2)]
844
+ elif self.pairwise == "diagonal":
845
+ n_pairs = min(n1, n2)
846
+ pairs = [(i, i) for i in range(n_pairs)]
847
+ elif self.pairwise == "none":
848
+ raise ValueError(
849
+ f"Both x1 and x2 have multiple groups ({n1} and {n2}). "
850
+ f"Use pairwise='all' or 'diagonal', or select specific groups with indexing."
851
+ )
852
+ else:
853
+ raise ValueError(f"Unknown pairwise strategy: {self.pairwise}")
854
+
855
+ # Generate COORDINATION commands
856
+ cv_labels = []
857
+ commands = []
858
+ for idx, (i, j) in enumerate(pairs):
859
+ if len(pairs) == 1:
860
+ label = self.prefix
861
+ else:
862
+ label = f"{self.prefix}_{idx}"
863
+
864
+ # Format group labels for COORDINATION
865
+ def format_group(g):
866
+ if isinstance(g, list): # List of atom indices
867
+ return ",".join(str(idx + 1) for idx in g)
868
+ else: # String label
869
+ return g
870
+
871
+ g_a = format_group(labels1[i])
872
+ g_b = format_group(labels2[j])
873
+
874
+ # Create COORDINATION command
875
+ cmd = f"{label}: COORDINATION GROUPA={g_a}"
876
+ if g_a != g_b: # Omit GROUPB for self-coordination
877
+ cmd += f" GROUPB={g_b}"
878
+
879
+ # Add parameters
880
+ cmd += f" R_0={self.r_0} NN={self.nn} D_0={self.d_0}"
881
+ if self.mm != 0:
882
+ cmd += f" MM={self.mm}"
883
+
884
+ commands.append(cmd)
885
+ cv_labels.append(label)
886
+
887
+ return cv_labels, commands
888
+
889
+
890
+ @dataclass
891
+ class TorsionCV(_BasePlumedCV):
892
+ """
893
+ PLUMED TORSION collective variable.
894
+
895
+ Calculates the torsional (dihedral) angle defined by four atoms. Each group
896
+ provided by the selector must contain exactly four atoms.
897
+
898
+ Parameters
899
+ ----------
900
+ atoms : AtomSelector
901
+ Selector for one or more groups of 4 atoms. Each group must contain exactly 4 atoms.
902
+ prefix : str
903
+ Label prefix for the generated PLUMED commands.
904
+ strategy : {"first", "all"}, default="first"
905
+ Strategy for handling multiple groups from the selector:
906
+ - "first": Process only the first group (creates 1 CV)
907
+ - "all": Process all groups independently (creates N CVs)
908
+
909
+ Resources
910
+ ---------
911
+ - https://www.plumed.org/doc-master/user-doc/html/TORSION
912
+ """
913
+
914
+ atoms: AtomSelector
915
+ prefix: str
916
+ strategy: Literal["first", "all"] = "first"
917
+
918
+ def _get_atom_highlights(
919
+ self, atoms: Atoms, **kwargs
920
+ ) -> Optional[AtomHighlightMap]:
921
+ groups = self.atoms.select(atoms)
922
+ if not groups or len(groups[0]) != 4:
923
+ print("Warning: Torsion CV requires a group of 4 atoms for visualization.")
924
+ return None
925
+
926
+ # Highlight the first 4-atom group with a color sequence.
927
+ torsion_atoms = groups[0]
928
+ colors = [
929
+ (1.0, 0.2, 0.2), # Red
930
+ (1.0, 0.6, 0.2), # Orange
931
+ (1.0, 1.0, 0.2), # Yellow
932
+ (0.2, 1.0, 0.2), # Green
933
+ ]
934
+ return {atom_idx: color for atom_idx, color in zip(torsion_atoms, colors)}
935
+
936
+ def to_plumed(self, atoms: Atoms) -> Tuple[List[str], List[str]]:
937
+ """
938
+ Generates PLUMED input strings for the TORSION CV.
939
+
940
+ Returns:
941
+ A tuple containing a list of CV labels and a list of PLUMED commands.
942
+ """
943
+ groups = self.atoms.select(atoms)
944
+ if not groups:
945
+ raise ValueError(f"Empty selection for torsion CV '{self.prefix}'")
946
+
947
+ for i, group in enumerate(groups):
948
+ if len(group) != 4:
949
+ raise ValueError(
950
+ f"Torsion CV requires 4 atoms per group, but group {i} has {len(group)}."
951
+ )
952
+
953
+ commands = self._generate_commands(groups)
954
+ labels = self._extract_labels(commands, self.prefix, "TORSION")
955
+ return labels, commands
956
+
957
+ def _generate_commands(self, groups: List[List[int]]) -> List[str]:
958
+ """Generates all necessary PLUMED commands."""
959
+ # Determine which groups to process based on strategy
960
+ if self.strategy == "first" and groups:
961
+ indices_to_process = [0]
962
+ else: # "all" - process all groups independently
963
+ indices_to_process = list(range(len(groups)))
964
+
965
+ commands = []
966
+ for i in indices_to_process:
967
+ label = (
968
+ self.prefix if len(indices_to_process) == 1 else f"{self.prefix}_{i}"
969
+ )
970
+ atom_list = ",".join(str(idx + 1) for idx in groups[i])
971
+ commands.append(f"{label}: TORSION ATOMS={atom_list}")
972
+ return commands
973
+
974
+
975
+ # TODO: we might need to set weights because plumed does not know about the atomistic weights?
976
+ @dataclass
977
+ class RadiusOfGyrationCV(_BasePlumedCV):
978
+ """
979
+ PLUMED GYRATION collective variable.
980
+
981
+ Calculates the radius of gyration of a group of atoms. The radius of gyration
982
+ is a measure of the size of a molecular system.
983
+
984
+ Parameters
985
+ ----------
986
+ atoms : AtomSelector
987
+ Selector for the atoms to include in the gyration calculation.
988
+ prefix : str
989
+ Label prefix for the generated PLUMED commands.
990
+ flatten : bool, default=False
991
+ How to handle multiple groups from the selector:
992
+ - True: Combine all groups into one and calculate single Rg (creates 1 CV)
993
+ - False: Keep groups separate, use strategy to determine which to process
994
+ strategy : {"first", "all"}, default="first"
995
+ Strategy for handling multiple groups when flatten=False:
996
+ - "first": Process only the first group (creates 1 CV)
997
+ - "all": Process all groups independently (creates N CVs)
998
+ type : str, default="RADIUS"
999
+ The type of gyration tensor to use.
1000
+ Options: "RADIUS", "GTPC_1", "GTPC_2", "GTPC_3", "ASPHERICITY", "ACYLINDRICITY", "KAPPA2", etc.
1001
+
1002
+ Resources
1003
+ ---------
1004
+ - https://www.plumed.org/doc-master/user-doc/html/GYRATION/
1005
+ """
1006
+
1007
+ atoms: AtomSelector
1008
+ prefix: str
1009
+ flatten: bool = False
1010
+ strategy: Literal["first", "all"] = "first"
1011
+ type: str = "RADIUS" # Options: RADIUS, GTPC_1, GTPC_2, GTPC_3, ASPHERICITY, ACYLINDRICITY, KAPPA2, etc.
1012
+
1013
+ def _get_atom_highlights(
1014
+ self, atoms: Atoms, **kwargs
1015
+ ) -> Optional[AtomHighlightMap]:
1016
+ groups = self.atoms.select(atoms)
1017
+ if not groups or not groups[0]:
1018
+ return None
1019
+
1020
+ # Highlight all atoms in the first group with a single color
1021
+ group = groups[0]
1022
+ return {atom_idx: (0.2, 0.8, 0.2) for atom_idx in group} # Green
1023
+
1024
+ def to_plumed(self, atoms: Atoms) -> Tuple[List[str], List[str]]:
1025
+ """
1026
+ Generates PLUMED input strings for the GYRATION CV.
1027
+
1028
+ Returns:
1029
+ A tuple containing a list of CV labels and a list of PLUMED commands.
1030
+ """
1031
+ groups = self.atoms.select(atoms)
1032
+ if not groups:
1033
+ raise ValueError(f"Empty selection for gyration CV '{self.prefix}'")
1034
+
1035
+ commands = self._generate_commands(groups)
1036
+ labels = self._extract_labels(commands, self.prefix, "GYRATION")
1037
+ return labels, commands
1038
+
1039
+ def _generate_commands(self, groups: List[List[int]]) -> List[str]:
1040
+ """Generates all necessary PLUMED commands."""
1041
+ commands = []
1042
+
1043
+ if self.flatten:
1044
+ # Combine all groups into single atom list
1045
+ flat_atoms = [idx for group in groups for idx in group]
1046
+ atom_list = ",".join(str(idx + 1) for idx in flat_atoms)
1047
+ command = f"{self.prefix}: GYRATION ATOMS={atom_list}"
1048
+ if self.type != "RADIUS":
1049
+ command += f" TYPE={self.type}"
1050
+ commands.append(command)
1051
+ else:
1052
+ # Keep groups separate and use strategy to determine which to process
1053
+ if self.strategy == "first" and groups:
1054
+ indices_to_process = [0]
1055
+ else: # "all" - process all groups independently
1056
+ indices_to_process = list(range(len(groups)))
1057
+
1058
+ for i in indices_to_process:
1059
+ label = (
1060
+ self.prefix
1061
+ if len(indices_to_process) == 1
1062
+ else f"{self.prefix}_{i}"
1063
+ )
1064
+ atom_list = ",".join(str(idx + 1) for idx in groups[i])
1065
+ command = f"{label}: GYRATION ATOMS={atom_list}"
1066
+ if self.type != "RADIUS":
1067
+ command += f" TYPE={self.type}"
1068
+ commands.append(command)
1069
+
1070
+ return commands