htpolynet 2.9.0__tar.gz → 2.10.0__tar.gz

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 (356) hide show
  1. {htpolynet-2.9.0 → htpolynet-2.10.0}/CHANGELOG.md +14 -0
  2. {htpolynet-2.9.0 → htpolynet-2.10.0}/PKG-INFO +1 -1
  3. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/configs/configs-for-run.rst +27 -0
  4. {htpolynet-2.9.0 → htpolynet-2.10.0}/pyproject.toml +1 -1
  5. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/paramcache.py +35 -2
  6. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/runtime.py +67 -4
  7. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/topology.py +43 -0
  8. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/external/ambertools.py +23 -1
  9. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/schema/base.yaml +12 -0
  10. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_paramcache.py +4 -2
  11. htpolynet-2.10.0/tests/unit/test_user_frcmod.py +149 -0
  12. {htpolynet-2.9.0 → htpolynet-2.10.0}/.claude/settings.json +0 -0
  13. {htpolynet-2.9.0 → htpolynet-2.10.0}/.claude/skills/htpolynet/SKILL.md +0 -0
  14. {htpolynet-2.9.0 → htpolynet-2.10.0}/.envrc +0 -0
  15. {htpolynet-2.9.0 → htpolynet-2.10.0}/.github/workflows/conda-forge-sync.yml +0 -0
  16. {htpolynet-2.9.0 → htpolynet-2.10.0}/.github/workflows/docker.yml +0 -0
  17. {htpolynet-2.9.0 → htpolynet-2.10.0}/.github/workflows/docs.yml +0 -0
  18. {htpolynet-2.9.0 → htpolynet-2.10.0}/.github/workflows/release.yaml +0 -0
  19. {htpolynet-2.9.0 → htpolynet-2.10.0}/.github/workflows/test.yml +0 -0
  20. {htpolynet-2.9.0 → htpolynet-2.10.0}/.gitignore +0 -0
  21. {htpolynet-2.9.0 → htpolynet-2.10.0}/.readthedocs.yaml +0 -0
  22. {htpolynet-2.9.0 → htpolynet-2.10.0}/CITATION.cff +0 -0
  23. {htpolynet-2.9.0 → htpolynet-2.10.0}/CLAUDE.md +0 -0
  24. {htpolynet-2.9.0 → htpolynet-2.10.0}/LICENSE +0 -0
  25. {htpolynet-2.9.0 → htpolynet-2.10.0}/MANIFEST.in +0 -0
  26. {htpolynet-2.9.0 → htpolynet-2.10.0}/README.md +0 -0
  27. {htpolynet-2.9.0 → htpolynet-2.10.0}/ROADMAP.md +0 -0
  28. {htpolynet-2.9.0 → htpolynet-2.10.0}/docker/Dockerfile +0 -0
  29. {htpolynet-2.9.0 → htpolynet-2.10.0}/docker/compose.yml +0 -0
  30. {htpolynet-2.9.0 → htpolynet-2.10.0}/docker/docker-entrypoint.sh +0 -0
  31. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/Makefile +0 -0
  32. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/README.rst +0 -0
  33. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/make.bat +0 -0
  34. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/requirements.txt +0 -0
  35. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/_static/.gitkeep +0 -0
  36. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/_static/htpolynet-favicon.ico +0 -0
  37. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/_static/htpolynet-landing.jpg +0 -0
  38. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/_static/htpolynet-logo-dark.png +0 -0
  39. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/_static/htpolynet-logo-light.png +0 -0
  40. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/changelog.rst +0 -0
  41. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/conf.py +0 -0
  42. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/0-liquid-styrene/configuration.rst +0 -0
  43. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/0-liquid-styrene/index.rst +0 -0
  44. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/0-liquid-styrene/introduction.rst +0 -0
  45. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/0-liquid-styrene/monomer.rst +0 -0
  46. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/0-liquid-styrene/postsim.rst +0 -0
  47. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/0-liquid-styrene/results.rst +0 -0
  48. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/0-liquid-styrene/run.rst +0 -0
  49. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/configuration.rst +0 -0
  50. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/index.rst +0 -0
  51. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/introduction.rst +0 -0
  52. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/monomer.rst +0 -0
  53. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/STY.png +0 -0
  54. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/STYCC.png +0 -0
  55. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/buildtraces.png +0 -0
  56. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/cure_info.png +0 -0
  57. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/densification-density.png +0 -0
  58. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/final-box.png +0 -0
  59. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/reaction_network.png +0 -0
  60. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-coloring.tcl +0 -0
  61. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-cured.png +0 -0
  62. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-detail.png +0 -0
  63. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-liq.png +0 -0
  64. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/pics/styrene-polymerization.png +0 -0
  65. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/postsim.rst +0 -0
  66. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/reactions.rst +0 -0
  67. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/results.rst +0 -0
  68. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/1-polystyrene/run.rst +0 -0
  69. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/configuration.rst +0 -0
  70. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/index.rst +0 -0
  71. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/introduction.rst +0 -0
  72. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/BPA.png +0 -0
  73. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/GMA.png +0 -0
  74. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/HIE.png +0 -0
  75. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/buildtraces.png +0 -0
  76. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/cure_info.png +0 -0
  77. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/densification-density.png +0 -0
  78. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.eps +0 -0
  79. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.fig +0 -0
  80. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.png +0 -0
  81. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-coloring.tcl +0 -0
  82. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-cured.png +0 -0
  83. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-detail.png +0 -0
  84. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-liq.png +0 -0
  85. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/p1-traces.png +0 -0
  86. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/reaction_network.png +0 -0
  87. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.eps +0 -0
  88. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.fig +0 -0
  89. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.png +0 -0
  90. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/postsim.rst +0 -0
  91. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/reactions.rst +0 -0
  92. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/results.rst +0 -0
  93. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/run.rst +0 -0
  94. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/configuration.rst +0 -0
  95. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/index.rst +0 -0
  96. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/introduction.rst +0 -0
  97. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/monomers.rst +0 -0
  98. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/DGE-epoxy.png +0 -0
  99. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/DGE-labelled.png +0 -0
  100. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/PAC-2d.png +0 -0
  101. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/PAC-labelled.png +0 -0
  102. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/buildtraces.png +0 -0
  103. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/cure_info.png +0 -0
  104. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/densification-density.png +0 -0
  105. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-coloring.tcl +0 -0
  106. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-cured.png +0 -0
  107. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-detail.png +0 -0
  108. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-liq.png +0 -0
  109. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.eps +0 -0
  110. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.fig +0 -0
  111. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.png +0 -0
  112. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/equil-rho_v_ns.png +0 -0
  113. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/postsim-typical.png +0 -0
  114. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-e.png +0 -0
  115. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-equil-rho_v_ns.png +0 -0
  116. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-rho_v_ns.png +0 -0
  117. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-tg.png +0 -0
  118. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r1.png +0 -0
  119. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r2.png +0 -0
  120. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r3.png +0 -0
  121. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/reaction_network.png +0 -0
  122. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/rho_v_ns.png +0 -0
  123. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/short-e.png +0 -0
  124. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/short-tg.png +0 -0
  125. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/postsim.rst +0 -0
  126. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/reactions.rst +0 -0
  127. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/results.rst +0 -0
  128. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/run.rst +0 -0
  129. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/configuration.rst +0 -0
  130. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/index.rst +0 -0
  131. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/introduction.rst +0 -0
  132. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/monomers.rst +0 -0
  133. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/buildtraces.png +0 -0
  134. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/cure_info.png +0 -0
  135. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/densification-density.png +0 -0
  136. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-coloring.tcl +0 -0
  137. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-cured.png +0 -0
  138. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-detail.png +0 -0
  139. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-liq.png +0 -0
  140. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/reaction_network.png +0 -0
  141. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/postsim.rst +0 -0
  142. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/reactions.rst +0 -0
  143. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/results.rst +0 -0
  144. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/run.rst +0 -0
  145. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/configuration.rst +0 -0
  146. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/index.rst +0 -0
  147. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/introduction.rst +0 -0
  148. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/monomers.rst +0 -0
  149. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/buildtraces.png +0 -0
  150. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/cure_info.png +0 -0
  151. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/densification-density.png +0 -0
  152. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-coloring.tcl +0 -0
  153. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-cured.png +0 -0
  154. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-detail.png +0 -0
  155. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-liq.png +0 -0
  156. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/reaction_network.png +0 -0
  157. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/postsim.rst +0 -0
  158. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/reactions.rst +0 -0
  159. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/results.rst +0 -0
  160. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/5-htpb-ipdi/run.rst +0 -0
  161. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/configuration.rst +0 -0
  162. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/index.rst +0 -0
  163. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/introduction.rst +0 -0
  164. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/monomers.rst +0 -0
  165. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-coloring.tcl +0 -0
  166. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-cured.png +0 -0
  167. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-detail.png +0 -0
  168. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-liq.png +0 -0
  169. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/buildtraces.png +0 -0
  170. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/cure_info.png +0 -0
  171. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/densification-density.png +0 -0
  172. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/pics/reaction_network.png +0 -0
  173. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/postsim.rst +0 -0
  174. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/reactions.rst +0 -0
  175. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/results.rst +0 -0
  176. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/6-cyanate-ester/run.rst +0 -0
  177. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/example-tutorials/index.rst +0 -0
  178. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/htpolynetpackage.rst +0 -0
  179. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/index.rst +0 -0
  180. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/install.rst +0 -0
  181. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/references/index.rst +0 -0
  182. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/references/mol2-reference.pdf +0 -0
  183. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/references.bib +0 -0
  184. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/CURE.odg +0 -0
  185. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/bond_filter.odg +0 -0
  186. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/building-a-system.rst +0 -0
  187. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/configs/configs-for-analyze.rst +0 -0
  188. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/configs/configs-for-postsim.rst +0 -0
  189. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/configuration-files.rst +0 -0
  190. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/container-usage.rst +0 -0
  191. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/flow1.odg +0 -0
  192. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/flow1yaml.odg +0 -0
  193. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/hydrogenated.eps +0 -0
  194. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/hydrogenated.fig +0 -0
  195. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/index.rst +0 -0
  196. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/molecular-structure-inputs.rst +0 -0
  197. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/periodic-networks.rst +0 -0
  198. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/STY.png +0 -0
  199. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/STYCC.png +0 -0
  200. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/TypicalUsageFlow.png +0 -0
  201. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/bond_filter.png +0 -0
  202. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/chemdoodle-2dsketcher-emb.png +0 -0
  203. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/chemdoodle-2dsketcher-styrene.png +0 -0
  204. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/hydrogenated.png +0 -0
  205. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/ring_pierce/ring_pierce_cases.png +0 -0
  206. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/pics/ring_pierce/ring_pierce_linkcell.png +0 -0
  207. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/postcure-repair.rst +0 -0
  208. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/program-flow.rst +0 -0
  209. {htpolynet-2.9.0 → htpolynet-2.10.0}/docs/source/user-guide/usage.rst +0 -0
  210. {htpolynet-2.9.0 → htpolynet-2.10.0}/scripts/check-conda-sync.py +0 -0
  211. {htpolynet-2.9.0 → htpolynet-2.10.0}/scripts/release.sh +0 -0
  212. {htpolynet-2.9.0 → htpolynet-2.10.0}/scripts/render-detail.sh +0 -0
  213. {htpolynet-2.9.0 → htpolynet-2.10.0}/scripts/render-detail.tcl +0 -0
  214. {htpolynet-2.9.0 → htpolynet-2.10.0}/scripts/render-snapshot.sh +0 -0
  215. {htpolynet-2.9.0 → htpolynet-2.10.0}/scripts/run_all_examples.sh +0 -0
  216. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/__init__.py +0 -0
  217. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/analysis/__init__.py +0 -0
  218. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/analysis/analyze.py +0 -0
  219. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/analysis/plot.py +0 -0
  220. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/analysis/postsim.py +0 -0
  221. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/analysis/utils.py +0 -0
  222. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/cli.py +0 -0
  223. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/__init__.py +0 -0
  224. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/bondtemplate.py +0 -0
  225. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/configuration.py +0 -0
  226. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/coordinates.py +0 -0
  227. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/molecule.py +0 -0
  228. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/projectfilesystem.py +0 -0
  229. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/core/topocoord.py +0 -0
  230. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/cure/__init__.py +0 -0
  231. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/cure/chain.py +0 -0
  232. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/cure/curecontroller.py +0 -0
  233. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/cure/expandreactions.py +0 -0
  234. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/cure/reaction.py +0 -0
  235. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/external/__init__.py +0 -0
  236. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/external/command.py +0 -0
  237. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/external/gromacs.py +0 -0
  238. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/external/slurm.py +0 -0
  239. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/external/smiles_input.py +0 -0
  240. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/external/software.py +0 -0
  241. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/geometry/__init__.py +0 -0
  242. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/geometry/bondlist.py +0 -0
  243. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/geometry/lattice.py +0 -0
  244. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/geometry/linkcell.py +0 -0
  245. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/geometry/matrix4.py +0 -0
  246. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/geometry/ring.py +0 -0
  247. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/io/__init__.py +0 -0
  248. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/io/gro.py +0 -0
  249. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/io/mol2.py +0 -0
  250. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/io/pdb.py +0 -0
  251. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/repair/__init__.py +0 -0
  252. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/repair/cyanate_cap.py +0 -0
  253. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/repair/topology_surgery.py +0 -0
  254. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/README.md +0 -0
  255. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/__init__.py +0 -0
  256. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/claude/SKILL.md +0 -0
  257. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/example_depot/0-liquid-styrene.yaml +0 -0
  258. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/example_depot/1-polystyrene.yaml +0 -0
  259. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/example_depot/2-bisgma-styrene-thermoset.yaml +0 -0
  260. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/example_depot/3-pacm-dgeba-epoxy-thermoset.yaml +0 -0
  261. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/example_depot/4-dfda-fde-epoxy-thermoset.yaml +0 -0
  262. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/example_depot/5-htpb-ipdi.yaml +0 -0
  263. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/example_depot/6-cyanate-ester.yaml +0 -0
  264. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/README.md +0 -0
  265. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/drag-min.mdp +0 -0
  266. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/drag-npt.mdp +0 -0
  267. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/drag-nvt.mdp +0 -0
  268. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/min.mdp +0 -0
  269. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/npt.mdp +0 -0
  270. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/nvt.mdp +0 -0
  271. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/relax-min.mdp +0 -0
  272. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/relax-npt.mdp +0 -0
  273. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/relax-nvt.mdp +0 -0
  274. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/single-molecule-min.mdp +0 -0
  275. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/mdp/single-molecule-nvt.mdp +0 -0
  276. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/inputs/DFA.pdb +0 -0
  277. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/inputs/DGE.mol2 +0 -0
  278. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/inputs/EMB.mol2 +0 -0
  279. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/inputs/FDE.pdb +0 -0
  280. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/inputs/GMA.mol2 +0 -0
  281. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/inputs/PAC.mol2 +0 -0
  282. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/inputs/STY.mol2 +0 -0
  283. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/make-monomers.sh +0 -0
  284. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/pics/DFA.png +0 -0
  285. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/pics/DGE.png +0 -0
  286. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/pics/EMB.png +0 -0
  287. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/pics/FDE.png +0 -0
  288. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/pics/GMA.png +0 -0
  289. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/pics/PAC.png +0 -0
  290. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/pics/STY.png +0 -0
  291. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/sample-inputs/DFA.pdb +0 -0
  292. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/sample-inputs/DGE.mol2 +0 -0
  293. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/sample-inputs/EMB.mol2 +0 -0
  294. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/sample-inputs/FDE.pdb +0 -0
  295. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/sample-inputs/GMA.mol2 +0 -0
  296. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/sample-inputs/PAC.mol2 +0 -0
  297. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/molecules/sample-inputs/STY.mol2 +0 -0
  298. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/tcl/readbonds.tcl +0 -0
  299. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/tcl/readgrx.tcl +0 -0
  300. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/resources/tcl/render.tcl +0 -0
  301. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/__init__.py +0 -0
  302. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/banner.py +0 -0
  303. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/checkpoint.py +0 -0
  304. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/convergence.py +0 -0
  305. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/dataframetools.py +0 -0
  306. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/inputcheck.py +0 -0
  307. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/logsetup.py +0 -0
  308. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/profiling.py +0 -0
  309. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/stringthings.py +0 -0
  310. {htpolynet-2.9.0 → htpolynet-2.10.0}/src/htpolynet/utils/vmd_viz.py +0 -0
  311. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/__init__.py +0 -0
  312. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/conftest.py +0 -0
  313. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/__init__.py +0 -0
  314. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/config1.gro +0 -0
  315. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/config1.top +0 -0
  316. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/config2.gro +0 -0
  317. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/config2.top +0 -0
  318. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/items31.edr +0 -0
  319. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/items43.edr +0 -0
  320. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/items45.edr +0 -0
  321. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/fixtures/short.mdp +0 -0
  322. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_bondtemplate.py +0 -0
  323. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_cap_placement.py +0 -0
  324. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_chain.py +0 -0
  325. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_completion_bias.py +0 -0
  326. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_conda_sync.py +0 -0
  327. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_configuration.py +0 -0
  328. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_convergence.py +0 -0
  329. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_cure_site_functionality.py +0 -0
  330. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_dataframetools.py +0 -0
  331. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_density_gate.py +0 -0
  332. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_gpu_usability.py +0 -0
  333. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_gromacs_get_energy_menu.py +0 -0
  334. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_gromacs_gmx_energy_trace.py +0 -0
  335. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_inputcheck.py +0 -0
  336. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_linkcell_pierce.py +0 -0
  337. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_mol2_bond_fallback.py +0 -0
  338. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_molecule_charge_report.py +0 -0
  339. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_moleculetype_name.py +0 -0
  340. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_paramcache_ambertools.py +0 -0
  341. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_parameterize_react.py +0 -0
  342. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_plot_smoke.py +0 -0
  343. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_projectfilesystem.py +0 -0
  344. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_relax_instrumentation.py +0 -0
  345. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_repair_charge_neutrality.py +0 -0
  346. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_repair_conversion.py +0 -0
  347. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_resources.py +0 -0
  348. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_ring.py +0 -0
  349. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_ring_pierce_figs.py +0 -0
  350. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_setup_claude.py +0 -0
  351. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_sibling_templates.py +0 -0
  352. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_slurm_script.py +0 -0
  353. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_smiles_input.py +0 -0
  354. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_software_provenance.py +0 -0
  355. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_topology/test.top +0 -0
  356. {htpolynet-2.9.0 → htpolynet-2.10.0}/tests/unit/test_topology.py +0 -0
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.10.0] - 2026-09-15
11
+
12
+ ### Added
13
+
14
+ - **A constituent can carry its own frcmod.** `frcmod: file.frcmod` on a
15
+ constituent loads that Amber parameter file into `tleap` after GAFF and
16
+ `parmchk2`, so its parameters override theirs. It is loaded for the
17
+ molecule and for every template containing it, including cure, capping,
18
+ repair and multiply-substituted templates. The file's contents are part of
19
+ the parameter cache record, so an edited frcmod re-parameterizes instead of
20
+ reusing stale numbers. A system has one table per bonded type, so setup
21
+ stops with the type's name if an overridden type also appears, with other
22
+ parameters, in a molecule built without the file.
23
+
10
24
  ## [2.9.0] - 2026-09-15
