htpolynet 2.3.0__tar.gz → 2.3.1__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 (333) hide show
  1. {htpolynet-2.3.0 → htpolynet-2.3.1}/CHANGELOG.md +57 -0
  2. {htpolynet-2.3.0 → htpolynet-2.3.1}/PKG-INFO +1 -1
  3. {htpolynet-2.3.0 → htpolynet-2.3.1}/ROADMAP.md +118 -0
  4. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/postsim.rst +16 -0
  5. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/configs/configs-for-run.rst +9 -1
  6. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/usage.rst +9 -4
  7. {htpolynet-2.3.0 → htpolynet-2.3.1}/pyproject.toml +1 -1
  8. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/analysis/analyze.py +1 -1
  9. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/analysis/postsim.py +1 -1
  10. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/cli.py +1 -1
  11. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/projectfilesystem.py +25 -1
  12. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/runtime.py +19 -8
  13. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/external/software.py +18 -2
  14. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_paramcache.py +20 -4
  15. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_projectfilesystem.py +51 -0
  16. htpolynet-2.3.1/tests/unit/test_software_provenance.py +61 -0
  17. {htpolynet-2.3.0 → htpolynet-2.3.1}/.claude/settings.json +0 -0
  18. {htpolynet-2.3.0 → htpolynet-2.3.1}/.claude/skills/htpolynet/SKILL.md +0 -0
  19. {htpolynet-2.3.0 → htpolynet-2.3.1}/.envrc +0 -0
  20. {htpolynet-2.3.0 → htpolynet-2.3.1}/.github/workflows/conda-forge-sync.yml +0 -0
  21. {htpolynet-2.3.0 → htpolynet-2.3.1}/.github/workflows/docker.yml +0 -0
  22. {htpolynet-2.3.0 → htpolynet-2.3.1}/.github/workflows/release.yaml +0 -0
  23. {htpolynet-2.3.0 → htpolynet-2.3.1}/.github/workflows/test.yml +0 -0
  24. {htpolynet-2.3.0 → htpolynet-2.3.1}/.gitignore +0 -0
  25. {htpolynet-2.3.0 → htpolynet-2.3.1}/.readthedocs.yaml +0 -0
  26. {htpolynet-2.3.0 → htpolynet-2.3.1}/CITATION.cff +0 -0
  27. {htpolynet-2.3.0 → htpolynet-2.3.1}/CLAUDE.md +0 -0
  28. {htpolynet-2.3.0 → htpolynet-2.3.1}/LICENSE +0 -0
  29. {htpolynet-2.3.0 → htpolynet-2.3.1}/MANIFEST.in +0 -0
  30. {htpolynet-2.3.0 → htpolynet-2.3.1}/README.md +0 -0
  31. {htpolynet-2.3.0 → htpolynet-2.3.1}/docker/Dockerfile +0 -0
  32. {htpolynet-2.3.0 → htpolynet-2.3.1}/docker/compose.yml +0 -0
  33. {htpolynet-2.3.0 → htpolynet-2.3.1}/docker/docker-entrypoint.sh +0 -0
  34. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/Makefile +0 -0
  35. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/README.rst +0 -0
  36. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/make.bat +0 -0
  37. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/requirements.txt +0 -0
  38. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/_static/.gitkeep +0 -0
  39. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/changelog.rst +0 -0
  40. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/conf.py +0 -0
  41. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/0-liquid-styrene/configuration.rst +0 -0
  42. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/0-liquid-styrene/index.rst +0 -0
  43. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/0-liquid-styrene/introduction.rst +0 -0
  44. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/0-liquid-styrene/monomer.rst +0 -0
  45. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/0-liquid-styrene/postsim.rst +0 -0
  46. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/0-liquid-styrene/results.rst +0 -0
  47. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/0-liquid-styrene/run.rst +0 -0
  48. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/configuration.rst +0 -0
  49. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/index.rst +0 -0
  50. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/introduction.rst +0 -0
  51. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/monomer.rst +0 -0
  52. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/STY.png +0 -0
  53. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/STYCC.png +0 -0
  54. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/buildtraces.png +0 -0
  55. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/cure_info.png +0 -0
  56. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/densification-density.png +0 -0
  57. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/final-box.png +0 -0
  58. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/reaction_network.png +0 -0
  59. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/sty-coloring.tcl +0 -0
  60. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/sty-cured.png +0 -0
  61. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/sty-detail.png +0 -0
  62. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/sty-liq.png +0 -0
  63. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/pics/styrene-polymerization.png +0 -0
  64. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/postsim.rst +0 -0
  65. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/reactions.rst +0 -0
  66. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/results.rst +0 -0
  67. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/1-polystyrene/run.rst +0 -0
  68. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/configuration.rst +0 -0
  69. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/index.rst +0 -0
  70. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/introduction.rst +0 -0
  71. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/BPA.png +0 -0
  72. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/GMA.png +0 -0
  73. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/HIE.png +0 -0
  74. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/buildtraces.png +0 -0
  75. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/cure_info.png +0 -0
  76. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/densification-density.png +0 -0
  77. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.eps +0 -0
  78. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.fig +0 -0
  79. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/four_dimers.png +0 -0
  80. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-coloring.tcl +0 -0
  81. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-cured.png +0 -0
  82. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-detail.png +0 -0
  83. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/gma-sty-liq.png +0 -0
  84. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/p1-traces.png +0 -0
  85. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/reaction_network.png +0 -0
  86. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.eps +0 -0
  87. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.fig +0 -0
  88. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/pics/vesys.png +0 -0
  89. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/postsim.rst +0 -0
  90. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/reactions.rst +0 -0
  91. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/results.rst +0 -0
  92. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/2-bisgma-styrene-thermoset/run.rst +0 -0
  93. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/configuration.rst +0 -0
  94. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/index.rst +0 -0
  95. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/introduction.rst +0 -0
  96. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/monomers.rst +0 -0
  97. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/DGE-epoxy.png +0 -0
  98. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/DGE-labelled.png +0 -0
  99. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/PAC-2d.png +0 -0
  100. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/PAC-labelled.png +0 -0
  101. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/buildtraces.png +0 -0
  102. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/cure_info.png +0 -0
  103. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/densification-density.png +0 -0
  104. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-coloring.tcl +0 -0
  105. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-cured.png +0 -0
  106. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-detail.png +0 -0
  107. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dge-pac-liq.png +0 -0
  108. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.eps +0 -0
  109. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.fig +0 -0
  110. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/dgesys.png +0 -0
  111. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/equil-rho_v_ns.png +0 -0
  112. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/postsim-typical.png +0 -0
  113. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-e.png +0 -0
  114. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-equil-rho_v_ns.png +0 -0
  115. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-rho_v_ns.png +0 -0
  116. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/prod-tg.png +0 -0
  117. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r1.png +0 -0
  118. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r2.png +0 -0
  119. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/r3.png +0 -0
  120. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/reaction_network.png +0 -0
  121. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/rho_v_ns.png +0 -0
  122. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/short-e.png +0 -0
  123. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/pics/short-tg.png +0 -0
  124. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/postsim.rst +0 -0
  125. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/reactions.rst +0 -0
  126. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/results.rst +0 -0
  127. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/3-pacm-dgeba-epoxy-thermoset/run.rst +0 -0
  128. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/configuration.rst +0 -0
  129. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/index.rst +0 -0
  130. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/introduction.rst +0 -0
  131. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/monomers.rst +0 -0
  132. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/buildtraces.png +0 -0
  133. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/cure_info.png +0 -0
  134. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/densification-density.png +0 -0
  135. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-coloring.tcl +0 -0
  136. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-cured.png +0 -0
  137. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-detail.png +0 -0
  138. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/dfa-fde-liq.png +0 -0
  139. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/pics/reaction_network.png +0 -0
  140. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/postsim.rst +0 -0
  141. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/reactions.rst +0 -0
  142. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/results.rst +0 -0
  143. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/4-dfda-fde-epoxy-thermoset/run.rst +0 -0
  144. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/configuration.rst +0 -0
  145. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/index.rst +0 -0
  146. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/introduction.rst +0 -0
  147. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/monomers.rst +0 -0
  148. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/buildtraces.png +0 -0
  149. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/cure_info.png +0 -0
  150. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/densification-density.png +0 -0
  151. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-coloring.tcl +0 -0
  152. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-cured.png +0 -0
  153. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-detail.png +0 -0
  154. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/htpb-ipdi-liq.png +0 -0
  155. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/pics/reaction_network.png +0 -0
  156. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/postsim.rst +0 -0
  157. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/reactions.rst +0 -0
  158. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/results.rst +0 -0
  159. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/5-htpb-ipdi/run.rst +0 -0
  160. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/configuration.rst +0 -0
  161. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/index.rst +0 -0
  162. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/introduction.rst +0 -0
  163. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/monomers.rst +0 -0
  164. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-coloring.tcl +0 -0
  165. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-cured.png +0 -0
  166. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-detail.png +0 -0
  167. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/badcy-liq.png +0 -0
  168. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/buildtraces.png +0 -0
  169. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/cure_info.png +0 -0
  170. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/densification-density.png +0 -0
  171. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/pics/reaction_network.png +0 -0
  172. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/reactions.rst +0 -0
  173. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/results.rst +0 -0
  174. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/6-cyanate-ester/run.rst +0 -0
  175. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/example-tutorials/index.rst +0 -0
  176. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/htpolynetpackage.rst +0 -0
  177. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/index.rst +0 -0
  178. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/install.rst +0 -0
  179. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/references/index.rst +0 -0
  180. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/references/mol2-reference.pdf +0 -0
  181. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/references.bib +0 -0
  182. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/release-history.rst +0 -0
  183. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/CURE.odg +0 -0
  184. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/bond_filter.odg +0 -0
  185. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/building-a-system.rst +0 -0
  186. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/configs/configs-for-analyze.rst +0 -0
  187. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/configs/configs-for-postsim.rst +0 -0
  188. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/configuration-files.rst +0 -0
  189. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/container-usage.rst +0 -0
  190. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/flow1.odg +0 -0
  191. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/flow1yaml.odg +0 -0
  192. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/hydrogenated.eps +0 -0
  193. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/hydrogenated.fig +0 -0
  194. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/index.rst +0 -0
  195. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/molecular-structure-inputs.rst +0 -0
  196. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/STY.png +0 -0
  197. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/STYCC.png +0 -0
  198. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/TypicalUsageFlow.png +0 -0
  199. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/bond_filter.png +0 -0
  200. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/chemdoodle-2dsketcher-emb.png +0 -0
  201. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/chemdoodle-2dsketcher-styrene.png +0 -0
  202. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/hydrogenated.png +0 -0
  203. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/ring_pierce/ring_pierce_cases.png +0 -0
  204. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/pics/ring_pierce/ring_pierce_linkcell.png +0 -0
  205. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/postcure-repair.rst +0 -0
  206. {htpolynet-2.3.0 → htpolynet-2.3.1}/docs/source/user-guide/program-flow.rst +0 -0
  207. {htpolynet-2.3.0 → htpolynet-2.3.1}/scripts/check-conda-sync.py +0 -0
  208. {htpolynet-2.3.0 → htpolynet-2.3.1}/scripts/release.sh +0 -0
  209. {htpolynet-2.3.0 → htpolynet-2.3.1}/scripts/render-detail.sh +0 -0
  210. {htpolynet-2.3.0 → htpolynet-2.3.1}/scripts/render-detail.tcl +0 -0
  211. {htpolynet-2.3.0 → htpolynet-2.3.1}/scripts/render-snapshot.sh +0 -0
  212. {htpolynet-2.3.0 → htpolynet-2.3.1}/scripts/run_all_examples.sh +0 -0
  213. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/__init__.py +0 -0
  214. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/analysis/__init__.py +0 -0
  215. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/analysis/plot.py +0 -0
  216. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/analysis/utils.py +0 -0
  217. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/__init__.py +0 -0
  218. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/bondtemplate.py +0 -0
  219. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/configuration.py +0 -0
  220. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/coordinates.py +0 -0
  221. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/molecule.py +0 -0
  222. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/paramcache.py +0 -0
  223. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/topocoord.py +0 -0
  224. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/core/topology.py +0 -0
  225. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/cure/__init__.py +0 -0
  226. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/cure/chain.py +0 -0
  227. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/cure/curecontroller.py +0 -0
  228. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/cure/expandreactions.py +0 -0
  229. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/cure/reaction.py +0 -0
  230. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/external/__init__.py +0 -0
  231. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/external/ambertools.py +0 -0
  232. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/external/command.py +0 -0
  233. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/external/gromacs.py +0 -0
  234. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/external/slurm.py +0 -0
  235. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/external/smiles_input.py +0 -0
  236. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/geometry/__init__.py +0 -0
  237. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/geometry/bondlist.py +0 -0
  238. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/geometry/lattice.py +0 -0
  239. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/geometry/linkcell.py +0 -0
  240. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/geometry/matrix4.py +0 -0
  241. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/geometry/ring.py +0 -0
  242. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/io/__init__.py +0 -0
  243. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/io/gro.py +0 -0
  244. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/io/mol2.py +0 -0
  245. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/io/pdb.py +0 -0
  246. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/repair/__init__.py +0 -0
  247. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/repair/cyanate_cap.py +0 -0
  248. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/repair/topology_surgery.py +0 -0
  249. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/README.md +0 -0
  250. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/__init__.py +0 -0
  251. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/example_depot/0-liquid-styrene.yaml +0 -0
  252. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/example_depot/1-polystyrene.yaml +0 -0
  253. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/example_depot/2-bisgma-styrene-thermoset.yaml +0 -0
  254. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/example_depot/3-pacm-dgeba-epoxy-thermoset.yaml +0 -0
  255. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/example_depot/4-dfda-fde-epoxy-thermoset.yaml +0 -0
  256. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/example_depot/5-htpb-ipdi.yaml +0 -0
  257. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/example_depot/6-cyanate-ester.yaml +0 -0
  258. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/README.md +0 -0
  259. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/drag-min.mdp +0 -0
  260. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/drag-npt.mdp +0 -0
  261. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/drag-nvt.mdp +0 -0
  262. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/min.mdp +0 -0
  263. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/npt.mdp +0 -0
  264. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/nvt.mdp +0 -0
  265. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/relax-min.mdp +0 -0
  266. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/relax-npt.mdp +0 -0
  267. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/relax-nvt.mdp +0 -0
  268. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/single-molecule-min.mdp +0 -0
  269. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/mdp/single-molecule-nvt.mdp +0 -0
  270. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/inputs/DFA.pdb +0 -0
  271. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/inputs/DGE.mol2 +0 -0
  272. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/inputs/EMB.mol2 +0 -0
  273. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/inputs/FDE.pdb +0 -0
  274. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/inputs/GMA.mol2 +0 -0
  275. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/inputs/PAC.mol2 +0 -0
  276. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/inputs/STY.mol2 +0 -0
  277. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/make-monomers.sh +0 -0
  278. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/pics/DFA.png +0 -0
  279. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/pics/DGE.png +0 -0
  280. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/pics/EMB.png +0 -0
  281. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/pics/FDE.png +0 -0
  282. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/pics/GMA.png +0 -0
  283. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/pics/PAC.png +0 -0
  284. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/pics/STY.png +0 -0
  285. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/sample-inputs/DFA.pdb +0 -0
  286. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/sample-inputs/DGE.mol2 +0 -0
  287. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/sample-inputs/EMB.mol2 +0 -0
  288. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/sample-inputs/FDE.pdb +0 -0
  289. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/sample-inputs/GMA.mol2 +0 -0
  290. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/sample-inputs/PAC.mol2 +0 -0
  291. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/molecules/sample-inputs/STY.mol2 +0 -0
  292. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/tcl/readbonds.tcl +0 -0
  293. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/tcl/readgrx.tcl +0 -0
  294. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/resources/tcl/render.tcl +0 -0
  295. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/__init__.py +0 -0
  296. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/banner.py +0 -0
  297. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/checkpoint.py +0 -0
  298. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/dataframetools.py +0 -0
  299. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/inputcheck.py +0 -0
  300. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/logsetup.py +0 -0
  301. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/profiling.py +0 -0
  302. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/stringthings.py +0 -0
  303. {htpolynet-2.3.0 → htpolynet-2.3.1}/src/htpolynet/utils/vmd_viz.py +0 -0
  304. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/__init__.py +0 -0
  305. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/conftest.py +0 -0
  306. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/__init__.py +0 -0
  307. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/config1.gro +0 -0
  308. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/config1.top +0 -0
  309. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/config2.gro +0 -0
  310. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/config2.top +0 -0
  311. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/items31.edr +0 -0
  312. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/items43.edr +0 -0
  313. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/items45.edr +0 -0
  314. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/fixtures/short.mdp +0 -0
  315. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_bondtemplate.py +0 -0
  316. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_chain.py +0 -0
  317. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_configuration.py +0 -0
  318. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_dataframetools.py +0 -0
  319. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_gpu_usability.py +0 -0
  320. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_gromacs_get_energy_menu.py +0 -0
  321. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_gromacs_gmx_energy_trace.py +0 -0
  322. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_inputcheck.py +0 -0
  323. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_linkcell_pierce.py +0 -0
  324. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_paramcache_ambertools.py +0 -0
  325. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_parameterize_react.py +0 -0
  326. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_plot_smoke.py +0 -0
  327. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_resources.py +0 -0
  328. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_ring.py +0 -0
  329. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_ring_pierce_figs.py +0 -0
  330. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_slurm_script.py +0 -0
  331. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_smiles_input.py +0 -0
  332. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_topology/test.top +0 -0
  333. {htpolynet-2.3.0 → htpolynet-2.3.1}/tests/unit/test_topology.py +0 -0
@@ -7,6 +7,63 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.3.1] - 2026-08-25
11
+
12
+ ### Fixed
13
+
14
+ - **`htpolynet postsim` and `htpolynet analyze` no longer die in a directory
15
+ without a `lib/`.** Both took `-lib` with a default of `'lib'`, but only
16
+ `htpolynet run` creates that tree, so the default value of a flag the user
17
+ never typed reached `UserLibrary` and tripped its existence assertion:
18
+ `AssertionError: lib is not a directory`, three seconds in. The documented
19
+ container invocation failed on it -- running `postsim` against a staged
20
+ directory holding only finished results, which is the normal shape of a
21
+ cluster workflow, where the cure ran somewhere else.
22
+
23
+ `-lib` now defaults to nothing for these two subcommands, and the
24
+ conventional `./lib` is used when it happens to be there. An explicitly
25
+ supplied `-lib` is still validated, so a typo fails loudly rather than
26
+ silently degrading to no library. The assertion message now names the flag
27
+ and the resolved path instead of echoing a bare `lib` the user never
28
+ supplied.
29
+
30
+ ### Changed
31
+
32
+ - Documented that **the density at the end of `postcure` is not an
33
+ equilibrated density**, in the `postcure` directive reference and at the
34
+ point in the example 6 tutorial where a reader would take a number off the
35
+ plot. A crosslinked network relaxes only above its glass transition, and
36
+ a postcure anneal peaking near *Tg* spends almost no time where the
37
+ network can move, so the plateau reports an under-relaxed structure
38
+ however long the plateau runs. Measured on four independent BPA builds:
39
+ the plateau gives 1.1712 +/- 0.0029 g/cm3 against 1.1983 +/- 0.0037 for
40
+ the same systems melted and slowly re-cooled -- 2.31% apart, with the
41
+ plateau 2.6-2.8% below experiment and the re-cooled value within 1%.
42
+ Nothing is computed incorrectly; the protocol simply does not equilibrate
43
+ what a reader would assume it does. The example's own anneal peak is
44
+ unchanged pending a test that raising it actually helps; see `ROADMAP.md`.
45
+
46
+ - `htpolynet info` no longer reports `HTPolyNet git commit: unknown` for an
47
+ installed copy. pip, conda and the published container have no `.git`
48
+ beside the package, so the lookup could never succeed and a build had no
49
+ way to identify its own code from inside itself -- the same question the
50
+ parameterization records answer for molecules. It now falls back to the
51
+ installed distribution version, which for a released install maps to a tag.
52
+
53
+ - The warnings about a cached parameterization with no provenance record
54
+ now say that the cached values are being **used**, not merely that they
55
+ could not be checked. The old wording ("cannot be checked against the
56
+ requested 'bcc' charge method") left the decisive fact implicit: the
57
+ build proceeds with whatever charges that entry holds. A user who
58
+ upgrades with an existing library, asks for `bcc` and measures `gas`
59
+ numbers would reasonably conclude the 2.3.0 cache guard does not work --
60
+ correct behavior is otherwise indistinguishable from the bug it replaced.
61
+ Both the per-molecule line and the stage-end block now state that the
62
+ build carries the cached charges, that they may not match the requested
63
+ method, and that this is expected for a pre-2.3 entry rather than a
64
+ failure. The same clarification is made in the user guide and the
65
+ `ambertools` directive reference.
66
+
10
67
  ## [2.3.0] - 2026-08-23
11
68
 
12
69
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: htpolynet
3
- Version: 2.3.0
3
+ Version: 2.3.1
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/
@@ -27,6 +27,36 @@ Rough ordering within each section is by value, not by effort.
27
27
  every week via the scheduled rebuild, so a run recorded as "built with
28
28
  the container" is not reproducible. Per-commit tags already exist;
29
29
  what's missing is documenting that users should pin one.
30
+
31
+ - **The container cannot report which commit built it, and its version
32
+ string is actively misleading between releases.** `htpolynet info` used to
33
+ print `git commit: unknown` inside the image, because `_get_git_commit()`
34
+ runs `git rev-parse` against the installed package directory and no
35
+ installed copy has a `.git` beside it. That now falls back to the
36
+ installed distribution version, which is right for a pip or conda install
37
+ and *precisely wrong for the container*: the scheduled rebuild builds from
38
+ `main` HEAD, which can be many commits past the last tag, so the image
39
+ reports its `pyproject.toml` version honestly while running code that is
40
+ not that version.
41
+
42
+ This is live, not hypothetical. The 2026-08-24 03:54 scheduled build moved
43
+ `:latest` to an image built from `948c301` -- four commits past `v2.3.0`
44
+ -- and that image reports `installed version 2.3.0`. `:latest` is the
45
+ default thing a user pulls and the weekly cron is what keeps moving it, so
46
+ the wrong answer is the common case, and it is wrong silently. A user
47
+ could pull `:latest`, trust the version string, and write "htpolynet
48
+ 2.3.0" in a methods section while having run untagged `main`.
49
+
50
+ The fix is to bake the real commit in at image build time -- the build
51
+ context has `.git`, so `docker.yml` or the Dockerfile can capture
52
+ `git rev-parse HEAD` and write it where the package can read it. That
53
+ touches the Dockerfile and the release story together, which is why it was
54
+ kept out of the bugfix that added the version fallback. It belongs with
55
+ the build-manifest entry below: both answer "what produced this build",
56
+ and a manifest that recorded an unreliable version string would launder
57
+ the problem rather than solve it. Raised by the calibration study, which
58
+ has correct provenance only because it pins the image by full sha
59
+ externally -- which remains the right practice either way.
30
60
  - **Retire `ghcr.io/abramsgroup/htpolynet`.** Superseded by the
31
61
  `cameronabrams` package; still public and still serving a June image to
32
62
  anyone with an old link.
@@ -122,6 +152,56 @@ Coverage as of the last measurement: **38.8%** overall.
122
152
  cyanate-ester bridge series meant doing (a) and (b) by hand in a throwaway
123
153
  RDKit script, which is exactly the work a user should not have to
124
154
  reinvent.
155
+ - **The bundled Claude skill only reaches people working in a clone.**
156
+ `.claude/skills/htpolynet/` is picked up when the working directory is the
157
+ repository, which covers contributors and anyone who cloned to run the
158
+ examples -- but not the ordinary user who `pip install`s or
159
+ `conda install`s htpolynet and works in their own project directory, which
160
+ is most of them. Whether to ship it inside the package is undecided.
161
+
162
+ Two things to settle before doing so, neither obvious:
163
+
164
+ 1. **The skill would be broken as written.** Its central move is "read
165
+ `docs/source/user-guide/building-a-system.rst`", a repo path that does
166
+ not exist for an installed user. A shipped version would have to point
167
+ at the Read the Docs URL instead, or the guide would have to ship as
168
+ package data. The first is simpler and goes stale differently -- an RTD
169
+ link tracks `latest`, so an installed 2.3.0 would send its reader to
170
+ documentation for whatever is current.
171
+ 2. **Delivery.** A skill file inside a wheel does nothing on its own;
172
+ something has to place it where the tool looks. An `htpolynet skill
173
+ --install` subcommand copying it into the user's `~/.claude/skills/`
174
+ is the obvious mechanism, and is also the point at which a scientific
175
+ package starts carrying vendor-specific tooling and an install-time
176
+ side effect on a directory it does not own.
177
+
178
+ Worth weighing against simply doing nothing: the procedural guide the skill
179
+ points at is on Read the Docs already, and an agent that reads
180
+ documentation gets the same content without any of this.
181
+
182
+ - **Generated topologies cannot be compared byte-wise, because ParmEd
183
+ stamps them.** Every `.top` htpolynet writes opens with a ParmEd header
184
+ recording the invoking user, the host, and the date:
185
+
186
+ ; File TAZ.top was generated
187
+ ; By user: cfa (1000)
188
+ ; On host: panacea.chemeng.drexel.edu
189
+ ; At date: Fri. May 5 15:55:38 2026
190
+
191
+ So two physically identical parameterizations never hash the same, and the
192
+ obvious check -- "did these two builds produce the same parameters for the
193
+ shared monomers?" -- returns a false difference. The calibration study hit
194
+ this on its bridge series and nearly reported the series confounded on the
195
+ strength of an md5; the fix on their side was to strip comment lines before
196
+ comparing, which every user will have to reinvent. Note the header is
197
+ itself provenance, but the wrong kind: it records *when and where* rather
198
+ than *what directives*, which is what the `parm` record now covers, and it
199
+ actively prevents the comparison you would want. The `.itp` files carry no
200
+ such header and the `parm` records are JSON with sorted keys, so both
201
+ already compare cleanly. Worth either normalizing the `.top` header away or
202
+ shipping a comparison helper; this belongs with the manifest entry below,
203
+ since both are about being able to answer "is this the same build?".
204
+
125
205
  - **`-lib` is read-only, and nothing says so.** `pfs.checkout()` and
126
206
  `pfs.exists()` consult the `-lib` user library first, then the user cache,
127
207
  then the system library -- but `pfs.checkin()` writes unconditionally to the
@@ -234,6 +314,44 @@ Coverage as of the last measurement: **38.8%** overall.
234
314
 
235
315
  ## Example depot
236
316
 
317
+ - **Example 6's postcure anneal peaks too close to *T*:sub:`g` to relax the
318
+ network, and the fix is not simply "run it longer".** Measured on four
319
+ independent BPA builds: the postcure NPT plateau gives 1.1712 ± 0.0029
320
+ g/cm³ where the same systems melted and slowly re-cooled give 1.1983 ±
321
+ 0.0037 g/cm³ -- 2.31% apart, with the plateau 2.6-2.8% below experiment
322
+ and the re-cooled value within 1%. The plateau is an under-relaxed
323
+ structure.
324
+
325
+ The mechanism, and the reason the obvious fix is the wrong one: the
326
+ example anneals at a 500 K peak, and the measured *T*:sub:`g` for this
327
+ system is 487.9 K. That is 12.1 K above the glass transition, for 80 ps.
328
+ The Tg ladder that produced the relaxed value spent 15,785 ps above
329
+ *T*:sub:`g`, peaking 112 K above it -- 197x the time, in the only regime
330
+ where a crosslinked network moves at all. Lengthening an anneal that sits
331
+ 12 K above *T*:sub:`g` extends a process that barely moves anything, so
332
+ the lever is the peak temperature, not the duration.
333
+
334
+ **Untested.** Nobody has yet built with a hotter anneal and compared it
335
+ against the melt-and-recool value, which is the experiment that would
336
+ settle it; the argument above is mechanism, not measurement. That is why
337
+ the example is documented rather than changed. Raising the peak also costs
338
+ wall-clock in a tutorial that should stay small, so the test needs to
339
+ report both whether it works and what it costs.
340
+
341
+ The general problem is worth more than the instance: **any protocol whose
342
+ relaxation depends on being above *T*:sub:`g` is fragile when *T*:sub:`g`
343
+ is unknown at configuration time**, which it always is -- you cannot know
344
+ it before building the thing. A fixed absolute peak temperature is a guess
345
+ that happens to be right or wrong per chemistry, silently. Something
346
+ expressed relative to an estimated or measured *T*:sub:`g`, or a postcure
347
+ that measures *T*:sub:`g` and then anneals accordingly, would be robust
348
+ where an absolute number is not. That is a real design change, not a
349
+ config edit.
350
+
351
+ Raised by the calibration study, which had already published the
352
+ pessimistic density before catching it -- the failure mode is reporting a
353
+ protocol artifact as a force-field result.
354
+
237
355
  - **Example 5 is still not laptop-scale.** The `68d81fb` retune cut the
238
356
  monomer pool to make HTPB/IPDI "fit in a reasonable wall time on a
239
357
  laptop", but a validation run on 16 CPU cores took **10h17m** — about
@@ -87,6 +87,22 @@ Run it:
87
87
  Density during annealing and equilibration
88
88
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
89
89
 
90
+ .. warning::
91
+
92
+ The density this stage settles at is **not** an equilibrated density.
93
+ This example's postcure anneal peaks at 500 K, and the measured
94
+ *T*:sub:`g` for this system is about 488 K -- so the anneal spends its
95
+ 80 ps of peak time only ~12 K above the glass transition, where a
96
+ crosslinked network barely moves. On four independent builds the
97
+ postcure plateau came out 2.31% below the same systems' density after a
98
+ melt and slow re-cool, and 2.6-2.8% below experiment, where the
99
+ re-cooled value agrees with experiment to under 1%.
100
+
101
+ The 500 K peak is fine for this tutorial's purpose, which is to show the
102
+ workflow. It is not a basis for quoting a density. Take densities from
103
+ the cooling ladder below instead.
104
+
105
+
90
106
  .. admonition:: Placeholder
91
107
  :class: caution
92
108
 
@@ -61,7 +61,7 @@ In this section we show all subdirectives for each of the five main directives i
61
61
 
62
62
  These three directives determine the parameters ``antechamber`` produces, so each parameterization records them in a ``.parm`` file checked into the library alongside its ``gro``, ``top``, ``itp``, ``tpx`` and ``grx`` files. Because the library is keyed on molecule name, that record is the only thing distinguishing a molecule parameterized one way from the same molecule parameterized another. A run whose directives disagree with the record re-parameterizes rather than reusing the cached files, and says which directives differed.
63
63
 
64
- A parameterization checked in before htpolynet 2.3 has no such record. It is still used, but the run warns that it cannot verify what produced it; rebuild it with ``--force-parameterization`` if you need to be sure. Note that re-parameterizing after a mismatch does **not** replace the library entry unless you also pass ``--force-checkin``, so a library holding a ``gas`` parameterization keeps it even after a ``bcc`` run.
64
+ A parameterization checked in before htpolynet 2.3 has no such record. It is still used **as-is**, which means the build takes whatever charges that entry holds even if they were produced with a different ``charge_method``; the run warns, per molecule and again in a block at the end of the parameterization stage, that it could not verify what produced them. This is deliberate -- it keeps an existing library working instead of re-parameterizing it wholesale -- but it does mean that asking for ``bcc`` against a pre-2.3 library can still yield ``gas`` numbers. Rebuild those entries with ``--force-parameterization`` if a build's charges need to be certain. Note that re-parameterizing after a mismatch does **not** replace the library entry unless you also pass ``--force-checkin``, so a library holding a ``gas`` parameterization keeps it even after a ``bcc`` run.
65
65
 
66
66
  * ``GAFF``
67
67
 
@@ -263,6 +263,14 @@ In this section we show all subdirectives for each of the five main directives i
263
263
 
264
264
  The ``postcure`` directive instructs ``htpolynet`` on running a series of MD simulations after cure. Its form is identical to that of ``precure``, namely with optional ``preequilibration``, ``anneal``, and ``postequilibration`` subdirectives.
265
265
 
266
+ .. warning::
267
+
268
+ **The density at the end of ``postequilibration`` is not an equilibrated density, and should not be quoted as one.** A crosslinked network only relaxes appreciably above its glass transition, and a postcure anneal whose peak sits near *T*:sub:`g` spends very little time where the network can move -- so the postcure plateau reports an under-relaxed structure no matter how long the plateau itself runs.
269
+
270
+ Measured on four independent BPA cyanate-ester builds: the postcure NPT plateau gave 1.1712 ± 0.0029 g/cm³ at 300 K, while the same systems melted and slowly re-cooled gave 1.1983 ± 0.0037 g/cm³ at 293 K -- a 2.31% difference between two extractions of the same quantity. Against experiment (1.205 by mercury dilatometry, 1.202 by flotation, same monomer), the plateau value is 2.6-2.8% low and the re-cooled value 0.3-0.6% low. A build reporting the plateau density would have read as a force-field failure when it was a protocol artifact.
271
+
272
+ If you need a density, extract it from a melt-and-slow-cool trajectory (an :doc:`htpolynet postsim <configs-for-postsim>` ladder does this) rather than from the postcure plateau. The same caution applies to any property that depends on the structure having relaxed.
273
+
266
274
  .. _reactions:
267
275
 
268
276
  * ``reactions``
@@ -222,8 +222,9 @@ checked in under the same name.
222
222
  .. note::
223
223
 
224
224
  A parameterization checked in before htpolynet 2.3 carries no record.
225
- Those entries are still used, so an existing cache keeps working, but
226
- each one logs a warning that its provenance could not be verified, and
225
+ Those entries are still used **as-is**, so an existing cache keeps
226
+ working — but the build takes whatever charges they hold, which may not
227
+ be what your ``charge_method`` asks for. Each one logs a warning, and
227
228
  the parameterization stage ends with a block listing them all. Rebuild
228
229
  them with ``--force-parameterization`` if you need certainty about what
229
230
  a build used.
@@ -387,7 +388,9 @@ The purpose of the ``postsim`` subcommand is to control the execution of several
387
388
  -h, --help show this help message and exit
388
389
  -proj PROJ [PROJ ...]
389
390
  name of project directory
390
- -lib LIB local user library of molecular structures and parameterizations
391
+ -lib LIB local user library of molecular structures and
392
+ parameterizations (default: ./lib if it exists,
393
+ otherwise none)
391
394
  -ocfg OCFG original htpolynet config file used to generate project(s)
392
395
  -cfg CFG config file specifying the operations to perform
393
396
  --no-banner suppress the startup banner
@@ -410,7 +413,9 @@ The ``analyze`` subcommand provides a shortcut interfaces to selected ``gmx``-st
410
413
  -h, --help show this help message and exit
411
414
  -proj PROJ [PROJ ...]
412
415
  name of project directory
413
- -lib LIB local user library of molecular structures and parameterizations
416
+ -lib LIB local user library of molecular structures and
417
+ parameterizations (default: ./lib if it exists,
418
+ otherwise none)
414
419
  -ocfg OCFG original htpolynet config file used to generate project(s)
415
420
  -cfg CFG config file specifying the operations to perform
416
421
  --no-banner suppress the startup banner
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "htpolynet"
7
- version = "2.3.0"
7
+ version = "2.3.1"
8
8
  authors = [
9
9
  { name="Cameron F Abrams", email="cfa22@drexel.edu" },
10
10
  ]
@@ -248,7 +248,7 @@ def analyze(args):
248
248
  software.sw_setup()
249
249
  logger.debug(f'ogromacs {ogromacs}')
250
250
  for d in args.proj:
251
- pfs.pfs_setup(root=os.getcwd(),topdirs=pfs.Dirs.analyze_topdirs,verbose=True,projdir=d,reProject=False,userlibrary=args.lib)
251
+ pfs.pfs_setup(root=os.getcwd(),topdirs=pfs.Dirs.analyze_topdirs,verbose=True,projdir=d,reProject=False,userlibrary=pfs.resolve_user_library(args.lib))
252
252
  pfs.go_to(pfs.Dirs.analyze)
253
253
  for stage in cfg.stagelist:
254
254
  stage.do(**ogromacs)
@@ -436,7 +436,7 @@ def postsim(args):
436
436
  software.sw_setup()
437
437
  logger.debug(f'ogromacs {ogromacs}')
438
438
  for d in args.proj:
439
- pfs.pfs_setup(root=os.getcwd(),topdirs=pfs.Dirs.postsim_topdirs,verbose=True,projdir=d,reProject=False,userlibrary=args.lib)
439
+ pfs.pfs_setup(root=os.getcwd(),topdirs=pfs.Dirs.postsim_topdirs,verbose=True,projdir=d,reProject=False,userlibrary=pfs.resolve_user_library(args.lib))
440
440
  pfs.go_to(pfs.Dirs.postsim)
441
441
  for stage in cfg.stagelist:
442
442
  stage.do(mdp_pfx='local',**ogromacs)
@@ -235,7 +235,7 @@ def _add_gen_slurm_args(p):
235
235
  def _add_analysis_args(p):
236
236
  """Adds the common arguments shared by the postsim and analyze subcommands."""
237
237
  p.add_argument('-proj',type=str,default='',nargs='+',help='name of project directory')
238
- p.add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
238
+ p.add_argument('-lib',type=str,default=None,help='local user library of molecular structures and parameterizations (default: ./lib if it exists, otherwise none)')
239
239
  p.add_argument('-ocfg',type=str,default='',help='original htpolynet config file used to generate project(s)')
240
240
  p.add_argument('-cfg',type=str,default='',help='config file specifying the operations to perform')
241
241
  p.add_argument('--no-banner',default=False,action='store_true',help='suppress the startup banner')
@@ -208,7 +208,9 @@ class UserLibrary:
208
208
  pathname (str): path to the user library directory, defaults to '.'
209
209
  """
210
210
  self.root = Path(os.path.abspath(pathname))
211
- assert self.root.exists() and self.root.is_dir(), f'{pathname} is not a directory'
211
+ assert self.root.exists() and self.root.is_dir(), (
212
+ f'user library {pathname!r} (resolved to {self.root}) is not a directory; '
213
+ f'pass -lib <path> to point at an existing library, or create it')
212
214
 
213
215
  def exists(self, filename):
214
216
  """Checks if filename exists in the user library.
@@ -448,6 +450,28 @@ def pfs_setup(root='.', topdirs=['molecules', 'systems', 'plots'], projdir='next
448
450
  userlibrary=userlibrary, mock=mock)
449
451
 
450
452
 
453
+ def resolve_user_library(pathname, default='lib'):
454
+ """Returns the user-library path a subcommand should use, or None for none.
455
+
456
+ An explicitly supplied path is returned unchanged, so that a typo fails
457
+ loudly in UserLibrary rather than silently degrading to no library. When
458
+ nothing was supplied, the conventional ``lib/`` is used only if it is
459
+ actually there: a subcommand that merely reads finished results has no
460
+ reason to require a molecule library, and demanding one turns the default
461
+ value of a flag the user never typed into a hard error.
462
+
463
+ Args:
464
+ pathname (str or None): the value of the ``-lib`` flag, or None if unset
465
+ default (str): the conventional library directory, defaults to 'lib'
466
+
467
+ Returns:
468
+ str or None: path to use as the user library, or None for no user library
469
+ """
470
+ if pathname is not None:
471
+ return pathname
472
+ return default if os.path.isdir(default) else None
473
+
474
+
451
475
  def checkout(filename, altpath=[]):
452
476
  """Copies a file to cwd; searches user library, then user cache, then system library.
453
477
 
@@ -619,18 +619,27 @@ class Runtime:
619
619
  of the stage is what someone scanning the tail of a log actually hits,
620
620
  and it arrives while the expensive part of the build is still ahead of
621
621
  them rather than behind.
622
+
623
+ Both messages have to say that the cached parameters are being *used*,
624
+ not merely that they could not be checked. A reader who upgrades with
625
+ an existing library, asks for one charge method, and gets another one's
626
+ numbers will otherwise conclude the cache guard does not work -- the
627
+ correct behavior here is indistinguishable from the bug it replaced
628
+ unless the message says which is happening.
622
629
  """
623
630
  if not self.unverified_parameterizations:
624
631
  return
625
632
  names = sorted(set(self.unverified_parameterizations))
626
633
  ess = '' if len(names) == 1 else 's'
627
634
  requested = paramcache.build_key(self.cfg.ambertools)
628
- my_logger(f'{len(names)} parameterization{ess} of unverified provenance', logger.warning)
629
- logger.warning(f'Reused from the library, but the library entry records nothing about how it')
630
- logger.warning(f'was built, so this run could not confirm it was parameterized with the')
631
- logger.warning(f'requested {requested["charge_method"]!r} charge method:')
635
+ my_logger(f'{len(names)} parameterization{ess} reused without provenance', logger.warning)
636
+ logger.warning('These were taken from the library, which records nothing about how they were')
637
+ logger.warning('built. This build therefore carries whatever charges those entries hold, and')
638
+ logger.warning(f'they may not be {requested["charge_method"]!r}:')
632
639
  my_logger(names, logger.warning)
633
- logger.warning('Rebuild them from the current directives with --force-parameterization.')
640
+ logger.warning('This is expected for entries written before htpolynet 2.3, and is not a')
641
+ logger.warning('failure. Rebuild them with --force-parameterization if this build\'s charges')
642
+ logger.warning('need to be certain.')
634
643
 
635
644
  @staticmethod
636
645
  def _checkin_parameterization(mname, overwrite):
@@ -687,9 +696,11 @@ class Runtime:
687
696
  pfs.checkout(keyfile)
688
697
  stored = paramcache.read_key(M.name)
689
698
  if stored is None:
690
- logger.warning(f'Cached parameterization for {M.name} carries no record of how it was built, '
691
- f'so it cannot be checked against the requested {requested["charge_method"]!r} '
692
- f'charge method; use --force-parameterization to rebuild it')
699
+ logger.warning(f'Reusing cached parameterization for {M.name}, which carries no record of '
700
+ f'how it was built: this build takes whatever charges that entry holds, and '
701
+ f'they may not be {requested["charge_method"]!r}. Expected for an entry '
702
+ f'written before htpolynet 2.3; rebuild it with --force-parameterization '
703
+ f'if this build\'s charges need to be certain.')
693
704
  self.unverified_parameterizations.append(M.name)
694
705
  return []
695
706
  diffs = paramcache.describe_mismatch(stored, requested)
@@ -90,8 +90,15 @@ def _mdrun_cmd(base):
90
90
 
91
91
  def _get_git_commit():
92
92
  """Records the short git commit hash of the htpolynet source tree and
93
- whether there are uncommitted changes. Falls back to 'unknown' if the
94
- working directory is not inside a git repo or git is not available.
93
+ whether there are uncommitted changes.
94
+
95
+ An installed copy -- pip, conda, or the published container -- has no
96
+ ``.git`` beside it, so the git lookup fails and there is nothing to
97
+ report. Reporting 'unknown' there leaves a build with no way to answer
98
+ "what code produced this" from inside itself, which is exactly the
99
+ question the parameterization records exist to answer for molecules.
100
+ Fall back to the installed distribution version, which for a released
101
+ install maps to a tag and is the honest answer.
95
102
  """
96
103
  global git_commit
97
104
  src = os.path.dirname(__file__)
@@ -108,6 +115,15 @@ def _get_git_commit():
108
115
  )
109
116
  if dirty.returncode == 0 and dirty.stdout.strip():
110
117
  git_commit += ' (uncommitted changes present)'
118
+ return
119
+ except Exception:
120
+ pass
121
+ try:
122
+ from importlib.metadata import version, PackageNotFoundError
123
+ try:
124
+ git_commit = f'not a git checkout (installed version {version("htpolynet")})'
125
+ except PackageNotFoundError:
126
+ pass
111
127
  except Exception:
112
128
  pass
113
129
 
@@ -181,6 +181,11 @@ class TestCachedParameterizationMismatch:
181
181
  assert diffs == []
182
182
  assert 'TAZ' in caplog.text
183
183
  assert 'force-parameterization' in caplog.text
184
+ # The message must say the cached values are being USED, not merely
185
+ # that they could not be checked: correct behavior here is otherwise
186
+ # indistinguishable from the bug it replaced.
187
+ assert 'Reusing cached parameterization' in caplog.text
188
+ assert 'may not be' in caplog.text
184
189
  assert runtime.unverified_parameterizations == ['TAZ'], \
185
190
  'an unverifiable entry must also reach the stage-end summary'
186
191
 
@@ -212,7 +217,7 @@ class TestCachedParameterizationMismatch:
212
217
  paramcache.write_key('TAZ', paramcache.build_key({'charge_method': 'bcc'}))
213
218
  with caplog.at_level('WARNING'):
214
219
  assert _mismatch('TAZ', charge_method='bcc') == []
215
- assert 'no record of how it was built' in caplog.text
220
+ assert 'carries no record of how it was built' in caplog.text
216
221
 
217
222
 
218
223
  class TestCheckinParameterization:
@@ -311,16 +316,27 @@ class TestUnverifiedSummary:
311
316
 
312
317
  def test_states_the_count_and_the_requested_charge_method(self, caplog):
313
318
  text = self._report(['TAZ', 'BAF'], caplog, charge_method='bcc')
314
- assert '2 parameterizations of unverified provenance' in text
319
+ assert '2 parameterizations reused without provenance' in text
315
320
  assert "'bcc'" in text
316
321
 
317
322
  def test_singular_for_one_molecule(self, caplog):
318
323
  text = self._report(['TAZ'], caplog, charge_method='bcc')
319
- assert '1 parameterization of unverified provenance' in text
324
+ assert '1 parameterization reused without provenance' in text
325
+
326
+ def test_says_the_build_carries_the_cached_charges(self, caplog):
327
+ # Without this, a reader who upgrades with an existing library, asks
328
+ # for bcc and gets gas numbers concludes the guard is broken.
329
+ text = self._report(['TAZ'], caplog, charge_method='bcc')
330
+ assert 'carries whatever charges those entries hold' in text
331
+ assert 'may not be' in text
332
+
333
+ def test_says_this_is_expected_rather_than_a_failure(self, caplog):
334
+ text = self._report(['TAZ'], caplog, charge_method='bcc')
335
+ assert 'is not a' in text and 'failure' in text
320
336
 
321
337
  def test_says_how_to_resolve_it(self, caplog):
322
338
  assert 'force-parameterization' in self._report(['TAZ'], caplog, charge_method='bcc')
323
339
 
324
340
  def test_a_molecule_reported_twice_is_listed_once(self, caplog):
325
341
  text = self._report(['TAZ', 'TAZ'], caplog, charge_method='bcc')
326
- assert '1 parameterization of unverified provenance' in text
342
+ assert '1 parameterization reused without provenance' in text
@@ -476,3 +476,54 @@ class TestDirsIntegration(unittest.TestCase):
476
476
  proj_path = os.path.join(self.base, 'p')
477
477
  for d in Dirs.analyze_topdirs:
478
478
  self.assertTrue(os.path.isdir(os.path.join(proj_path, d)))
479
+
480
+
481
+ class TestResolveUserLibrary(unittest.TestCase):
482
+ """`htpolynet postsim` used to die in any directory without a `lib/`.
483
+
484
+ `-lib` defaulted to the string 'lib' for postsim and analyze, but only
485
+ `run` creates that tree, so the default value of a flag the user never
486
+ typed reached UserLibrary and tripped its existence assertion. The
487
+ documented container invocation -- postsim against a staged directory
488
+ holding only finished results -- failed in three seconds.
489
+ """
490
+
491
+ def setUp(self):
492
+ self.tmpdir = tempfile.mkdtemp()
493
+ self.orig = os.getcwd()
494
+ os.chdir(self.tmpdir)
495
+
496
+ def tearDown(self):
497
+ os.chdir(self.orig)
498
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
499
+
500
+ def test_absent_default_resolves_to_no_library(self):
501
+ self.assertIsNone(pfs.resolve_user_library(None))
502
+
503
+ def test_present_default_is_used(self):
504
+ os.makedirs('lib')
505
+ self.assertEqual(pfs.resolve_user_library(None), 'lib')
506
+
507
+ def test_explicit_path_is_returned_even_when_missing(self):
508
+ # A typo'd -lib must fail loudly in UserLibrary rather than silently
509
+ # degrade to no library.
510
+ self.assertEqual(pfs.resolve_user_library('mylib'), 'mylib')
511
+
512
+ def test_explicit_path_still_reaches_the_existence_check(self):
513
+ with self.assertRaises(AssertionError):
514
+ UserLibrary(pfs.resolve_user_library('/this/path/does/not/exist'))
515
+
516
+ def test_resolved_absent_default_never_reaches_the_check(self):
517
+ # The regression itself: with no lib/ present and no -lib given,
518
+ # nothing is constructed, so nothing can assert.
519
+ resolved = pfs.resolve_user_library(None)
520
+ self.assertIsNone(resolved)
521
+ pfs_obj = ProjectFileSystem(root=self.tmpdir, userlibrary=resolved, mock=True)
522
+ self.assertIsNone(pfs_obj.userlibrary)
523
+
524
+ def test_error_message_names_the_flag_and_the_remedy(self):
525
+ with self.assertRaises(AssertionError) as cm:
526
+ UserLibrary('/this/path/does/not/exist')
527
+ msg = str(cm.exception)
528
+ self.assertIn('-lib', msg)
529
+ self.assertIn('/this/path/does/not/exist', msg)
@@ -0,0 +1,61 @@
1
+ """Tests what an htpolynet build can say about the code that produced it.
2
+
3
+ `htpolynet info` reports a git commit, which works from a source checkout and
4
+ does not exist for an installed copy -- pip, conda, or the published
5
+ container, none of which carry a `.git`. Reporting 'unknown' there leaves a
6
+ build unable to answer "what code produced this" from inside itself, which is
7
+ the same question the parameterization records answer for molecules.
8
+
9
+ Author: Cameron F. Abrams <cfa22@drexel.edu>
10
+ """
11
+ import subprocess
12
+
13
+ import pytest
14
+
15
+ import htpolynet.external.software as software
16
+
17
+
18
+ @pytest.fixture(autouse=True)
19
+ def restore_git_commit():
20
+ """software.git_commit is module state; put it back afterwards."""
21
+ original = software.git_commit
22
+ yield
23
+ software.git_commit = original
24
+
25
+
26
+ def _no_git(monkeypatch):
27
+ """Makes the git lookup fail the way it does outside a checkout."""
28
+ def boom(*a, **kw):
29
+ raise FileNotFoundError('git')
30
+ monkeypatch.setattr(subprocess, 'run', boom)
31
+
32
+
33
+ class TestGitCommitReporting:
34
+
35
+ def test_a_checkout_reports_a_commit(self):
36
+ software.git_commit = 'unset'
37
+ software._get_git_commit()
38
+ # The test suite runs inside the repo, so this is a real lookup.
39
+ assert software.git_commit != 'unset'
40
+ assert 'unknown' not in software.git_commit
41
+
42
+ def test_an_installed_copy_reports_its_version(self, monkeypatch):
43
+ _no_git(monkeypatch)
44
+ software.git_commit = 'unset'
45
+ software._get_git_commit()
46
+ assert 'installed version' in software.git_commit
47
+ assert 'not a git checkout' in software.git_commit
48
+
49
+ def test_an_installed_copy_never_reports_unknown(self, monkeypatch):
50
+ # The container case 8a hit: htpolynet info said 'unknown', so a build
51
+ # could not identify its own code.
52
+ _no_git(monkeypatch)
53
+ software.git_commit = 'unknown'
54
+ software._get_git_commit()
55
+ assert software.git_commit != 'unknown'
56
+
57
+ def test_the_lookup_never_raises(self, monkeypatch):
58
+ # Provenance reporting must not be able to abort a build.
59
+ _no_git(monkeypatch)
60
+ monkeypatch.setattr(software, '__name__', software.__name__)
61
+ software._get_git_commit()
File without changes