htpolynet 2.4.0__tar.gz → 2.5.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 (338) hide show
  1. htpolynet-2.5.0/.github/workflows/docker.yml +72 -0
  2. {htpolynet-2.4.0 → htpolynet-2.5.0}/CHANGELOG.md +105 -0
  3. {htpolynet-2.4.0 → htpolynet-2.5.0}/PKG-INFO +9 -6
  4. {htpolynet-2.4.0 → htpolynet-2.5.0}/README.md +8 -5
  5. {htpolynet-2.4.0 → htpolynet-2.5.0}/ROADMAP.md +108 -15
  6. {htpolynet-2.4.0 → htpolynet-2.5.0}/docker/Dockerfile +26 -5
  7. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/results.rst +17 -0
  8. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/install.rst +18 -0
  9. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/configs/configs-for-run.rst +7 -0
  10. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/container-usage.rst +59 -16
  11. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/postcure-repair.rst +62 -3
  12. {htpolynet-2.4.0 → htpolynet-2.5.0}/pyproject.toml +1 -1
  13. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/runtime.py +43 -1
  14. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/cure/curecontroller.py +72 -1
  15. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/repair/__init__.py +9 -4
  16. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/repair/cyanate_cap.py +40 -4
  17. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/example_depot/6-cyanate-ester.yaml +9 -1
  18. htpolynet-2.5.0/tests/unit/test_completion_bias.py +95 -0
  19. htpolynet-2.5.0/tests/unit/test_repair_conversion.py +63 -0
  20. htpolynet-2.4.0/.github/workflows/docker.yml +0 -38
  21. {htpolynet-2.4.0 → htpolynet-2.5.0}/.claude/settings.json +0 -0
  22. {htpolynet-2.4.0 → htpolynet-2.5.0}/.claude/skills/htpolynet/SKILL.md +0 -0
  23. {htpolynet-2.4.0 → htpolynet-2.5.0}/.envrc +0 -0
  24. {htpolynet-2.4.0 → htpolynet-2.5.0}/.github/workflows/conda-forge-sync.yml +0 -0
  25. {htpolynet-2.4.0 → htpolynet-2.5.0}/.github/workflows/release.yaml +0 -0
  26. {htpolynet-2.4.0 → htpolynet-2.5.0}/.github/workflows/test.yml +0 -0
  27. {htpolynet-2.4.0 → htpolynet-2.5.0}/.gitignore +0 -0
  28. {htpolynet-2.4.0 → htpolynet-2.5.0}/.readthedocs.yaml +0 -0
  29. {htpolynet-2.4.0 → htpolynet-2.5.0}/CITATION.cff +0 -0
  30. {htpolynet-2.4.0 → htpolynet-2.5.0}/CLAUDE.md +0 -0
  31. {htpolynet-2.4.0 → htpolynet-2.5.0}/LICENSE +0 -0
  32. {htpolynet-2.4.0 → htpolynet-2.5.0}/MANIFEST.in +0 -0
  33. {htpolynet-2.4.0 → htpolynet-2.5.0}/docker/compose.yml +0 -0
  34. {htpolynet-2.4.0 → htpolynet-2.5.0}/docker/docker-entrypoint.sh +0 -0
  35. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/Makefile +0 -0
  36. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/README.rst +0 -0
  37. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/make.bat +0 -0
  38. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/requirements.txt +0 -0
  39. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/_static/.gitkeep +0 -0
  40. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/changelog.rst +0 -0
  41. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/conf.py +0 -0
  42. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/0-liquid-styrene/configuration.rst +0 -0
  43. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/0-liquid-styrene/index.rst +0 -0
  44. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/0-liquid-styrene/introduction.rst +0 -0
  45. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/0-liquid-styrene/monomer.rst +0 -0
  46. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/0-liquid-styrene/postsim.rst +0 -0
  47. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/0-liquid-styrene/results.rst +0 -0
  48. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/0-liquid-styrene/run.rst +0 -0
  49. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/configuration.rst +0 -0
  50. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/index.rst +0 -0
  51. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/introduction.rst +0 -0
  52. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/monomer.rst +0 -0
  53. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/STY.png +0 -0
  54. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/STYCC.png +0 -0
  55. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/buildtraces.png +0 -0
  56. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/cure_info.png +0 -0
  57. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/densification-density.png +0 -0
  58. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/final-box.png +0 -0
  59. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/reaction_network.png +0 -0
  60. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-coloring.tcl +0 -0
  61. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-cured.png +0 -0
  62. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-detail.png +0 -0
  63. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/sty-liq.png +0 -0
  64. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/pics/styrene-polymerization.png +0 -0
  65. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/postsim.rst +0 -0
  66. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/reactions.rst +0 -0
  67. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/results.rst +0 -0
  68. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/1-polystyrene/run.rst +0 -0
  69. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/configuration.rst +0 -0
  70. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/index.rst +0 -0
  71. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/introduction.rst +0 -0
  72. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/BPA.png +0 -0
  73. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/GMA.png +0 -0
  74. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/HIE.png +0 -0
  75. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/buildtraces.png +0 -0
  76. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/cure_info.png +0 -0
  77. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/densification-density.png +0 -0
  78. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.eps +0 -0
  79. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.fig +0 -0
  80. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.png +0 -0
  81. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-coloring.tcl +0 -0
  82. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-cured.png +0 -0
  83. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-detail.png +0 -0
  84. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-liq.png +0 -0
  85. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/p1-traces.png +0 -0
  86. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/reaction_network.png +0 -0
  87. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.eps +0 -0
  88. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.fig +0 -0
  89. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.png +0 -0
  90. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/postsim.rst +0 -0
  91. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/reactions.rst +0 -0
  92. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/results.rst +0 -0
  93. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/run.rst +0 -0
  94. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/configuration.rst +0 -0
  95. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/index.rst +0 -0
  96. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/introduction.rst +0 -0
  97. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/monomers.rst +0 -0
  98. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/DGE-epoxy.png +0 -0
  99. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/DGE-labelled.png +0 -0
  100. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/PAC-2d.png +0 -0
  101. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/PAC-labelled.png +0 -0
  102. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/buildtraces.png +0 -0
  103. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/cure_info.png +0 -0
  104. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/densification-density.png +0 -0
  105. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-coloring.tcl +0 -0
  106. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-cured.png +0 -0
  107. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-detail.png +0 -0
  108. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-liq.png +0 -0
  109. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.eps +0 -0
  110. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.fig +0 -0
  111. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.png +0 -0
  112. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/equil-rho_v_ns.png +0 -0
  113. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/postsim-typical.png +0 -0
  114. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-e.png +0 -0
  115. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-equil-rho_v_ns.png +0 -0
  116. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-rho_v_ns.png +0 -0
  117. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-tg.png +0 -0
  118. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r1.png +0 -0
  119. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r2.png +0 -0
  120. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r3.png +0 -0
  121. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/reaction_network.png +0 -0
  122. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/rho_v_ns.png +0 -0
  123. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/short-e.png +0 -0
  124. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/short-tg.png +0 -0
  125. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/postsim.rst +0 -0
  126. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/reactions.rst +0 -0
  127. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/results.rst +0 -0
  128. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/run.rst +0 -0
  129. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/configuration.rst +0 -0
  130. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/index.rst +0 -0
  131. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/introduction.rst +0 -0
  132. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/monomers.rst +0 -0
  133. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/buildtraces.png +0 -0
  134. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/cure_info.png +0 -0
  135. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/densification-density.png +0 -0
  136. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-coloring.tcl +0 -0
  137. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-cured.png +0 -0
  138. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-detail.png +0 -0
  139. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-liq.png +0 -0
  140. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/reaction_network.png +0 -0
  141. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/postsim.rst +0 -0
  142. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/reactions.rst +0 -0
  143. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/results.rst +0 -0
  144. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/run.rst +0 -0
  145. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/configuration.rst +0 -0
  146. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/index.rst +0 -0
  147. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/introduction.rst +0 -0
  148. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/monomers.rst +0 -0
  149. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/buildtraces.png +0 -0
  150. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/cure_info.png +0 -0
  151. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/densification-density.png +0 -0
  152. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-coloring.tcl +0 -0
  153. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-cured.png +0 -0
  154. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-detail.png +0 -0
  155. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-liq.png +0 -0
  156. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/pics/reaction_network.png +0 -0
  157. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/postsim.rst +0 -0
  158. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/reactions.rst +0 -0
  159. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/results.rst +0 -0
  160. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/5-htpb-ipdi/run.rst +0 -0
  161. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/configuration.rst +0 -0
  162. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/index.rst +0 -0
  163. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/introduction.rst +0 -0
  164. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/monomers.rst +0 -0
  165. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-coloring.tcl +0 -0
  166. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-cured.png +0 -0
  167. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-detail.png +0 -0
  168. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-liq.png +0 -0
  169. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/buildtraces.png +0 -0
  170. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/cure_info.png +0 -0
  171. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/densification-density.png +0 -0
  172. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/pics/reaction_network.png +0 -0
  173. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/postsim.rst +0 -0
  174. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/reactions.rst +0 -0
  175. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/6-cyanate-ester/run.rst +0 -0
  176. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/example-tutorials/index.rst +0 -0
  177. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/htpolynetpackage.rst +0 -0
  178. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/index.rst +0 -0
  179. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/references/index.rst +0 -0
  180. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/references/mol2-reference.pdf +0 -0
  181. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/references.bib +0 -0
  182. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/release-history.rst +0 -0
  183. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/CURE.odg +0 -0
  184. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/bond_filter.odg +0 -0
  185. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/building-a-system.rst +0 -0
  186. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/configs/configs-for-analyze.rst +0 -0
  187. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/configs/configs-for-postsim.rst +0 -0
  188. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/configuration-files.rst +0 -0
  189. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/flow1.odg +0 -0
  190. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/flow1yaml.odg +0 -0
  191. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/hydrogenated.eps +0 -0
  192. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/hydrogenated.fig +0 -0
  193. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/index.rst +0 -0
  194. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/molecular-structure-inputs.rst +0 -0
  195. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/STY.png +0 -0
  196. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/STYCC.png +0 -0
  197. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/TypicalUsageFlow.png +0 -0
  198. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/bond_filter.png +0 -0
  199. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/chemdoodle-2dsketcher-emb.png +0 -0
  200. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/chemdoodle-2dsketcher-styrene.png +0 -0
  201. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/hydrogenated.png +0 -0
  202. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/ring_pierce/ring_pierce_cases.png +0 -0
  203. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/pics/ring_pierce/ring_pierce_linkcell.png +0 -0
  204. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/program-flow.rst +0 -0
  205. {htpolynet-2.4.0 → htpolynet-2.5.0}/docs/source/user-guide/usage.rst +0 -0
  206. {htpolynet-2.4.0 → htpolynet-2.5.0}/scripts/check-conda-sync.py +0 -0
  207. {htpolynet-2.4.0 → htpolynet-2.5.0}/scripts/release.sh +0 -0
  208. {htpolynet-2.4.0 → htpolynet-2.5.0}/scripts/render-detail.sh +0 -0
  209. {htpolynet-2.4.0 → htpolynet-2.5.0}/scripts/render-detail.tcl +0 -0
  210. {htpolynet-2.4.0 → htpolynet-2.5.0}/scripts/render-snapshot.sh +0 -0
  211. {htpolynet-2.4.0 → htpolynet-2.5.0}/scripts/run_all_examples.sh +0 -0
  212. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/__init__.py +0 -0
  213. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/analysis/__init__.py +0 -0
  214. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/analysis/analyze.py +0 -0
  215. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/analysis/plot.py +0 -0
  216. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/analysis/postsim.py +0 -0
  217. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/analysis/utils.py +0 -0
  218. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/cli.py +0 -0
  219. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/__init__.py +0 -0
  220. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/bondtemplate.py +0 -0
  221. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/configuration.py +0 -0
  222. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/coordinates.py +0 -0
  223. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/molecule.py +0 -0
  224. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/paramcache.py +0 -0
  225. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/projectfilesystem.py +0 -0
  226. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/topocoord.py +0 -0
  227. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/core/topology.py +0 -0
  228. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/cure/__init__.py +0 -0
  229. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/cure/chain.py +0 -0
  230. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/cure/expandreactions.py +0 -0
  231. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/cure/reaction.py +0 -0
  232. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/external/__init__.py +0 -0
  233. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/external/ambertools.py +0 -0
  234. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/external/command.py +0 -0
  235. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/external/gromacs.py +0 -0
  236. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/external/slurm.py +0 -0
  237. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/external/smiles_input.py +0 -0
  238. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/external/software.py +0 -0
  239. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/geometry/__init__.py +0 -0
  240. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/geometry/bondlist.py +0 -0
  241. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/geometry/lattice.py +0 -0
  242. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/geometry/linkcell.py +0 -0
  243. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/geometry/matrix4.py +0 -0
  244. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/geometry/ring.py +0 -0
  245. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/io/__init__.py +0 -0
  246. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/io/gro.py +0 -0
  247. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/io/mol2.py +0 -0
  248. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/io/pdb.py +0 -0
  249. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/repair/topology_surgery.py +0 -0
  250. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/README.md +0 -0
  251. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/__init__.py +0 -0
  252. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/claude/SKILL.md +0 -0
  253. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/example_depot/0-liquid-styrene.yaml +0 -0
  254. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/example_depot/1-polystyrene.yaml +0 -0
  255. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/example_depot/2-bisgma-styrene-thermoset.yaml +0 -0
  256. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/example_depot/3-pacm-dgeba-epoxy-thermoset.yaml +0 -0
  257. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/example_depot/4-dfda-fde-epoxy-thermoset.yaml +0 -0
  258. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/example_depot/5-htpb-ipdi.yaml +0 -0
  259. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/README.md +0 -0
  260. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/drag-min.mdp +0 -0
  261. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/drag-npt.mdp +0 -0
  262. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/drag-nvt.mdp +0 -0
  263. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/min.mdp +0 -0
  264. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/npt.mdp +0 -0
  265. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/nvt.mdp +0 -0
  266. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/relax-min.mdp +0 -0
  267. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/relax-npt.mdp +0 -0
  268. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/relax-nvt.mdp +0 -0
  269. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/single-molecule-min.mdp +0 -0
  270. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/mdp/single-molecule-nvt.mdp +0 -0
  271. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/inputs/DFA.pdb +0 -0
  272. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/inputs/DGE.mol2 +0 -0
  273. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/inputs/EMB.mol2 +0 -0
  274. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/inputs/FDE.pdb +0 -0
  275. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/inputs/GMA.mol2 +0 -0
  276. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/inputs/PAC.mol2 +0 -0
  277. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/inputs/STY.mol2 +0 -0
  278. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/make-monomers.sh +0 -0
  279. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/pics/DFA.png +0 -0
  280. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/pics/DGE.png +0 -0
  281. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/pics/EMB.png +0 -0
  282. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/pics/FDE.png +0 -0
  283. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/pics/GMA.png +0 -0
  284. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/pics/PAC.png +0 -0
  285. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/pics/STY.png +0 -0
  286. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/sample-inputs/DFA.pdb +0 -0
  287. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/sample-inputs/DGE.mol2 +0 -0
  288. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/sample-inputs/EMB.mol2 +0 -0
  289. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/sample-inputs/FDE.pdb +0 -0
  290. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/sample-inputs/GMA.mol2 +0 -0
  291. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/sample-inputs/PAC.mol2 +0 -0
  292. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/molecules/sample-inputs/STY.mol2 +0 -0
  293. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/tcl/readbonds.tcl +0 -0
  294. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/tcl/readgrx.tcl +0 -0
  295. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/resources/tcl/render.tcl +0 -0
  296. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/__init__.py +0 -0
  297. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/banner.py +0 -0
  298. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/checkpoint.py +0 -0
  299. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/dataframetools.py +0 -0
  300. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/inputcheck.py +0 -0
  301. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/logsetup.py +0 -0
  302. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/profiling.py +0 -0
  303. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/stringthings.py +0 -0
  304. {htpolynet-2.4.0 → htpolynet-2.5.0}/src/htpolynet/utils/vmd_viz.py +0 -0
  305. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/__init__.py +0 -0
  306. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/conftest.py +0 -0
  307. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/__init__.py +0 -0
  308. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/config1.gro +0 -0
  309. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/config1.top +0 -0
  310. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/config2.gro +0 -0
  311. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/config2.top +0 -0
  312. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/items31.edr +0 -0
  313. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/items43.edr +0 -0
  314. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/items45.edr +0 -0
  315. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/fixtures/short.mdp +0 -0
  316. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_bondtemplate.py +0 -0
  317. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_chain.py +0 -0
  318. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_configuration.py +0 -0
  319. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_dataframetools.py +0 -0
  320. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_gpu_usability.py +0 -0
  321. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_gromacs_get_energy_menu.py +0 -0
  322. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_gromacs_gmx_energy_trace.py +0 -0
  323. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_inputcheck.py +0 -0
  324. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_linkcell_pierce.py +0 -0
  325. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_paramcache.py +0 -0
  326. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_paramcache_ambertools.py +0 -0
  327. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_parameterize_react.py +0 -0
  328. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_plot_smoke.py +0 -0
  329. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_projectfilesystem.py +0 -0
  330. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_resources.py +0 -0
  331. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_ring.py +0 -0
  332. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_ring_pierce_figs.py +0 -0
  333. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_setup_claude.py +0 -0
  334. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_slurm_script.py +0 -0
  335. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_smiles_input.py +0 -0
  336. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_software_provenance.py +0 -0
  337. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_topology/test.top +0 -0
  338. {htpolynet-2.4.0 → htpolynet-2.5.0}/tests/unit/test_topology.py +0 -0