11
25
 
12
26
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: htpolynet
3
- Version: 2.9.0
3
+ Version: 2.10.0
4
4
  Summary: Automated MD System Builder for Amorphous Network Polymers
5
5
  Project-URL: Source, https://github.com/cameronabrams/htpolynet
6
6
  Project-URL: Documentation, https://htpolynet.readthedocs.io/
@@ -98,8 +98,35 @@ In this section we show all subdirectives for each of the five main directives i
98
98
  ``smiles`` string (optional) SMILES for in-config monomer generation; htpolynet runs ``obabel`` (or RDKit) to materialize ``lib/molecules/inputs/<NAME>.mol2`` before parameterization
99
99
  ``rename_atoms`` dict (optional) ``{1-based-mol2-index: atom-name}`` map applied after obabel writes the mol2 (obabel path; pairs with ``smiles``)
100
100
  ``reactive_atoms`` dict (optional) ``{smiles-atom-map-label: atom-name}`` map (RDKit path; pairs with a ``smiles`` that uses ``[*:N]`` atom-mapping tokens). ``rdkit`` is a required runtime dependency and is pulled in by any normal ``htpolynet`` install
101
+ ``frcmod`` string (optional) path, relative to the configuration file, to an Amber ``frcmod`` of parameter overrides for this molecule; see below
101
102
  ===================================== ================= =====================
102
103
 
104
+ ``frcmod`` corrects GAFF for one monomer without touching the others, for
105
+ example a torsion refit against quantum-chemical scans. ``tleap`` loads
106
+ the file after GAFF and ``parmchk2``, so its entries win. It is loaded
107
+ for the molecule itself and for every template that contains it: cure
108
+ products, capping and repair templates, and the multiply-substituted
109
+ templates htpolynet derives. The file's contents are part of each of
110
+ those parameterizations' cache records, so editing it re-parameterizes
111
+ them on the next run rather than reusing the old numbers.
112
+
113
+ .. code-block:: yaml
114
+
115
+ constituents:
116
+ BAF:
117
+ smiles: "FC(F)(F)C(c1ccc([OH:1])cc1)(c1ccc([OH:2])cc1)C(F)(F)F"
118
+ reactive_atoms: {1: O1, 2: O2}
119
+ frcmod: baf-torsion.frcmod
120
+
121
+ One restriction follows from how GROMACS topologies work. A system holds
122
+ one parameter table per bonded *type* (four atom types, for a dihedral),
123
+ shared by every molecule in it. So an override cannot apply to a type in
124
+ one molecule and leave the same type alone in another: if a molecule
125
+ built without the frcmod also contains the overridden type, with
126
+ different parameters, setup stops and names it. Either load the same
127
+ frcmod for every constituent that contains the type, or give the atoms
128
+ you want to treat differently their own atom type.
129
+
103
130
  In the example below, we are requesting a system of 100 styrene molecules. The key ``STY`` signals to ``htpolynet`` that it should look for either ``STY.mol2`` or ``STY.pdb`` in ``./lib/molecules/inputs`` **or** it should look for ``STY.gro``, ``STY.itp``, ``STY.top``, and ``STY.grx`` in ``./lib/molecules/parameterized``. The latter is the case if either ``htpolynet run`` or ``htpolynet parameterized`` has already been run with ``STY.mol2`` or ``STY.pdb``. If neither set of files is found but the constituent carries a ``smiles`` key, ``htpolynet`` generates the input ``mol2`` itself. Multiple records in ``constituents`` should all have the "key":"record" syntax and be separated by commas.
104
131
 
105
132
  Any entry in ``constituents`` for which the ``count`` is 0 or missing is treated as an intermediate for which stereocenters or symmetry-equivalent atoms are specified.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "htpolynet"
7
- version = "2.9.0"
7
+ version = "2.10.0"
8
8
  authors = [
9
9
  { name="Cameron F Abrams", email="cfa22@drexel.edu" },
10
10
  ]
@@ -17,6 +17,7 @@ libraries keep working rather than silently re-parameterizing wholesale.
17
17
 
18
18
  Author: Cameron F. Abrams <cfa22@drexel.edu>
19
19
  """
20
+ import hashlib
20
21
  import json
21
22
  import logging
22
23
  import os
@@ -33,6 +34,30 @@ _KEY_FIELDS = (
33
34
  )
34
35
  """Fields that determine the parameters, with human-readable labels."""
35
36
 
37
+ FRCMOD_FIELD = 'frcmod'
38
+ """Record field holding a digest of the user frcmod files loaded after parmchk2's.
39
+
40
+ Written only when a molecule has any, so records of molecules without one are
41
+ unchanged; its absence means "none" when compared.
42
+ """
43
+
44
+
45
+ def frcmod_digest(frcmods):
46
+ """Returns a digest identifying a list of user frcmod files by content.
47
+
48
+ Args:
49
+ frcmods (list): (label, text) tuples, in load order
50
+
51
+ Returns:
52
+ str or None: hex digest, or None if the list is empty
53
+ """
54
+ if not frcmods:
55
+ return None
56
+ h = hashlib.sha256()
57
+ for label, text in frcmods:
58
+ h.update(label.encode('utf-8') + b'\0' + text.encode('utf-8') + b'\0')
59
+ return h.hexdigest()[:16]
60
+
36
61
 
37
62
  def build_key(ambertools=None):
38
63
  """Returns the provenance record describing a parameterization run under
@@ -49,8 +74,12 @@ def build_key(ambertools=None):
49
74
  """
50
75
  from ..external.ambertools import AMBERTOOLS_DEFAULTS
51
76
  ambertools = ambertools or {}
52
- return {field: ambertools.get(field, AMBERTOOLS_DEFAULTS[field])
53
- for field, _ in _KEY_FIELDS}
77
+ key = {field: ambertools.get(field, AMBERTOOLS_DEFAULTS[field])
78
+ for field, _ in _KEY_FIELDS}
79
+ digest = frcmod_digest(ambertools.get(FRCMOD_FIELD))
80
+ if digest:
81
+ key[FRCMOD_FIELD] = digest
82
+ return key
54
83
 
55
84
 
56
85
  def key_filename(prefix):
@@ -129,4 +158,8 @@ def describe_mismatch(stored, requested):
129
158
  continue
130
159
  if stored[field] != requested.get(field):
131
160
  diffs.append(f'{label} {stored[field]!r} cached, {requested.get(field)!r} requested')
161
+ # unlike the fields above, an absent frcmod digest is a statement: none was loaded
162
+ if stored.get(FRCMOD_FIELD) != requested.get(FRCMOD_FIELD):
163
+ diffs.append(f'user frcmod {stored.get(FRCMOD_FIELD) or "none"} cached, '
164
+ f'{requested.get(FRCMOD_FIELD) or "none"} requested')
132
165
  return diffs
@@ -20,7 +20,7 @@ from ..core import projectfilesystem as pfs
20
20
  from ..core.configuration import Configuration
21
21
  from ..core.molecule import Molecule, MoleculeDict
22
22
  from ..core.topocoord import TopoCoord
23
- from ..core.topology import select_topology_type_option
23
+ from ..core.topology import conflicting_types, select_topology_type_option
24
24
  from ..cure.chain import ChainManager
25
25
  from ..cure.curecontroller import CureController, CureState
26
26
  from ..cure.expandreactions import bondchain_expand_reactions, generate_stereo_reactions, generate_symmetry_reactions, sibling_expand_reactions
@@ -150,6 +150,7 @@ class Runtime:
150
150
  self.restart=restart
151
151
  if restart:
152
152
  logger.info(f'Restarting in {pfs.proj()}')
153
+ self.frcmods=self._read_frcmods()
153
154
  self.molecules:MoleculeDict={}
154
155
  # molecules reused from a library entry that carries no provenance
155
156
  # record, collected for a stage-end summary; see generate_molecules
@@ -194,6 +195,66 @@ class Runtime:
194
195
  if k not in current:
195
196
  current[k]=v
196
197
 
198
+ def _read_frcmods(self):
199
+ """Reads the user frcmod file each constituent names, if any.
200
+
201
+ Returns:
202
+ dict: {constituent name: frcmod text}
203
+
204
+ Raises:
205
+ FileNotFoundError: if a named frcmod file does not exist
206
+ """
207
+ frcmods={}
208
+ for name,rec in (self.cfg.constituents or {}).items():
209
+ fn=(rec or {}).get('frcmod')
210
+ if not fn: continue
211
+ path=os.path.join(pfs.root(),fn)
212
+ if not os.path.isfile(path):
213
+ raise FileNotFoundError(f'frcmod {fn!r} for constituent {name} not found (looked for {path})')
214
+ with open(path) as f:
215
+ frcmods[name]=f.read()
216
+ logger.info(f'{name}: user frcmod {fn} will be loaded for {name} and every template containing it')
217
+ return frcmods
218
+
219
+ def _ambertools_for(self,M:Molecule):
220
+ """Returns the AmberTools directives for parameterizing M.
221
+
222
+ These are the configuration's, plus the user frcmod files of every
223
+ constituent residue M contains, so that a template built around a
224
+ monomer carries the same override as the monomer.
225
+
226
+ Args:
227
+ M (Molecule): molecule about to be parameterized, or checked against the cache
228
+
229
+ Returns:
230
+ dict: ambertools directives
231
+ """
232
+ amb=dict(self.cfg.ambertools or {})
233
+ residues=set(getattr(M,'sequence',None) or [M.name])
234
+ frc=[(n,t) for n,t in sorted(getattr(self,'frcmods',{}).items()) if n in residues]
235
+ if frc:
236
+ amb['frcmod']=frc
237
+ return amb
238
+
239
+ def _check_frcmod_type_conflicts(self):
240
+ """Refuses a set of molecules that a user frcmod leaves with conflicting bonded types.
241
+
242
+ Raises:
243
+ RuntimeError: if a type is parameterized differently in molecules built with and
244
+ without (or with different) user frcmod files
245
+ """
246
+ if not getattr(self,'frcmods',None): return
247
+ tops={n:(M.TopoCoord.Topology,paramcache.frcmod_digest(self._ambertools_for(M).get('frcmod')))
248
+ for n,M in self.molecules.items() if M.TopoCoord.Topology.D.get('atoms') is not None}
249
+ conflicts=conflicting_types(tops)
250
+ if not conflicts: return
251
+ for typename,key,a,b in conflicts[:10]:
252
+ logger.error(f'{typename} {"-".join(key[:-1])} (func {key[-1]}) differs between {a} and {b}')
253
+ raise RuntimeError(f'{len(conflicts)} bonded type(s) are parameterized differently in molecules built with and '
254
+ f'without a user frcmod. A system has one table per type, so the override cannot apply to '
255
+ f'some molecules and not others. Give the overridden atoms their own atom type, or apply '
256
+ f'the frcmod to every constituent that contains the type.')
257
+
197
258
  def _build_molecules_and_reactions(self):
198
259
  """Builds Molecule and Reaction objects from the parsed configuration.
199
260
 
@@ -331,6 +392,8 @@ class Runtime:
331
392
  self.molecules[mname]=M
332
393
  logger.debug(f'Generated {mname}')
333
394
 
395
+ self._check_frcmod_type_conflicts()
396
+
334
397
  for M in self.molecules:
335
398
  self.molecules[M].is_reactant=is_reactant(M,self.reactions,stage=reaction_stage.cure)
336
399
 
@@ -781,7 +844,7 @@ class Runtime:
781
844
  list: descriptions of the differing directives; empty if the cached
782
845
  parameterization agrees with this run or carries no record
783
846
  """
784
- requested = paramcache.build_key(self.cfg.ambertools)
847
+ requested = paramcache.build_key(self._ambertools_for(M))
785
848
  # Discard any record left in the project directory by an earlier run so
786
849
  # that what we check is the record belonging to the library entry that
787
850
  # previously_parameterized() just found.
@@ -829,7 +892,7 @@ class Runtime:
829
892
  generatable=(not M.generator) or (all([m in self.molecules for m in M.generator.reactants.values()]))
830
893
  if generatable:
831
894
  logger.debug(f'Generating {mname}')
832
- M.generate(available_molecules=self.molecules,gaff=self.cfg.gaff,ambertools=self.cfg.ambertools)
895
+ M.generate(available_molecules=self.molecules,gaff=self.cfg.gaff,ambertools=self._ambertools_for(M))
833
896
  self._checkin_parameterization(mname, force_checkin)
834
897
  M.origin='newly parameterized'
835
898
  else:
@@ -899,7 +962,7 @@ class Runtime:
899
962
  # assert it matches what falls out of the merged dataframe).
900
963
  M.set_sequence_from_moldict(self.molecules)
901
964
  M.generate(available_molecules=self.molecules,
902
- gaff=self.cfg.gaff, ambertools=self.cfg.ambertools)
965
+ gaff=self.cfg.gaff, ambertools=self._ambertools_for(M))
903
966
  self._checkin_parameterization(mname, True)
904
967
  M.origin='newly parameterized (stale cache refreshed)'
905
968
  else:
@@ -137,6 +137,49 @@ _GromacsTopologyDirectiveDefaults_ = {
137
137
  'defaults': [1, 2, 'yes', 0.5, 0.83333333]
138
138
  }
139
139
 
140
+ def conflicting_types(topologies, typenames=('bondtypes', 'angletypes', 'dihedraltypes')):
141
+ """Finds bonded types that two groups of topologies parameterize differently.
142
+
143
+ A merged topology keeps one table per type, shared by every molecule in
144
+ it, so a type with different parameters in two molecules cannot be
145
+ honored for both: drop_duplicates keeps both sets, and GROMACS applies
146
+ them together. A user frcmod loaded for some molecules and not others is
147
+ the usual way to create one.
148
+
149
+ Args:
150
+ topologies (dict): {name: (Topology, group)}; types are compared only between topologies
151
+ whose groups differ, e.g. a digest of the frcmod files each was built with
152
+ typenames (tuple): type sections to compare
153
+
154
+ Returns:
155
+ list: (typename, type key, name of one topology, name of another) tuples, one per conflicting
156
+ type; the key is the atom types followed by the function number
157
+ """
158
+ seen = {}
159
+ conflicts = []
160
+ for name, (T, group) in topologies.items():
161
+ for typename in typenames:
162
+ df = T.D.get(typename)
163
+ if df is None or df.empty:
164
+ continue
165
+ hashables = _GromacsTopologyHashables_[typename]
166
+ params = [c for c in df.columns if c not in hashables]
167
+ rows = {}
168
+ for r in df.itertuples(index=False):
169
+ r = r._asdict()
170
+ t = tuple(r[h] for h in hashables)
171
+ # a proper and an improper can share atom types; they are different types
172
+ key = (*min(t, t[::-1]), int(r['func']) if 'func' in r else 0)
173
+ rows.setdefault(key, []).append(tuple(round(float(r[p]), 4) if isinstance(r[p], (int, float)) else r[p]
174
+ for p in params))
175
+ for key, values in rows.items():
176
+ values = tuple(sorted(values))
177
+ first = seen.setdefault((typename, key), (name, group, values))
178
+ if first[1] != group and first[2] != values:
179
+ conflicts.append((typename, key, first[0], name))
180
+ return conflicts
181
+
182
+
140
183
  def select_topology_type_option(options, typename='dihedraltypes', rule='stiffest'):
141
184
  """Selects from a list of topological interaction options of type typename using the provided rule.
142
185
 
@@ -28,6 +28,15 @@ invocations below and the provenance record written beside their output; see
28
28
  """
29
29
 
30
30
 
31
+ def leapsafe(name):
32
+ """Returns a tleap-safe file stem for name.
33
+
34
+ tleap reads a bare filename containing 'e' after a digit as a number, so
35
+ names are hashed, as GAFFParameterize does for its own files.
36
+ """
37
+ return 'u' + hashlib.shake_128(name.encode('utf-8')).hexdigest(8).replace('e', 'x')
38
+
39
+
31
40
  def GAFFParameterize(inputPrefix, outputPrefix, input_structure_format='mol2', ambertools=None):
32
41
  """Manages execution of antechamber, parmchk2, and tleap to generate GAFF parameters,
33
42
  then converts the result to Gromacs gro/top files via parmed.
@@ -36,7 +45,9 @@ def GAFFParameterize(inputPrefix, outputPrefix, input_structure_format='mol2', a
36
45
  inputPrefix (str): basename of input structure file
37
46
  outputPrefix (str): basename of output files
38
47
  input_structure_format (str): format of input structure file, defaults to 'mol2'; 'pdb' is other option
39
- ambertools (dict | None): ambertools configuration directives, defaults to None
48
+ ambertools (dict | None): ambertools configuration directives, defaults to None; an
49
+ ``frcmod`` entry, a list of (label, text) tuples, names user frcmod files that
50
+ tleap loads after parmchk2's, so their parameters take precedence
40
51
 
41
52
  Raises:
42
53
  parmed.exceptions.GromacsError: if parmed fails to convert tleap output
@@ -65,6 +76,13 @@ def GAFFParameterize(inputPrefix, outputPrefix, input_structure_format='mol2', a
65
76
  f' -at {atomtype} -i {new_structin} -o {mol2out} -pf Y -nc {netcharge} -eq 1 -pl 10', quiet=False)
66
77
  logger.debug(f'AmberTools> Antechamber generated {mol2out}')
67
78
  run(f'parmchk2 -i {mol2out} -o {frcmodout} -f mol2 -s {atomtype}', quiet=False)
79
+ user_frcmods = []
80
+ for label, text in ambertools.get('frcmod') or []:
81
+ fn = f'{leapsafe(label)}-user.frcmod'
82
+ with open(fn, 'w') as f:
83
+ f.write(text)
84
+ user_frcmods.append(fn)
85
+ logger.info(f'AmberTools> loading user frcmod for {label} into {outputPrefix}')
68
86
 
69
87
  # Antechamber ignores SUBSTRUCTURE records, so patch the antechamber output
70
88
  # mol2 with the original resName/resNum before passing it to tleap.
@@ -92,6 +110,8 @@ def GAFFParameterize(inputPrefix, outputPrefix, input_structure_format='mol2', a
92
110
  # fires, and we abort even though tleap would have completed once
93
111
  # the frcmod was loaded.
94
112
  f'loadamberparams {leapprefix}.frcmod',
113
+ # user files last, so they override both GAFF and parmchk2's guesses
114
+ *[f'loadamberparams {fn}' for fn in user_frcmods],
95
115
  f'mymol = loadmol2 {leapprefix}.mol2',
96
116
  'check mymol',
97
117
  f'saveamberparm mymol {leapprefix}-tleap.top {leapprefix}-tleap.crd',
@@ -102,6 +122,8 @@ def GAFFParameterize(inputPrefix, outputPrefix, input_structure_format='mol2', a
102
122
  run(f'tleap -f {inputPrefix}-tleap.in', override=('Error!', 'Unspecified tleap error'))
103
123
 
104
124
  os.remove(f'{leapprefix}.frcmod')
125
+ for fn in user_frcmods:
126
+ os.remove(fn)
105
127
  shutil.move(f'{leapprefix}.mol2', mol2out)
106
128
  shutil.move(f'{leapprefix}-tleap.top', f'{outputPrefix}-tleap.top')
107
129
  shutil.move(f'{leapprefix}-tleap.crd', f'{outputPrefix}-tleap.crd')
@@ -192,6 +192,18 @@ attributes:
192
192
  - name: symmetry_equivalent_atoms
193
193
  type: list
194
194
  text: Sets of symmetry-equivalent atom names, if any.
195
+ - name: frcmod
196
+ type: str
197
+ required: false
198
+ text: >
199
+ Path, relative to the configuration file, to an Amber frcmod file of
200
+ parameter overrides for this molecule. tleap loads it after GAFF and
201
+ parmchk2, so its entries win. It is loaded for this molecule and for
202
+ every template containing it, and it is part of each parameterization's
203
+ cache record, so editing the file re-parameterizes them. A system
204
+ keeps one table per bonded type, so an overridden type must not also
205
+ occur, unoverridden, in a molecule without this residue; setup stops if
206
+ it does.
195
207
  - name: conformers
196
208
  type: dict
197
209
  required: false
@@ -63,8 +63,10 @@ def _library_entry(library, name, **ambertools):
63
63
 
64
64
  def _runtime(**ambertools):
65
65
  """A stand-in for Runtime carrying only what the check reads off it."""
66
- return SimpleNamespace(cfg=SimpleNamespace(ambertools=ambertools),
67
- unverified_parameterizations=[])
66
+ r = SimpleNamespace(cfg=SimpleNamespace(ambertools=ambertools),
67
+ unverified_parameterizations=[], frcmods={})
68
+ r._ambertools_for = lambda M: Runtime._ambertools_for(r, M)
69
+ return r
68
70
 
69
71
 
70
72
  def _mismatch(name='TAZ', **ambertools):
@@ -0,0 +1,149 @@
1
+ """
2
+
3
+ .. module:: test_user_frcmod
4
+ :synopsis: a constituent's frcmod reaches its own parameterization, every template containing it,
5
+ and the parameter-cache record, and nothing else
6
+
7
+ .. moduleauthor: Cameron F. Abrams, <cfa22@drexel.edu>
8
+
9
+ """
10
+ import os
11
+ import shutil
12
+ from types import SimpleNamespace
13
+
14
+ import pandas as pd
15
+ import pytest
16
+
17
+ import htpolynet.core.projectfilesystem as pfs
18
+ from htpolynet.core import paramcache
19
+ from htpolynet.core.molecule import Molecule
20
+ from htpolynet.core.runtime import Runtime
21
+ from htpolynet.core.topology import Topology, conflicting_types
22
+
23
+ FRC = ('BAF', 'MASS\n\nDIHE\nca-ca-c3-ca 1 0.3190 180.0 -2.0\n\n')
24
+
25
+
26
+ class TestCacheRecord:
27
+ def test_no_frcmod_leaves_the_record_as_it_was(self):
28
+ assert paramcache.FRCMOD_FIELD not in paramcache.build_key({'charge_method': 'bcc'})
29
+
30
+ def test_frcmod_is_recorded_by_content(self):
31
+ a = paramcache.build_key({'frcmod': [FRC]})
32
+ b = paramcache.build_key({'frcmod': [(FRC[0], FRC[1] + 'x')]})
33
+ assert a[paramcache.FRCMOD_FIELD] != b[paramcache.FRCMOD_FIELD]
34
+ assert a == paramcache.build_key({'frcmod': [FRC]})
35
+
36
+ def test_plain_cache_entry_is_a_miss_when_a_frcmod_is_requested(self):
37
+ diffs = paramcache.describe_mismatch(paramcache.build_key({}), paramcache.build_key({'frcmod': [FRC]}))
38
+ assert any('frcmod' in d for d in diffs)
39
+
40
+ def test_overridden_cache_entry_is_a_miss_when_none_is_requested(self):
41
+ assert paramcache.describe_mismatch(paramcache.build_key({'frcmod': [FRC]}), paramcache.build_key({}))
42
+
43
+ def test_edited_frcmod_is_a_miss(self):
44
+ assert paramcache.describe_mismatch(paramcache.build_key({'frcmod': [FRC]}),
45
+ paramcache.build_key({'frcmod': [(FRC[0], FRC[1] + '\n')]}))
46
+
47
+ def test_record_from_before_frcmods_matches_a_run_without_one(self):
48
+ stored = {'charge_method': 'bcc', 'net_charge': 0, 'atom_type': 'gaff'}
49
+ assert paramcache.describe_mismatch(stored, paramcache.build_key({})) == []
50
+
51
+
52
+ class TestWhichMoleculesGetIt:
53
+ def runtime(self):
54
+ r = SimpleNamespace(cfg=SimpleNamespace(ambertools={'atom_type': 'gaff2'}), frcmods={'BAF': FRC[1]})
55
+ return r
56
+
57
+ def test_the_monomer(self):
58
+ amb = Runtime._ambertools_for(self.runtime(), SimpleNamespace(name='BAF', sequence=['BAF']))
59
+ assert amb['frcmod'] == [FRC]
60
+ assert amb['atom_type'] == 'gaff2'
61
+
62
+ def test_every_template_containing_it(self):
63
+ M = SimpleNamespace(name='BAF~O1-C1~BAF~O1-C2~TAZ', sequence=['BAF', 'BAF', 'TAZ'])
64
+ assert Runtime._ambertools_for(self.runtime(), M)['frcmod'] == [FRC]
65
+
66
+ def test_not_a_molecule_without_it(self):
67
+ assert 'frcmod' not in Runtime._ambertools_for(self.runtime(), SimpleNamespace(name='TAZ', sequence=['TAZ']))
68
+
69
+ def test_configuration_directives_are_not_mutated(self):
70
+ r = self.runtime()
71
+ Runtime._ambertools_for(r, SimpleNamespace(name='BAF', sequence=['BAF']))
72
+ assert 'frcmod' not in r.cfg.ambertools
73
+
74
+
75
+ class TestReadFrcmods:
76
+ def runtime(self, constituents):
77
+ return SimpleNamespace(cfg=SimpleNamespace(constituents=constituents))
78
+
79
+ def test_reads_relative_to_the_configuration(self, tmp_path):
80
+ (tmp_path / 'baf.frcmod').write_text(FRC[1])
81
+ pfs.pfs_setup(root=str(tmp_path), mock=True)
82
+ got = Runtime._read_frcmods(self.runtime({'BAF': {'frcmod': 'baf.frcmod'}, 'TAZ': {'count': 1}}))
83
+ assert got == {'BAF': FRC[1]}
84
+
85
+ def test_a_missing_file_stops_setup(self, tmp_path):
86
+ pfs.pfs_setup(root=str(tmp_path), mock=True)
87
+ with pytest.raises(FileNotFoundError, match='BAF'):
88
+ Runtime._read_frcmods(self.runtime({'BAF': {'frcmod': 'nope.frcmod'}}))
89
+
90
+
91
+ def dihedraltypes(rows):
92
+ T = Topology()
93
+ T.D['dihedraltypes'] = pd.DataFrame(rows, columns=['i', 'j', 'k', 'l', 'func', 'phase', 'kd', 'pn'])
94
+ return T
95
+
96
+
97
+ class TestConflictingTypes:
98
+ plain = [('ca', 'c3', 'ca', 'ca', 9, 0.0, 0.0, 2)]
99
+ fitted = [('ca', 'c3', 'ca', 'ca', 9, 180.0, 1.335, 2), ('ca', 'c3', 'ca', 'ca', 9, 180.0, 0.959, 4)]
100
+
101
+ def test_overridden_type_also_present_without_the_override(self):
102
+ c = conflicting_types({'BAF': (dihedraltypes(self.fitted), 'd1'), 'BPA': (dihedraltypes(self.plain), None)})
103
+ assert c == [('dihedraltypes', ('ca', 'c3', 'ca', 'ca', 9), 'BAF', 'BPA')]
104
+
105
+ def test_an_improper_with_the_same_atom_types_is_a_different_type(self):
106
+ improper = [('ca', 'c3', 'ca', 'ca', 4, 180.0, 0.263, 2)]
107
+ both = self.fitted + improper
108
+ assert conflicting_types({'BAF': (dihedraltypes(both), 'd1'), 'X': (dihedraltypes(improper), None)}) == []
109
+
110
+ def test_reversed_type_names_are_the_same_type(self):
111
+ rev = [('ca', 'ca', 'c3', 'ca', 9, 0.0, 0.0, 2)]
112
+ assert conflicting_types({'BAF': (dihedraltypes(self.fitted), 'd1'), 'BPA': (dihedraltypes(rev), None)})
113
+
114
+ def test_same_group_differences_are_not_this_check(self):
115
+ # discrepancies among plain GAFF molecules are resolve_type_discrepancies' business
116
+ assert conflicting_types({'A': (dihedraltypes(self.fitted), None), 'B': (dihedraltypes(self.plain), None)}) == []
117
+
118
+ def test_agreeing_parameters_are_fine(self):
119
+ assert conflicting_types({'BAF': (dihedraltypes(self.plain), 'd1'), 'TAZ': (dihedraltypes(self.plain), None)}) == []
120
+
121
+
122
+ _MISSING = [t for t in ('antechamber', 'parmchk2', 'tleap', 'gmx') if shutil.which(t) is None]
123
+
124
+
125
+ @pytest.mark.skipif(bool(_MISSING), reason=f'external tools not on PATH: {", ".join(_MISSING)}')
126
+ class TestTleapLoadsIt:
127
+ def parameterize(self, root, ambertools):
128
+ orig = os.getcwd()
129
+ pfs.pfs_setup(root=str(root), projdir='proj', mock=False)
130
+ try:
131
+ M = Molecule.New('STY', None)
132
+ M.set_sequence_from_moldict({'STY': M})
133
+ M.generate(ambertools=ambertools, gaff={'minimize_molecules': False})
134
+ return Topology.read_top('STY.top').D['dihedraltypes'], paramcache.read_key('STY')
135
+ finally:
136
+ os.chdir(orig)
137
+
138
+ def test_the_override_replaces_the_gaff_torsion(self, tmp_path_factory):
139
+ plain, plain_key = self.parameterize(tmp_path_factory.mktemp('plain'), {'charge_method': 'gas'})
140
+ t = plain.iloc[0]
141
+ names = '-'.join(str(x).ljust(2) for x in (t.i, t.j, t.k, t.l))
142
+ frcmod = f'override\nDIHE\n{names} 1 7.7700 180.0 3.0\n\n'
143
+ over, over_key = self.parameterize(tmp_path_factory.mktemp('over'),
144
+ {'charge_method': 'gas', 'frcmod': [('STY', frcmod)]})
145
+ rows = over[(over.i == t.i) & (over.j == t.j) & (over.k == t.k) & (over.l == t.l)]
146
+ assert list(rows.kd.round(3)) == [round(7.77 * 4.184, 3)]
147
+ assert list(rows.pn) == [3]
148
+ assert paramcache.FRCMOD_FIELD not in plain_key
149
+ assert over_key[paramcache.FRCMOD_FIELD] == paramcache.frcmod_digest([('STY', frcmod)])
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes