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