@@ -0,0 +1,72 @@
1
+ name: Build and push Docker image
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ - "d*"
8
+ schedule:
9
+ # Rebuild weekly (Monday 03:00 UTC) to pick up latest Gromacs/AmberTools
10
+ - cron: '0 3 * * 1'
11
+
12
+ jobs:
13
+ docker:
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ contents: read
17
+ packages: write
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ include:
22
+ # The default image. conda-forge's preferred linux-64 Gromacs is the
23
+ # OpenCL build, which cannot drive NVIDIA devices; this image is
24
+ # CPU-only by design and stays the default so `docker run` on a
25
+ # laptop keeps working without pulling the CUDA toolkit.
26
+ - variant: cpu
27
+ gromacs_build: ""
28
+ cuda_override: ""
29
+ tag_prefix: ""
30
+ # The CUDA image. gromacs=*=nompi_cuda* depends on the __cuda
31
+ # virtual package, which conda synthesizes only where an NVIDIA
32
+ # driver is present -- so the solve fails on this runner unless
33
+ # CONDA_OVERRIDE_CUDA declares one. That makes the image buildable
34
+ # here; it does NOT make it verifiable here. Nothing in CI can
35
+ # confirm that mdrun actually offloads, so smoke-test a new CUDA
36
+ # image on real hardware before recommending it.
37
+ - variant: cuda
38
+ gromacs_build: "nompi_cuda"
39
+ cuda_override: "12.9"
40
+ tag_prefix: "cuda-"
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+
44
+ - name: Log in to GitHub Container Registry
45
+ uses: docker/login-action@v3
46
+ with:
47
+ registry: ghcr.io
48
+ username: ${{ github.actor }}
49
+ password: ${{ secrets.GITHUB_TOKEN }}
50
+
51
+ - name: Compute tags
52
+ id: tags
53
+ run: |
54
+ if [ "${{ matrix.variant }}" = "cuda" ]; then
55
+ moving="ghcr.io/cameronabrams/htpolynet:cuda"
56
+ else
57
+ moving="ghcr.io/cameronabrams/htpolynet:latest"
58
+ fi
59
+ printf 'tags<<EOF\n%s\nghcr.io/cameronabrams/htpolynet:%s%s\nEOF\n' \
60
+ "$moving" "${{ matrix.tag_prefix }}" "${{ github.sha }}" >> "$GITHUB_OUTPUT"
61
+
62
+ - name: Build and push
63
+ uses: docker/build-push-action@v6
64
+ with:
65
+ context: .
66
+ file: docker/Dockerfile
67
+ push: true
68
+ build-args: |
69
+ HTPOLYNET_COMMIT=${{ github.sha }}
70
+ GROMACS_BUILD=${{ matrix.gromacs_build }}
71
+ CONDA_OVERRIDE_CUDA=${{ matrix.cuda_override }}
72
+ tags: ${{ steps.tags.outputs.tags }}
@@ -7,6 +7,111 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.5.0] - 2026-08-26
11
+
12
+ ### Added
13
+
14
+ - **The conversion a cyanate-ester run reports is now the conversion the
15
+ structure actually carries.** The cure iterates on bond conversion --
16
+ bonds formed over bonds possible -- and that is the only number it printed.
17
+ But `postcure_repair` then dismantles every crosslinker that did not fill
18
+ all of its sites, so the structure leaving the repair stage contains only
19
+ *complete* crosslinkers, and the fraction of those is what an experiment
20
+ measures. For a trifunctional crosslinker under random placement the
21
+ second number is roughly the cube of the first, so a run at a bond
22
+ conversion of 0.90 leaves a cyanate conversion near 0.73 -- and nothing
23
+ said so. The repair stage now logs both figures side by side and writes
24
+ them, with the counts behind them, to `repair-summary.yaml` in the repair
25
+ directory.
26
+
27
+ - **`CURE.controls.completion_bias`, an opt-in change to how bond candidates
28
+ are ranked.** Candidates have always been ordered by pair separation
29
+ alone. Separation is uncorrelated with how many bonds a crosslinker
30
+ already carries, and the downselection that follows admits at most one bond
31
+ per residue per iteration, so bonds spread evenly across every crosslinker
32
+ in the box instead of finishing any of them. With `completion_bias: true`
33
+ the number of bonds already on the candidate's `B`-side residue becomes the
34
+ primary sort key and separation the tie-break within each group, so a
35
+ crosslinker with two of three sites filled is completed before an untouched
36
+ one is started. Nothing else about the search changes: same radius growth,
37
+ same dragging and relaxation, same probability application, same cycle
38
+ handling.
39
+
40
+ This is a modelling option, not a fix, and it is **off by default** so that
41
+ every existing config and every shipped example behaves exactly as before.
42
+ Where a partly-reacted crosslinker is a stable species the old ranking is
43
+ the more faithful one; where it is a reactive intermediate -- a
44
+ cyclotrimerizing cyanate ester, whose triazine ring either closes or does
45
+ not -- the new one is. Turning it on changes what `desired_conversion`
46
+ means physically: a config that reaches a crosslinker conversion of 0.76 at
47
+ `desired_conversion: 0.90` unbiased reaches about 0.90 biased, so runs
48
+ either side of the setting must be compared at matched crosslinker
49
+ conversion, not matched `desired_conversion`.
50
+
51
+ It also nearly empties the repair stage, which is worth having on its own
52
+ terms: repair has been seen to blow up at low conversion, where it places
53
+ hundreds of caps and transfers hundreds of fragments and the cap-placement
54
+ geometry produces atom overlaps that a step-0 minimization cannot recover
55
+ from. Completing crosslinkers instead of decorating them leaves it almost
56
+ nothing to do.
57
+
58
+ - **A CUDA image, published as `ghcr.io/cameronabrams/htpolynet:cuda`.** The
59
+ only image until now installed conda-forge's default linux-64 Gromacs,
60
+ which is an OpenCL build; Gromacs no longer drives NVIDIA devices through
61
+ OpenCL, so that image cannot use a GPU at all. The new tag installs
62
+ `gromacs=*=nompi_cuda*` from the same Dockerfile and can.
63
+
64
+ The CPU image remains `:latest` and remains the default, because the CUDA
65
+ build pulls in the CUDA toolkit and inflates the image substantially --
66
+ `docker run` on a laptop should not download a toolkit it cannot use.
67
+ Both tags are built by the same workflow on the same triggers, and both
68
+ carry the per-commit tag scheme (`:<sha>` and `:cuda-<sha>`).
69
+
70
+ `external.software.gpu_unusable_reasons()` needed no change: it already
71
+ reconciled detected hardware against what the gmx build can drive, so the
72
+ CUDA image simply starts passing checks the CPU image fails.
73
+
74
+ Verified on hardware, since nothing in CI can check this: on a Picotte
75
+ V100 node the image reports `GPU support: CUDA` where `:latest` reports
76
+ OpenCL, `htpolynet info` detects the device instead of calling it
77
+ `unusable`, and a complete `fetch-example 1` build ran every one of its
78
+ `gmx mdrun` steps with `-gpu_id 0`, using cuFFT for PME. The CUDA 12.9
79
+ runtime in the image runs against that node's 12.4-era driver (550.127.05)
80
+ under CUDA minor-version compatibility, so a site does not need a
81
+ bleeding-edge driver to use the tag.
82
+
83
+ ### Changed
84
+
85
+ - **Repair drivers now return a statistics dict rather than an operation
86
+ count**, and `htpolynet.repair.run_repair` returns `(total, stats)` rather
87
+ than a bare total. This is what carries the crosslinker-conversion figures
88
+ out to the runtime for reporting. Only affects code that calls a repair
89
+ driver directly; the `postcure_repair` config surface is unchanged.
90
+
91
+ ### Fixed
92
+
93
+ - **The container documentation told users to give the image a GPU, which it
94
+ cannot use.** `container-usage.rst` carried a "GPU support" section
95
+ walking through the NVIDIA Container Toolkit and a `deploy.resources`
96
+ block reserving nvidia devices, ending "htpolynet will detect the
97
+ available GPU(s) automatically at startup" -- and then, sixty lines later
98
+ in the Singularity section, correctly warned that the image's conda-forge
99
+ Gromacs is an OpenCL build and cannot drive NVIDIA devices at all. The
100
+ page contradicted itself, and `README.md` repeated the wrong half with a
101
+ `docker run --gpus all` example.
102
+
103
+ Both now say the same thing: exposing a GPU to this image starts the
104
+ container and changes nothing about how it computes, so target CPU
105
+ partitions and do not hold a device another job could use. The reason and
106
+ the detection behavior are stated once, under Docker, and the HPC warning
107
+ points at it for the `--nv`/`--gres=gpu` specifics.
108
+
109
+ ### Changed
110
+
111
+ - `htpolynet setup-claude` is now discoverable where a new user will meet
112
+ it: the installation page and the README, rather than only the subcommand
113
+ reference.
114
+
10
115
  ## [2.4.0] - 2026-08-25
11
116
 
12
117
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: htpolynet
3
- Version: 2.4.0
3
+ Version: 2.5.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/
@@ -67,21 +67,24 @@ pip install -e .
67
67
 
68
68
  Once installed, the user has access to the main ``htpolynet`` command.
69
69
 
70
+ If you drive htpolynet with [Claude Code](https://claude.com/claude-code), install the bundled skill so the agent knows how to use it:
71
+ ```bash
72
+ htpolynet setup-claude
73
+ ```
74
+ This writes `~/.claude/skills/htpolynet/SKILL.md`; nothing is installed there unless you run it.
75
+
70
76
  IMPORTANT NOTES: The programs ``antechamber``, ``parmchk2`` and ``tleap`` from AmberTools must be in your path. These can be installed using the ``ambertools`` package from ``conda-forge`` or compiled from source. You also need Gromacs installed so ``gmx`` is in your path. The examples show how to build input monomer structures using OpenBabel, so to use them you need ``obabel`` in your path as well.
71
77
 
72
78
  ## Docker
73
79
 
74
- As an alternative to a local installation, a prebuilt container image is published at ``ghcr.io/cameronabrams/htpolynet``. It bundles htpolynet together with Gromacs, AmberTools, and OpenBabel, so no additional dependencies are required on the host beyond Docker (and, optionally, the NVIDIA Container Toolkit for GPU runs).
80
+ As an alternative to a local installation, a prebuilt container image is published at ``ghcr.io/cameronabrams/htpolynet``. It bundles htpolynet together with Gromacs, AmberTools, and OpenBabel, so no additional dependencies are required on the host beyond Docker.
75
81
 
76
82
  Run htpolynet against a configuration file in the current directory:
77
83
  ```bash
78
84
  docker run --rm -v $(pwd):/work ghcr.io/cameronabrams/htpolynet run config.yaml
79
85
  ```
80
86
 
81
- With GPU support:
82
- ```bash
83
- docker run --rm --gpus all -v $(pwd):/work ghcr.io/cameronabrams/htpolynet run config.yaml
84
- ```
87
+ **The image cannot use a GPU.** Its Gromacs comes from conda-forge, built against OpenCL rather than CUDA, and Gromacs no longer drives NVIDIA devices through OpenCL. Passing `--gpus all` starts the container and changes nothing about how it computes; on a cluster, target CPU partitions and do not request `--gres=gpu` or pass `--nv`. If you need GPU-accelerated Gromacs, install htpolynet natively against a CUDA-enabled Gromacs.
85
88
 
86
89
  A Docker Compose file is also provided in [docker/compose.yml](docker/compose.yml) for a shorter invocation (``docker compose run --rm htpolynet run config.yaml``). See [docs/source/user-guide/container-usage.rst](docs/source/user-guide/container-usage.rst) for the full story, including Singularity/Apptainer use on HPC systems.
87
90
 
@@ -33,21 +33,24 @@ pip install -e .
33
33
 
34
34
  Once installed, the user has access to the main ``htpolynet`` command.
35
35
 
36
+ If you drive htpolynet with [Claude Code](https://claude.com/claude-code), install the bundled skill so the agent knows how to use it:
37
+ ```bash
38
+ htpolynet setup-claude
39
+ ```
40
+ This writes `~/.claude/skills/htpolynet/SKILL.md`; nothing is installed there unless you run it.
41
+
36
42
  IMPORTANT NOTES: The programs ``antechamber``, ``parmchk2`` and ``tleap`` from AmberTools must be in your path. These can be installed using the ``ambertools`` package from ``conda-forge`` or compiled from source. You also need Gromacs installed so ``gmx`` is in your path. The examples show how to build input monomer structures using OpenBabel, so to use them you need ``obabel`` in your path as well.
37
43
 
38
44
  ## Docker
39
45
 
40
- As an alternative to a local installation, a prebuilt container image is published at ``ghcr.io/cameronabrams/htpolynet``. It bundles htpolynet together with Gromacs, AmberTools, and OpenBabel, so no additional dependencies are required on the host beyond Docker (and, optionally, the NVIDIA Container Toolkit for GPU runs).
46
+ As an alternative to a local installation, a prebuilt container image is published at ``ghcr.io/cameronabrams/htpolynet``. It bundles htpolynet together with Gromacs, AmberTools, and OpenBabel, so no additional dependencies are required on the host beyond Docker.
41
47
 
42
48
  Run htpolynet against a configuration file in the current directory:
43
49
  ```bash
44
50
  docker run --rm -v $(pwd):/work ghcr.io/cameronabrams/htpolynet run config.yaml
45
51
  ```
46
52
 
47
- With GPU support:
48
- ```bash
49
- docker run --rm --gpus all -v $(pwd):/work ghcr.io/cameronabrams/htpolynet run config.yaml
50
- ```
53
+ **The image cannot use a GPU.** Its Gromacs comes from conda-forge, built against OpenCL rather than CUDA, and Gromacs no longer drives NVIDIA devices through OpenCL. Passing `--gpus all` starts the container and changes nothing about how it computes; on a cluster, target CPU partitions and do not request `--gres=gpu` or pass `--nv`. If you need GPU-accelerated Gromacs, install htpolynet natively against a CUDA-enabled Gromacs.
51
54
 
52
55
  A Docker Compose file is also provided in [docker/compose.yml](docker/compose.yml) for a shorter invocation (``docker compose run --rm htpolynet run config.yaml``). See [docs/source/user-guide/container-usage.rst](docs/source/user-guide/container-usage.rst) for the full story, including Singularity/Apptainer use on HPC systems.
53
56
 
@@ -8,21 +8,25 @@ Rough ordering within each section is by value, not by effort.
8
8
 
9
9
  ## Container and deployment
10
10
 
11
- - **CUDA-enabled Gromacs in the container image.** The published image
12
- installs Gromacs from conda-forge, whose default linux-64 package is
13
- built with OpenCL (not CUDA) and generic `AVX2_256` SIMD. Gromacs no
14
- longer drives NVIDIA devices through OpenCL, so the image cannot use a
15
- GPU at all, and on an AVX-512 host it also leaves single-core
16
- throughput on the table. For reference, Picotte's own module is
17
- `AVX_512` + CUDA. Doing this well probably means a second image tag
18
- (e.g. `:cuda`) built against `gromacs=*=nompi_cuda*` rather than
19
- changing the default, since it pulls in the CUDA runtime and inflates
20
- the image substantially, and it needs a GPU-equipped runner or a
21
- manual build to verify. The CPU image should stay the default so that
22
- `docker run` on a laptop keeps working. Note that
23
- `software.gpu_unusable_reasons()` already reasons about this correctly,
24
- so a CUDA image would simply start passing its checks rather than
25
- needing new logic.
11
+ - **The container's Gromacs is generic `AVX2_256`, on both tags.** The
12
+ `:cuda` image fixes the GPU half of this problem and not the SIMD half:
13
+ conda-forge builds for a portable baseline, so on an AVX-512 host both
14
+ images leave single-core throughput on the table relative to a natively
15
+ built or module-provided Gromacs. This is inherent to installing Gromacs
16
+ from conda-forge and cannot be fixed by choosing a different build string
17
+ -- it would take building Gromacs in the image, which trades the weekly
18
+ rebuild's freshness for a long build and a host-specific artifact.
19
+
20
+ Confirmed on hardware 2026-08-25: the `:cuda` image on Picotte's gpu001
21
+ reports `SIMD instructions: AVX2_256` on a node whose CPUs support
22
+ AVX-512, and Gromacs itself prints the hint that "AVX_512 ...
23
+ instructions will perform best on this hardware". Still unquantified, and
24
+ that is what would decide whether it matters: nobody has run the same
25
+ system against Picotte's own `abramsGrp-gromacs/2021.2/cpu-gpu` module on
26
+ the same node. Note Gromacs also observes that AVX2 is often the better
27
+ choice for runs that offload to a GPU anyway, so the penalty may be small
28
+ for exactly the case the `:cuda` image serves.
29
+
26
30
  - **Publish a digest or version tag people can pin.** `:latest` moves
27
31
  every week via the scheduled rebuild, so a run recorded as "built with
28
32
  the container" is not reproducible. Per-commit tags already exist;
@@ -91,6 +95,40 @@ Coverage as of the last measurement: **38.8%** overall.
91
95
 
92
96
  ## Release and distribution
93
97
 
98
+ - **The release preflight cannot tell whether the *previous* release
99
+ actually shipped to conda-forge.** `scripts/check-conda-sync.py` compares
100
+ `pyproject.toml`'s runtime deps against the feedstock recipe, which
101
+ catches dependency drift the autotick bot cannot handle. It says nothing
102
+ about whether the bot's last PR ever merged. That gap ran for four
103
+ releases: `pip check` in the recipe's test section started failing when
104
+ `ambertools` began pulling in distributions with unsatisfiable metadata,
105
+ so the bot PRs for 2.2.0, 2.3.0, 2.3.1 and 2.4.0 all sat red and unmerged
106
+ while conda-forge served 2.1.0 from June. Every one of those releases
107
+ passed the preflight, because the deps genuinely did match. Nobody
108
+ noticed until a bot email got read.
109
+
110
+ Fixed on 2026-08-26 by dropping `pip check` from the recipe's test section
111
+ (it fails on `ambertools`' bundled distributions, never on ours) and
112
+ merging the 2.4.0 bump, which closed the other three. **The damage is
113
+ permanent and visible**: conda-forge's version list for this package now
114
+ reads 1.0.9 -> 2.1.0 -> 2.4.0, because 2.2.0, 2.3.0 and 2.3.1 were never
115
+ built there and never will be. That is worth knowing as a diagnostic in
116
+ its own right -- a published version list that skips releases the project
117
+ actually made is the retroactive signature of this failure, in any
118
+ package, without needing to have run any check at the time.
119
+
120
+ The check is cheap: query `api.anaconda.org/package/conda-forge/htpolynet`
121
+ for `latest_version` and compare it against the version being superseded.
122
+ A mismatch does not have to block the release -- the fix is usually on the
123
+ feedstock, not here -- but it must be loud, because the failure mode is
124
+ silence. Consider also listing open PRs on the feedstock, since a red bot
125
+ PR is the specific thing to look at.
126
+
127
+ The deeper point is that publishing to conda-forge is the one leg of the
128
+ release that completes *after* `release.sh` exits and on someone else's
129
+ infrastructure, so it is the only one that can fail without anything here
130
+ noticing.
131
+
94
132
  - **External services still keyed to the old repo identity.** The Aug 2026
95
133
  transfer from `AbramsGroup/HTPolyNet` to `cameronabrams/htpolynet` moved
96
134
  the code but left every integration pointing at the old owner. Three
@@ -105,6 +143,12 @@ Coverage as of the last measurement: **38.8%** overall.
105
143
  query". Fix the RTD project URL, then activate the tagged version. Worth
106
144
  keeping this list as the checklist if the repo ever moves again.
107
145
 
146
+ A fourth instance turned up on 2026-08-26 and is fixed: the conda-forge
147
+ recipe's `about:` block still gave `AbramsGroup/HTPolyNet` for `home` and
148
+ `dev_url`, and a `doc_url` of `abramsgroup.github.io/HTPolyNet` that
149
+ returns 404. Corrected in the same feedstock PR that unblocked the version
150
+ bumps.
151
+
108
152
 
109
153
  - **Mint a software DOI.** Enable the Zenodo GitHub integration for
110
154
  `cameronabrams/htpolynet`, then the next `scripts/release.sh` run
@@ -120,6 +164,55 @@ Coverage as of the last measurement: **38.8%** overall.
120
164
  it read `3.10 | 3.11 | 3.12 | 3.13`, which CI already verifies at both
121
165
  ends.
122
166
 
167
+ ## Cure and repair
168
+
169
+ - **`completion_bias` biases the `B` side only, and that is a convention,
170
+ not a law.** The new `CURE.controls.completion_bias` ranks bond candidates
171
+ by how many bonds their `B`-side residue already carries, because
172
+ htpolynet's A2+B3 idiom puts the multifunctional crosslinker in the `B`
173
+ position -- as example 6 does. A user who declares the crosslinker as `A`
174
+ gets no bias at all, silently, because every candidate scores zero on a
175
+ difunctional bridge and the ordering collapses back to distance. The
176
+ generalization is small (rank on the `A` side, or on the sum of both) but
177
+ each variant is a different physical claim about which partly-reacted
178
+ species is an intermediate, and none of them has been run. Do it when
179
+ someone has a chemistry that needs it, and make them say which side.
180
+
181
+ - **Nobody has run example 6 with `completion_bias` on.** The ranking is
182
+ unit-tested -- ordering, tie-breaks, missing residues, the fallback when a
183
+ `.grx` predates the `nreactions` attribute -- but the acceptance criteria
184
+ that matter are system-scale and need gmx and a multi-hour build:
185
+ incomplete triazines should collapse from tens to ~1 at
186
+ `desired_conversion: 0.90`, crosslinker conversion should rise from ~0.76
187
+ to ~0.90, `TAZ + CYN/3` must still equal the initial triazine count
188
+ exactly, and atom conservation must still be exact. If the incomplete
189
+ count does *not* collapse, the sort key is not surviving as far as the
190
+ truncation and that is where to look. Until someone runs it, the directive
191
+ is documented as untested at scale.
192
+
193
+ - **The repair stage's cap placement does not scale to low conversion.**
194
+ At a bond conversion of 0.90 the triazine-to-cyanate driver places on the
195
+ order of a hundred caps; at 0.30 it places hundreds and transfers hundreds
196
+ of fragments, and runs have died at the repair NVT with a step-0 potential
197
+ energy around 7e15 dominated by Lennard-Jones -- atom overlap from cap
198
+ placement. It is packing-dependent rather than systematic: siblings at the
199
+ same conversion survive. `completion_bias` makes the stage nearly empty
200
+ and so hides this, but the geometry is still wrong for a crowded box, and
201
+ a sub-gel-point study that wants the unbiased ranking will hit it again.
202
+ The fix is in `repair/cyanate_cap.py::_place_cyn_along` and the greedy
203
+ matcher around it: place against the local neighbourhood rather than along
204
+ the old O-H vector alone, or minimize incrementally as caps are placed.
205
+
206
+ - **`bdf.loc[:abs_max]` takes one bond more than the limit.** In
207
+ `curecontroller.py::_searchbonds`, the truncation that applies
208
+ `max_conversion_per_iteration` uses `.loc` with a slice, which is
209
+ inclusive of its endpoint, so an iteration limited to `n` bonds forms
210
+ `n + 1`. Harmless in practice -- the limit is a throttle, not a
211
+ correctness bound -- but it is off by one, and fixing it changes the
212
+ trajectory of every existing config by one bond per throttled iteration.
213
+ Worth doing at a version boundary where a small reproducibility break is
214
+ already expected, not before.
215
+
123
216
  ## Usability
124
217
 
125
218
  - **`gen-slurm-script` doesn't stage to scratch.** The emitted script
@@ -20,10 +20,22 @@
20
20
  #
21
21
  # docker run --rm -v $(pwd):/work ghcr.io/cameronabrams/htpolynet run config.yaml
22
22
  #
23
- # GPU support (requires nvidia-container-toolkit):
23
+ # GPUs: the DEFAULT image cannot use one. Its Gromacs is the conda-forge
24
+ # OpenCL build, and Gromacs no longer drives NVIDIA devices through OpenCL, so
25
+ # --gpus all starts the container and changes nothing about how it computes.
24
26
  #
25
- # docker run --rm --gpus all -v $(pwd):/work \
26
- # ghcr.io/cameronabrams/htpolynet run config.yaml
27
+ # Build the CUDA variant instead, which installs gromacs=*=nompi_cuda*:
28
+ #
29
+ # CONDA_OVERRIDE_CUDA=12.9 docker build -f docker/Dockerfile \
30
+ # --build-arg GROMACS_BUILD=nompi_cuda \
31
+ # --build-arg CONDA_OVERRIDE_CUDA=12.9 \
32
+ # -t ghcr.io/cameronabrams/htpolynet:cuda .
33
+ #
34
+ # CONDA_OVERRIDE_CUDA is required to build on a machine with no NVIDIA driver:
35
+ # the CUDA package depends on the __cuda virtual package, which conda
36
+ # synthesizes only where a driver is present, so the solve fails on an
37
+ # ordinary CI runner without it. Running the resulting image does need
38
+ # nvidia-container-toolkit and --gpus all.
27
39
 
28
40
  FROM condaforge/miniforge3:latest
29
41
 
@@ -39,9 +51,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
39
51
  gosu \
40
52
  && apt-get clean && rm -rf /var/lib/apt/lists/*
41
53
 
42
- RUN mamba install -y -n base -c conda-forge \
54
+ # Which Gromacs build to install. Empty (the default) takes whatever
55
+ # conda-forge considers best, which for linux-64 is the OpenCL build -- correct
56
+ # for a CPU image, and unable to drive an NVIDIA device. Pass
57
+ # GROMACS_BUILD=nompi_cuda for the CUDA image; that package depends on the
58
+ # __cuda virtual package, so a build host without an NVIDIA driver also needs
59
+ # CONDA_OVERRIDE_CUDA set to a version to satisfy the solve.
60
+ ARG GROMACS_BUILD=
61
+ ARG CONDA_OVERRIDE_CUDA=
62
+
63
+ RUN CONDA_OVERRIDE_CUDA="${CONDA_OVERRIDE_CUDA}" mamba install -y -n base -c conda-forge \
43
64
  ambertools \
44
- gromacs \
65
+ "gromacs${GROMACS_BUILD:+=*=${GROMACS_BUILD}*}" \
45
66
  parmed \
46
67
  rdkit \
47
68
  && mamba clean -afy
@@ -39,6 +39,23 @@ Diagnostic-log plots:
39
39
  with k<3 are the ones the postcure repair stage subsequently
40
40
  dismantled.
41
41
 
42
+ .. warning::
43
+
44
+ The 90 % above is a **bond** conversion, and it is not the number
45
+ this structure would be reported as in an experiment. Repair
46
+ dismantles every triazine that did not fill all three of its
47
+ sites, so what survives is complete triazines plus unreacted
48
+ cyanate — and the fraction of triazines that survive is what FTIR
49
+ measures at 2270 cm\ :sup:`-1`. Under random placement that
50
+ fraction is about the cube of the bond conversion, so a run at
51
+ 0.90 leaves a cyanate conversion near 0.73. htpolynet reports
52
+ both figures at the end of the repair stage and writes them to
53
+ ``repair-summary.yaml``; see :ref:`what repair reports
54
+ <postcure_repair_reporting>`. If what you want is a structure
55
+ that really is 90 % converted, see the
56
+ ``CURE.controls.completion_bias`` directive.
57
+
58
+
42
59
  For end-to-end traces:
43
60
 
44
61
  .. code-block:: console
@@ -218,6 +218,24 @@ OpenBabel sources are in ``~/Downloads`` and the install prefix is
218
218
  Then set ``PATH``, ``LD_LIBRARY_PATH``, and ``BABEL_LIBDIR`` to point
219
219
  at ``${HOME}/opt/obabel``.
220
220
 
221
+ Driving htpolynet with Claude Code
222
+ ----------------------------------
223
+
224
+ ``htpolynet`` ships a skill for `Claude Code
225
+ <https://claude.com/claude-code>`_ that teaches the agent how to use it:
226
+ start from the nearest example, describe monomers in their active form,
227
+ check a configuration before spending compute, and recognize the failure
228
+ modes that are known rather than mysterious. Install it once, after
229
+ installing the package:
230
+
231
+ .. code-block:: console
232
+
233
+ $ htpolynet setup-claude
234
+
235
+ Installing ``htpolynet`` never writes to ``~/.claude/`` on its own; the
236
+ skill is copied only when you run this. See :doc:`/user-guide/usage` for the options,
237
+ including how to scope the skill to a single project.
238
+
221
239
  Other Prerequisites
222
240
  -------------------
223
241
 
@@ -208,6 +208,8 @@ In this section we show all subdirectives for each of the five main directives i
208
208
  ``gromacs`` list any ``mdp`` keyword:value pairs to include in all ``mdp`` files in the ``CURE`` sequence
209
209
  ===================================== ================= =====================
210
210
 
211
+ .. _cure.controls:
212
+
211
213
  * ``CURE.controls`` parameters
212
214
 
213
215
  ================================== ================= ======================
@@ -221,10 +223,15 @@ In this section we show all subdirectives for each of the five main directives i
221
223
  ``late_threshold`` float [0-1] conversion above which bond probabilities are ignored (default 0.85)
222
224
  ``max_conversion_per_iteration`` float [0-1] upper limit, as a fraction of total reactable bonds, on the new bonds formed in any single iteration (default 1.0)
223
225
  ``min_allowable_bondcycle_length`` int minimum number of C atoms allowed in a cycle of C-C bonds that form via polymerization (default 0, disallow all such cycles)
226
+ ``completion_bias`` bool rank bond candidates by how many bonds their ``B``-side residue already carries, and only then by distance, so that partly-reacted crosslinkers are completed before untouched ones are started (default ``False``)
224
227
  ================================== ================= ======================
225
228
 
226
229
  The ``min_bonds_per_iteration`` knob batches small late-stage finds together: instead of accepting whichever 1-2 bonds happen to lie within the initial 0.5 nm radius and immediately moving on to relax + equilibrate, the search grows the radius until at least 10 (the default) eligible bonds have been gathered. On the DGEBA/PACM example, raising it from 1 to 10 cuts the cure iteration count from 41 to 15; raising further to 20 only buys one more iteration. The default of 10 is roughly the diminishing-returns sweet spot.
227
230
 
231
+ ``completion_bias`` changes which bonds a CURE iteration chooses, and with it what ``desired_conversion`` means physically, so it is off by default and every shipped example runs without it. Candidates are ranked by separation alone by default. Separation is uncorrelated with how many bonds a crosslinker already carries, so in an A2+B3 system the bonds spread evenly across every ``B`` in the box rather than finishing any of them: at a bond conversion of 0.90, a trifunctional crosslinker placed at random is complete with probability 0.90\ :sup:`3`, about 0.73. Turning ``completion_bias`` on makes the number of bonds already on the candidate's ``B``-side residue the primary sort key, with separation as the tie-break inside each group, so a crosslinker with two of three sites filled is finished before an untouched one is started. Nothing else about the search changes -- same radius growth, same dragging and relaxation, same probabilities, same cycle handling.
232
+
233
+ Which conversion this matters for depends on the chemistry. Where a partly-reacted crosslinker is a real, stable species, the unbiased ranking is the more faithful one. Where it is a reactive intermediate -- a cyclotrimerizing cyanate ester, whose triazine ring either closes or does not -- the biased ranking is, and the difference shows up directly in what :ref:`postcure repair <postcure_repair>` has to dismantle. Note that the two settings reach different structures at the same ``desired_conversion``: a config that yields a crosslinker conversion of 0.76 at ``desired_conversion: 0.90`` unbiased will yield about 0.90 biased. Comparisons across the setting have to be made at matched crosslinker conversion, not matched ``desired_conversion``.
234
+
228
235
  The ``min_allowable_bondcycle_length`` refers to the fact that in systems that polymerize via activation of carbon-carbon double bonds, it is possible in the htpolynet implementation that the "head" of a chain of C-C bonds can attack the "tail" and form a cycle, because those represent atom types that can react. It is unclear whether such cycles actually form; if a monomer remains bound to a radical initiator it is hard to see how the head of the growing chain could attack it, but maybe it could. Setting ``min_allowable_bondcycle_length`` to zero (the default) disallows any bonds that would form cycles involving only atoms that were once part of C=C double bonds. (Think about the backbone of polystyrene, for example.) In a given CURE iteration, htpolynet tests the full set of suggested bonds to see if together they result in any cycles, and for each nascent cycle longer than ``min_allowable_bondcycle_length``, htpolynet will disallow the nascent bond that has the longest initial length.
229
236
 
230
237
  .. _cure.drag:
@@ -151,18 +151,52 @@ otherwise it is treated as an ``htpolynet`` subcommand. So:
151
151
  Desktop on Windows always runs as the current user). Output files will be
152
152
  owned correctly without any changes.
153
153
 
154
- GPU support
155
- """""""""""
154
+ GPUs: use the ``:cuda`` tag, not the default image
155
+ """"""""""""""""""""""""""""""""""""""""""""""""""
156
156
 
157
- If you have an NVIDIA GPU and the
158
- `NVIDIA Container Toolkit <https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html>`_
159
- installed, add a ``deploy`` block to your local copy of ``compose.yml``:
157
+ Two images are published from the same Dockerfile, and they differ only in
158
+ which conda-forge Gromacs they install:
159
+
160
+ =============================================== ======================================
161
+ Tag Gromacs build
162
+ =============================================== ======================================
163
+ ``ghcr.io/cameronabrams/htpolynet:latest`` OpenCL (CPU only) --- the default
164
+ ``ghcr.io/cameronabrams/htpolynet:cuda`` CUDA
165
+ =============================================== ======================================
166
+
167
+ .. warning::
168
+
169
+ **The default image cannot use a GPU.** Its Gromacs is the conda-forge
170
+ OpenCL build, and Gromacs no longer supports OpenCL on NVIDIA devices, so
171
+ there is no code path that can drive the device. Passing ``--gpus all`` to
172
+ ``docker run``, or adding a ``deploy.resources`` block reserving ``nvidia``
173
+ devices to ``compose.yml``, starts the container successfully and changes
174
+ nothing about how it computes. On a shared machine, do not hold a GPU that
175
+ another job could use.
176
+
177
+ ``htpolynet`` detects this at startup rather than failing obscurely later:
178
+ it reports ``unusable`` in its GPU banner, drops any ``gpu_id`` from the
179
+ config's ``mdrun_options``, and adds ``-nb cpu`` to the ``mdrun`` command
180
+ line. So the default image on a GPU host is merely wasteful, never wrong.
181
+
182
+ For GPU runs, pull the ``:cuda`` tag and give the container a device. This
183
+ needs the `NVIDIA Container Toolkit
184
+ <https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html>`_
185
+ on the host:
186
+
187
+ .. code-block:: console
188
+
189
+ $ docker run --rm --gpus all -v $(pwd):/work \
190
+ ghcr.io/cameronabrams/htpolynet:cuda run config.yaml
191
+
192
+ Under Compose, point the ``image:`` at the ``:cuda`` tag and add a
193
+ reservation:
160
194
 
161
195
  .. code-block:: yaml
162
196
 
163
197
  services:
164
198
  htpolynet:
165
- image: ghcr.io/cameronabrams/htpolynet:latest
199
+ image: ghcr.io/cameronabrams/htpolynet:cuda
166
200
  volumes:
167
201
  - ${PWD}:/work:Z
168
202
  - htpolynet-home:/home/htpolynet
@@ -181,7 +215,17 @@ installed, add a ``deploy`` block to your local copy of ``compose.yml``:
181
215
  volumes:
182
216
  htpolynet-home:
183
217
 
184
- ``htpolynet`` will detect the available GPU(s) automatically at startup.
218
+ ``htpolynet`` detects the available device(s) at startup and lets Gromacs use
219
+ them. Confirm from its GPU banner rather than assuming: if the banner still
220
+ says ``unusable``, you are running the default image under a ``:cuda`` name,
221
+ or the container was not given a device.
222
+
223
+ .. note::
224
+
225
+ The ``:cuda`` image is substantially larger, because the CUDA build of
226
+ Gromacs pulls in the CUDA toolkit. That is why the CPU image remains the
227
+ default: ``docker run`` on a laptop should not have to download a toolkit
228
+ it cannot use.
185
229
 
186
230
  HPC Users (Singularity/Apptainer)
187
231
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -216,15 +260,14 @@ Then run it, binding your working directory:
216
260
 
217
261
  .. warning::
218
262
 
219
- **The bundled Gromacs is not a CUDA build.** The image installs Gromacs
220
- from conda-forge, whose default package is compiled with OpenCL rather than
221
- CUDA support, and Gromacs no longer supports OpenCL on NVIDIA devices. So
222
- ``--nv`` and ``--gres=gpu:...`` buy you nothing with this image: target a
223
- CPU partition instead. ``htpolynet`` detects this at startup, reports
224
- ``unusable`` in its GPU banner, drops any ``gpu_id`` from the config's
225
- ``mdrun_options``, and adds ``-nb cpu`` to the ``mdrun`` command line. If
226
- you need GPU-accelerated Gromacs on a cluster, run htpolynet natively
227
- against a CUDA-enabled Gromacs module rather than through this container.
263
+ **Pull the right tag.** For the reason given above under `GPUs: use the
264
+ ``:cuda`` tag, not the default image`_, ``--nv`` and ``--gres=gpu:...`` buy
265
+ you nothing with the default image: requesting a GPU only lengthens your
266
+ queue wait and idles a device another job could use. Either target a CPU
267
+ partition, or pull ``docker://ghcr.io/cameronabrams/htpolynet:cuda`` and
268
+ pass ``--nv``. Your cluster's own CUDA-enabled Gromacs module is still
269
+ worth benchmarking against the image, which is built for generic
270
+ ``AVX2_256`` and may lose on a newer host.
228
271
 
229
272
  Example shell scripts work the same way as under Docker — the entrypoint will
230
273
  recognize ``bash`` as an executable and exec it directly: