openpkflow 2.3.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. openpkflow-2.5.0/.dockerignore +11 -0
  2. {openpkflow-2.3.0 → openpkflow-2.5.0}/.github/workflows/ci.yml +11 -8
  3. {openpkflow-2.3.0 → openpkflow-2.5.0}/.github/workflows/docs.yml +2 -2
  4. {openpkflow-2.3.0 → openpkflow-2.5.0}/.github/workflows/publish.yml +5 -5
  5. openpkflow-2.5.0/.github/workflows/slow-validation.yml +26 -0
  6. {openpkflow-2.3.0 → openpkflow-2.5.0}/.gitignore +5 -0
  7. openpkflow-2.3.0/CLAUDE.md → openpkflow-2.5.0/AGENTS.md +11 -3
  8. {openpkflow-2.3.0 → openpkflow-2.5.0}/CHANGELOG.md +43 -0
  9. openpkflow-2.5.0/CLAUDE.md +383 -0
  10. openpkflow-2.5.0/Dockerfile +25 -0
  11. {openpkflow-2.3.0 → openpkflow-2.5.0}/FUTURE_PLANS.md +48 -3
  12. openpkflow-2.5.0/HANDOFF.md +264 -0
  13. {openpkflow-2.3.0 → openpkflow-2.5.0}/PKG-INFO +54 -32
  14. {openpkflow-2.3.0 → openpkflow-2.5.0}/README.md +52 -31
  15. openpkflow-2.5.0/RELEASE.md +48 -0
  16. {openpkflow-2.3.0 → openpkflow-2.5.0}/ROADMAP.md +34 -7
  17. {openpkflow-2.3.0 → openpkflow-2.5.0}/VALIDATION.md +26 -0
  18. openpkflow-2.5.0/api/README.md +41 -0
  19. openpkflow-2.5.0/api/app/__init__.py +1 -0
  20. openpkflow-2.5.0/api/app/config.py +23 -0
  21. openpkflow-2.5.0/api/app/deps.py +40 -0
  22. openpkflow-2.5.0/api/app/main.py +48 -0
  23. openpkflow-2.5.0/api/app/routers/__init__.py +1 -0
  24. openpkflow-2.5.0/api/app/routers/be.py +57 -0
  25. openpkflow-2.5.0/api/app/routers/dissolution.py +79 -0
  26. openpkflow-2.5.0/api/app/routers/ivivc.py +47 -0
  27. openpkflow-2.5.0/api/app/routers/nca.py +59 -0
  28. openpkflow-2.5.0/api/app/routers/sim.py +67 -0
  29. openpkflow-2.5.0/api/app/schemas/__init__.py +1 -0
  30. openpkflow-2.5.0/api/app/schemas/be.py +40 -0
  31. openpkflow-2.5.0/api/app/schemas/dissolution.py +30 -0
  32. openpkflow-2.5.0/api/app/schemas/ivivc.py +41 -0
  33. openpkflow-2.5.0/api/app/schemas/nca.py +40 -0
  34. openpkflow-2.5.0/api/app/schemas/sim.py +57 -0
  35. openpkflow-2.5.0/api/app/services/__init__.py +1 -0
  36. openpkflow-2.5.0/api/app/services/be_service.py +79 -0
  37. openpkflow-2.5.0/api/app/services/dissolution_service.py +70 -0
  38. openpkflow-2.5.0/api/app/services/ivivc_service.py +84 -0
  39. openpkflow-2.5.0/api/app/services/nca_service.py +99 -0
  40. openpkflow-2.5.0/api/app/services/sim_service.py +57 -0
  41. openpkflow-2.5.0/api/requirements.txt +6 -0
  42. openpkflow-2.5.0/api/tests/__init__.py +1 -0
  43. openpkflow-2.5.0/api/tests/conftest.py +26 -0
  44. openpkflow-2.5.0/api/tests/test_dissolution.py +95 -0
  45. openpkflow-2.5.0/api/tests/test_nca.py +83 -0
  46. openpkflow-2.5.0/api/tests/test_sim.py +79 -0
  47. openpkflow-2.5.0/docker-compose.yml +16 -0
  48. openpkflow-2.5.0/docs/changelog.md +205 -0
  49. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/index.md +29 -3
  50. openpkflow-2.5.0/docs/migration-cheatsheet.md +196 -0
  51. openpkflow-2.5.0/docs/openfit/CLAUDE.md +402 -0
  52. openpkflow-2.5.0/docs/openfit/OPENASSAYFLOW_CLAUDE.md +228 -0
  53. openpkflow-2.5.0/docs/openfit/ROADMAP.md +230 -0
  54. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/be.md +53 -3
  55. openpkflow-2.5.0/docs/theory.md +482 -0
  56. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/tutorials/bayes.md +4 -0
  57. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/tutorials/be.md +39 -4
  58. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/tutorials/dissolution.md +5 -0
  59. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/tutorials/ivivc.md +5 -0
  60. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/tutorials/nca.md +5 -0
  61. openpkflow-2.5.0/docs/tutorials/pop.md +225 -0
  62. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/tutorials/sim.md +5 -0
  63. openpkflow-2.5.0/docs/validation-matrix.md +46 -0
  64. openpkflow-2.5.0/examples/replicate_be_partial.csv +10 -0
  65. {openpkflow-2.3.0 → openpkflow-2.5.0}/mkdocs.yml +8 -1
  66. openpkflow-2.5.0/progress_web_app.md +96 -0
  67. {openpkflow-2.3.0 → openpkflow-2.5.0}/pyproject.toml +8 -2
  68. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/conda-forge/meta.yaml +9 -5
  69. openpkflow-2.5.0/scripts/crossval_winnonlin.py +252 -0
  70. openpkflow-2.5.0/scripts/powertost_crossval.R +95 -0
  71. openpkflow-2.5.0/scripts/release_readiness.py +78 -0
  72. openpkflow-2.5.0/scripts/replicate_be_crossval.R +154 -0
  73. openpkflow-2.5.0/site/app/index.html +3685 -0
  74. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/__init__.py +1 -1
  75. openpkflow-2.5.0/src/openpkflow/be/__init__.py +28 -0
  76. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/be/methods.py +152 -2
  77. openpkflow-2.5.0/src/openpkflow/be/replicate.py +311 -0
  78. openpkflow-2.5.0/src/openpkflow/be/reporting.py +289 -0
  79. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/cli.py +232 -0
  80. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/models.py +20 -3
  81. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/study.py +2 -2
  82. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/nca/__init__.py +2 -0
  83. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/nca/methods.py +63 -0
  84. openpkflow-2.5.0/src/openpkflow/student/__init__.py +36 -0
  85. openpkflow-2.5.0/src/openpkflow/student/dissolution.py +506 -0
  86. openpkflow-2.5.0/src/openpkflow/student/nca.py +404 -0
  87. openpkflow-2.5.0/src/openpkflow/student/sim.py +492 -0
  88. openpkflow-2.5.0/tests/be/test_methods_hypothesis.py +112 -0
  89. openpkflow-2.5.0/tests/be/test_replicate.py +150 -0
  90. openpkflow-2.5.0/tests/dissolution/test_bootstrap_hypothesis.py +68 -0
  91. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_models.py +1 -1
  92. openpkflow-2.5.0/tests/dissolution/test_similarity_hypothesis.py +107 -0
  93. openpkflow-2.5.0/tests/ivivc/test_methods_hypothesis.py +71 -0
  94. openpkflow-2.5.0/tests/nca/test_methods_hypothesis.py +621 -0
  95. openpkflow-2.5.0/tests/sim/test_dosing_hypothesis.py +153 -0
  96. openpkflow-2.5.0/tests/sim/test_methods_hypothesis.py +313 -0
  97. openpkflow-2.5.0/tests/sim/test_models_hypothesis.py +118 -0
  98. openpkflow-2.5.0/tests/sim/test_simulate_hypothesis.py +133 -0
  99. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/test_cli.py +72 -0
  100. openpkflow-2.5.0/tests/test_student.py +352 -0
  101. openpkflow-2.5.0/tests/validation/test_be_power_reference.py +199 -0
  102. openpkflow-2.5.0/tests/validation/test_be_replicate_reference.py +133 -0
  103. openpkflow-2.5.0/tests/validation/test_nca_winnonlin_reference.py +788 -0
  104. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_pop_foce_reference.py +2 -0
  105. openpkflow-2.5.0/webapp/.gitignore +24 -0
  106. openpkflow-2.5.0/webapp/README.md +33 -0
  107. openpkflow-2.5.0/webapp/eslint.config.js +22 -0
  108. openpkflow-2.5.0/webapp/index.html +13 -0
  109. openpkflow-2.5.0/webapp/package-lock.json +3679 -0
  110. openpkflow-2.5.0/webapp/package.json +43 -0
  111. openpkflow-2.5.0/webapp/playwright.config.ts +23 -0
  112. openpkflow-2.5.0/webapp/public/favicon.svg +1 -0
  113. openpkflow-2.5.0/webapp/public/icons.svg +24 -0
  114. openpkflow-2.5.0/webapp/src/App.tsx +45 -0
  115. openpkflow-2.5.0/webapp/src/components/layout/AppShell.tsx +25 -0
  116. openpkflow-2.5.0/webapp/src/components/layout/ErrorBoundary.tsx +45 -0
  117. openpkflow-2.5.0/webapp/src/components/layout/PageLoader.tsx +12 -0
  118. openpkflow-2.5.0/webapp/src/components/layout/Sidebar.tsx +82 -0
  119. openpkflow-2.5.0/webapp/src/components/layout/ThemeProvider.tsx +19 -0
  120. openpkflow-2.5.0/webapp/src/components/layout/ThemeToggle.tsx +22 -0
  121. openpkflow-2.5.0/webapp/src/components/layout/TopBar.tsx +66 -0
  122. openpkflow-2.5.0/webapp/src/components/layout/theme-context.ts +16 -0
  123. openpkflow-2.5.0/webapp/src/components/shared/AnalysisShell.tsx +81 -0
  124. openpkflow-2.5.0/webapp/src/components/shared/ColumnMapper.tsx +42 -0
  125. openpkflow-2.5.0/webapp/src/components/shared/Disclaimer.tsx +10 -0
  126. openpkflow-2.5.0/webapp/src/components/shared/DownloadReportButton.tsx +175 -0
  127. openpkflow-2.5.0/webapp/src/components/shared/ErrorBanner.tsx +32 -0
  128. openpkflow-2.5.0/webapp/src/components/shared/FileDropzone.tsx +134 -0
  129. openpkflow-2.5.0/webapp/src/components/shared/MetricCard.tsx +39 -0
  130. openpkflow-2.5.0/webapp/src/components/shared/PKChart.tsx +218 -0
  131. openpkflow-2.5.0/webapp/src/components/shared/PasteDataGrid.tsx +297 -0
  132. openpkflow-2.5.0/webapp/src/components/ui/Badge.tsx +30 -0
  133. openpkflow-2.5.0/webapp/src/components/ui/Button.tsx +42 -0
  134. openpkflow-2.5.0/webapp/src/components/ui/Card.tsx +6 -0
  135. openpkflow-2.5.0/webapp/src/components/ui/Input.tsx +16 -0
  136. openpkflow-2.5.0/webapp/src/components/ui/SegmentedControl.tsx +44 -0
  137. openpkflow-2.5.0/webapp/src/components/ui/Select.tsx +21 -0
  138. openpkflow-2.5.0/webapp/src/components/ui/Skeleton.tsx +5 -0
  139. openpkflow-2.5.0/webapp/src/index.css +249 -0
  140. openpkflow-2.5.0/webapp/src/lib/api.ts +199 -0
  141. openpkflow-2.5.0/webapp/src/lib/gridCsv.ts +18 -0
  142. openpkflow-2.5.0/webapp/src/lib/types.ts +95 -0
  143. openpkflow-2.5.0/webapp/src/lib/utils.ts +6 -0
  144. openpkflow-2.5.0/webapp/src/main.tsx +10 -0
  145. openpkflow-2.5.0/webapp/src/pages/BePage.tsx +512 -0
  146. openpkflow-2.5.0/webapp/src/pages/DissolutionPage.tsx +348 -0
  147. openpkflow-2.5.0/webapp/src/pages/Home.tsx +156 -0
  148. openpkflow-2.5.0/webapp/src/pages/IvIvcPage.tsx +388 -0
  149. openpkflow-2.5.0/webapp/src/pages/NcaPage.tsx +486 -0
  150. openpkflow-2.5.0/webapp/src/pages/NotFound.tsx +20 -0
  151. openpkflow-2.5.0/webapp/src/pages/SimPage.tsx +503 -0
  152. openpkflow-2.5.0/webapp/tests/paste-run.spec.ts +144 -0
  153. openpkflow-2.5.0/webapp/tsconfig.app.json +28 -0
  154. openpkflow-2.5.0/webapp/tsconfig.json +7 -0
  155. openpkflow-2.5.0/webapp/tsconfig.node.json +24 -0
  156. openpkflow-2.5.0/webapp/vite.config.ts +17 -0
  157. openpkflow-2.5.0/wrangler.toml +6 -0
  158. openpkflow-2.3.0/HANDOFF.md +0 -135
  159. openpkflow-2.3.0/docs/tutorials/pop.md +0 -100
  160. openpkflow-2.3.0/src/openpkflow/be/__init__.py +0 -14
  161. openpkflow-2.3.0/src/openpkflow/be/reporting.py +0 -133
  162. {openpkflow-2.3.0 → openpkflow-2.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  163. {openpkflow-2.3.0 → openpkflow-2.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  164. {openpkflow-2.3.0 → openpkflow-2.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  165. {openpkflow-2.3.0 → openpkflow-2.5.0}/.github/dependabot.yml +0 -0
  166. {openpkflow-2.3.0 → openpkflow-2.5.0}/.pre-commit-config.yaml +0 -0
  167. {openpkflow-2.3.0 → openpkflow-2.5.0}/CITATION.cff +0 -0
  168. {openpkflow-2.3.0 → openpkflow-2.5.0}/CODE_OF_CONDUCT.md +0 -0
  169. {openpkflow-2.3.0 → openpkflow-2.5.0}/CONTRIBUTING.md +0 -0
  170. {openpkflow-2.3.0 → openpkflow-2.5.0}/LICENSE +0 -0
  171. {openpkflow-2.3.0 → openpkflow-2.5.0}/SECURITY.md +0 -0
  172. {openpkflow-2.3.0 → openpkflow-2.5.0}/V2_ARCHITECTURE_DECISION.md +0 -0
  173. {openpkflow-2.3.0 → openpkflow-2.5.0}/codecov.yml +0 -0
  174. {openpkflow-2.3.0 → openpkflow-2.5.0}/demo.ipynb +0 -0
  175. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/logo.png +0 -0
  176. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/bayes.md +0 -0
  177. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/dissolution.md +0 -0
  178. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/ivivc.md +0 -0
  179. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/ml.md +0 -0
  180. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/nca.md +0 -0
  181. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/pop.md +0 -0
  182. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/sim.md +0 -0
  183. {openpkflow-2.3.0 → openpkflow-2.5.0}/docs/reference/validation.md +0 -0
  184. {openpkflow-2.3.0 → openpkflow-2.5.0}/examples/be_report.html +0 -0
  185. {openpkflow-2.3.0 → openpkflow-2.5.0}/examples/dissolution_advanced.py +0 -0
  186. {openpkflow-2.3.0 → openpkflow-2.5.0}/examples/dissolution_basic.py +0 -0
  187. {openpkflow-2.3.0 → openpkflow-2.5.0}/examples/dissolution_comparison.html +0 -0
  188. {openpkflow-2.3.0 → openpkflow-2.5.0}/examples/ivivc_report.md +0 -0
  189. {openpkflow-2.3.0 → openpkflow-2.5.0}/examples/openpkflow_tour.ipynb +0 -0
  190. {openpkflow-2.3.0 → openpkflow-2.5.0}/examples/report_dissolution.html +0 -0
  191. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/bootf2_dissolution_crossval.R +0 -0
  192. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/dissolution_models_crossval.R +0 -0
  193. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/ivivc_wn_lr_crossval.R +0 -0
  194. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/nlmixr2_popk_crossval.R +0 -0
  195. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/noncompart_theoph_crossval.R +0 -0
  196. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/pknca_ss_crossval.R +0 -0
  197. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/pknca_theoph_crossval.R +0 -0
  198. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/pknca_theoph_crossval_extended.R +0 -0
  199. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/probe_bootf2.R +0 -0
  200. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/probe_foce_i.py +0 -0
  201. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/probe_noncompart.R +0 -0
  202. {openpkflow-2.3.0 → openpkflow-2.5.0}/scripts/urine_nca_crossval.R +0 -0
  203. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/bayes/__init__.py +0 -0
  204. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/bayes/bayes_be.py +0 -0
  205. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/bayes/bayes_pk.py +0 -0
  206. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/bayes/map_pk.py +0 -0
  207. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/bayes/priors.py +0 -0
  208. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/bayes/reporting.py +0 -0
  209. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/bayes/results.py +0 -0
  210. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/be/results.py +0 -0
  211. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/be/study.py +0 -0
  212. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/datasets/__init__.py +0 -0
  213. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/datasets/example_dissolution.csv +0 -0
  214. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/datasets/example_not_similar.csv +0 -0
  215. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/datasets/example_similar.csv +0 -0
  216. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/datasets/ss_crossval.csv +0 -0
  217. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/datasets/theoph.csv +0 -0
  218. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/__init__.py +0 -0
  219. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/bootstrap.py +0 -0
  220. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/loader.py +0 -0
  221. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/multi_media.py +0 -0
  222. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/plotting.py +0 -0
  223. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/reporting.py +0 -0
  224. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/similarity.py +0 -0
  225. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/__init__.py +0 -0
  226. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/methods.py +0 -0
  227. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/reporting.py +0 -0
  228. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/results.py +0 -0
  229. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/study.py +0 -0
  230. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/ml/__init__.py +0 -0
  231. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/ml/surrogate.py +0 -0
  232. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/nca/loader.py +0 -0
  233. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/nca/reporting.py +0 -0
  234. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/nca/results.py +0 -0
  235. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/nca/sparse.py +0 -0
  236. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/nca/study.py +0 -0
  237. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/__init__.py +0 -0
  238. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/dataset.py +0 -0
  239. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/__init__.py +0 -0
  240. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/diagnostics.py +0 -0
  241. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/foce_i.py +0 -0
  242. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/foce_inner.py +0 -0
  243. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/model.py +0 -0
  244. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/objective.py +0 -0
  245. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/omega.py +0 -0
  246. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/plotting.py +0 -0
  247. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/reporting.py +0 -0
  248. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/result.py +0 -0
  249. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/saem.py +0 -0
  250. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/saem_kernel.py +0 -0
  251. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/gof.py +0 -0
  252. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/plotting.py +0 -0
  253. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/reporting.py +0 -0
  254. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/pop/vpc.py +0 -0
  255. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/py.typed +0 -0
  256. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/__init__.py +0 -0
  257. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/docx.py +0 -0
  258. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/html.py +0 -0
  259. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/pdf.py +0 -0
  260. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/bayes_be_report.html +0 -0
  261. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/be_report.html +0 -0
  262. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/dissolution_report.html +0 -0
  263. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/fit_report.html +0 -0
  264. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/ivivc_report.html +0 -0
  265. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/map_pk_report.html +0 -0
  266. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/multi_media_report.html +0 -0
  267. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/nca_single_report.html +0 -0
  268. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/nca_summary_report.html +0 -0
  269. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/pop_gof_report.html +0 -0
  270. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/pop_vpc_report.html +0 -0
  271. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/sim_report.html +0 -0
  272. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/__init__.py +0 -0
  273. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/dosing.py +0 -0
  274. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/methods.py +0 -0
  275. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/models.py +0 -0
  276. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/plotting.py +0 -0
  277. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/reporting.py +0 -0
  278. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/results.py +0 -0
  279. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/sim/simulate.py +0 -0
  280. {openpkflow-2.3.0 → openpkflow-2.5.0}/src/openpkflow/validation/__init__.py +0 -0
  281. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/__init__.py +0 -0
  282. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/bayes/__init__.py +0 -0
  283. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/bayes/test_bayes_be.py +0 -0
  284. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/bayes/test_map_pk.py +0 -0
  285. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/be/__init__.py +0 -0
  286. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/be/test_methods.py +0 -0
  287. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/be/test_study.py +0 -0
  288. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/__init__.py +0 -0
  289. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_alternatives.py +0 -0
  290. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_bootstrap.py +0 -0
  291. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_excel_loader.py +0 -0
  292. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_m13b.py +0 -0
  293. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_model_comparison.py +0 -0
  294. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_multi_media.py +0 -0
  295. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_similarity.py +0 -0
  296. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/dissolution/test_study.py +0 -0
  297. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/ivivc/test_ivivc.py +0 -0
  298. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/ml/__init__.py +0 -0
  299. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/ml/test_surrogate.py +0 -0
  300. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/__init__.py +0 -0
  301. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_loader.py +0 -0
  302. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_methods.py +0 -0
  303. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_nca_pdf_docx.py +0 -0
  304. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_nca_reporting.py +0 -0
  305. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_nca_results.py +0 -0
  306. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_sparse_nca.py +0 -0
  307. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_steady_state_urine.py +0 -0
  308. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_study.py +0 -0
  309. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/nca/test_theoph_reference.py +0 -0
  310. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/__init__.py +0 -0
  311. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_dataset.py +0 -0
  312. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_estimation_diagnostics.py +0 -0
  313. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_estimation_model.py +0 -0
  314. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_estimation_objective.py +0 -0
  315. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_foce_i.py +0 -0
  316. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_gof.py +0 -0
  317. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_pop_vpc.py +0 -0
  318. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_saem.py +0 -0
  319. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/pop/test_vpc.py +0 -0
  320. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/report/__init__.py +0 -0
  321. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/report/test_docx.py +0 -0
  322. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/report/test_pdf.py +0 -0
  323. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/sim/__init__.py +0 -0
  324. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/sim/test_methods.py +0 -0
  325. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/sim/test_roundtrip_nca.py +0 -0
  326. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/sim/test_sim_models.py +0 -0
  327. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/sim/test_simulate.py +0 -0
  328. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/test_benchmark.py +0 -0
  329. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/__init__.py +0 -0
  330. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_dissolution_bootf2_reference.py +0 -0
  331. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_dissolution_models_reference.py +0 -0
  332. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_ivivc_wn_lr_reference.py +0 -0
  333. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_nca_noncompart_reference.py +0 -0
  334. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_nca_ss_reference.py +0 -0
  335. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_nca_theoph_reference.py +0 -0
  336. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_nca_urine_reference.py +0 -0
  337. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_nca_validation.py +0 -0
  338. {openpkflow-2.3.0 → openpkflow-2.5.0}/tests/validation/test_sim_validation.py +0 -0
@@ -0,0 +1,11 @@
1
+ *.pyc
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ *.log
7
+ .env
8
+ .ipynb_checkpoints/
9
+ site/
10
+ .vscode/
11
+ .idea/
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ permissions:
10
+ contents: write
11
+
9
12
  jobs:
10
13
  test:
11
14
  name: Python ${{ matrix.python-version }}
@@ -16,10 +19,10 @@ jobs:
16
19
  python-version: ["3.10", "3.11", "3.12"]
17
20
 
18
21
  steps:
19
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@v6
20
23
 
21
24
  - name: Set up Python ${{ matrix.python-version }}
22
- uses: actions/setup-python@v5
25
+ uses: actions/setup-python@v6
23
26
  with:
24
27
  python-version: ${{ matrix.python-version }}
25
28
 
@@ -32,7 +35,7 @@ jobs:
32
35
  run: pytest --tb=short -q --cov=openpkflow --cov-report=xml
33
36
 
34
37
  - name: Upload coverage to Codecov
35
- uses: codecov/codecov-action@v5
38
+ uses: codecov/codecov-action@v6
36
39
  with:
37
40
  files: coverage.xml
38
41
  flags: ${{ matrix.python-version }}
@@ -46,9 +49,9 @@ jobs:
46
49
  runs-on: ubuntu-latest
47
50
 
48
51
  steps:
49
- - uses: actions/checkout@v4
52
+ - uses: actions/checkout@v6
50
53
 
51
- - uses: actions/setup-python@v5
54
+ - uses: actions/setup-python@v6
52
55
  with:
53
56
  python-version: "3.12"
54
57
 
@@ -61,9 +64,9 @@ jobs:
61
64
  if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false)
62
65
 
63
66
  steps:
64
- - uses: actions/checkout@v4
67
+ - uses: actions/checkout@v6
65
68
 
66
- - uses: actions/setup-python@v5
69
+ - uses: actions/setup-python@v6
67
70
  with:
68
71
  python-version: "3.12"
69
72
 
@@ -81,7 +84,7 @@ jobs:
81
84
  --benchmark-columns=min,mean,stddev,rounds,iterations
82
85
 
83
86
  - name: Upload benchmark results
84
- uses: actions/upload-artifact@v4
87
+ uses: actions/upload-artifact@v7
85
88
  with:
86
89
  name: benchmark-results-${{ github.sha }}
87
90
  path: benchmark.json
@@ -18,9 +18,9 @@ jobs:
18
18
  runs-on: ubuntu-latest
19
19
 
20
20
  steps:
21
- - uses: actions/checkout@v4
21
+ - uses: actions/checkout@v6
22
22
 
23
- - uses: actions/setup-python@v5
23
+ - uses: actions/setup-python@v6
24
24
  with:
25
25
  python-version: "3.12"
26
26
 
@@ -14,10 +14,10 @@ jobs:
14
14
  runs-on: ubuntu-latest
15
15
 
16
16
  steps:
17
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v6
18
18
 
19
19
  - name: Set up Python
20
- uses: actions/setup-python@v5
20
+ uses: actions/setup-python@v6
21
21
  with:
22
22
  python-version: "3.12"
23
23
 
@@ -31,7 +31,7 @@ jobs:
31
31
  run: python -m twine check dist/*
32
32
 
33
33
  - name: Upload build artifacts
34
- uses: actions/upload-artifact@v4
34
+ uses: actions/upload-artifact@v7
35
35
  with:
36
36
  name: dist
37
37
  path: dist/
@@ -47,7 +47,7 @@ jobs:
47
47
 
48
48
  steps:
49
49
  - name: Download build artifacts
50
- uses: actions/download-artifact@v4
50
+ uses: actions/download-artifact@v8
51
51
  with:
52
52
  name: dist
53
53
  path: dist/
@@ -68,7 +68,7 @@ jobs:
68
68
 
69
69
  steps:
70
70
  - name: Download build artifacts
71
- uses: actions/download-artifact@v4
71
+ uses: actions/download-artifact@v8
72
72
  with:
73
73
  name: dist
74
74
  path: dist/
@@ -0,0 +1,26 @@
1
+ name: Slow validation
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ schedule:
6
+ - cron: "0 3 * * 0"
7
+
8
+ jobs:
9
+ slow-validation:
10
+ name: Slow validation (Python 3.12)
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+
16
+ - uses: actions/setup-python@v6
17
+ with:
18
+ python-version: "3.12"
19
+
20
+ - name: Install package and dev dependencies
21
+ run: |
22
+ python -m pip install --upgrade pip
23
+ pip install -e ".[dev,reports,ml]"
24
+
25
+ - name: Run slow validation tests
26
+ run: pytest -m slow tests/validation -q
@@ -65,9 +65,14 @@ handout/
65
65
  !src/**/*.html
66
66
  !examples/**/*.html
67
67
  !docs/**
68
+ !webapp/index.html
68
69
 
69
70
  # MkDocs generated site
70
71
  site/
72
+ !site/
73
+ site/*
74
+ !site/app/
75
+ !site/app/index.html
71
76
 
72
77
  # Uploaded images
73
78
  *.png
@@ -1,6 +1,6 @@
1
- # CLAUDE.md
1
+ # AGENTS.md
2
2
 
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
3
+ This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
4
4
 
5
5
  ## Scope and boundary (read this first)
6
6
 
@@ -14,6 +14,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
14
14
  - `report/` — HTML, PDF, DOCX, Markdown
15
15
  - `validation/` — cross-checks against published references
16
16
 
17
+ **Web app layer (same as CLAUDE.md):**
18
+ - `api/` — FastAPI REST adapter. Current routers: nca, dissolution, sim, ivivc, be.
19
+ Adding a new endpoint requires a schema (schemas/), service (services/), router (routers/),
20
+ and registration in main.py. Follow the existing nca router pattern exactly.
21
+ - `webapp/` — React + Vite + Tailwind frontend. Current pages: Home, NCA, Dissolution, Sim,
22
+ IVIVC, BE. See `progress_web_app.md` for the full file map and next candidates.
23
+ - Do NOT add pharmacometric logic to api/ or webapp/. Add to src/openpkflow/ first.
24
+
17
25
  **Out of scope — do not extend (existing code is frozen at v2.3.0):**
18
26
  - `pop/estimation/` — FOCE-I and SAEM exist but must not be extended. Pharmpy and
19
27
  nlmixr2 are validated NLME engines. Bug fixes only. No IOV, no 3-cmt, no covariate
@@ -28,7 +36,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
28
36
  3. The former covariate API in `pop/estimation/` (`CovariateModel`, `apply_covariates`)
29
37
  was a non-functional v2.2.0 skeleton and was removed in v2.3.0. Do not reintroduce
30
38
  covariate estimation without a full external validation plan.
31
- 4. When ROADMAP.md and CLAUDE.md disagree, CLAUDE.md wins. Flag the conflict to the user.
39
+ 4. When ROADMAP.md and AGENTS.md disagree, AGENTS.md wins. Flag the conflict to the user.
32
40
  5. Never use `--no-verify` to bypass pre-commit hooks. Fix the underlying issue instead.
33
41
 
34
42
  ---
@@ -11,6 +11,49 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
11
11
 
12
12
  ---
13
13
 
14
+ ## [2.5.0] - 2026-06-12
15
+
16
+ ### Added
17
+
18
+ - Student-friendly interface (`openpkflow.student`) with three one-call
19
+ functions: `analyze_pk()` for NCA, `fit_dissolution()` for dissolution
20
+ model fitting + f1/f2 comparison, and `fit_pk_model()` for 1/2-compartment
21
+ PK curve fitting.
22
+ - `student/sim.py` compartmental equations delegate to validated
23
+ `sim/methods.py` functions (c_1cmt_oral, c_1cmt_iv_bolus, c_2cmt_oral) --
24
+ no duplicate math.
25
+ - Hixson-Crowell model added to dissolution `_REGISTRY` (now 6 default
26
+ models: zero_order, first_order, higuchi, korsmeyer_peppas, weibull,
27
+ hixson_crowell).
28
+ - External validation of NCA engine against WinNonlin(r) (Kim et al. 2018,
29
+ PMC6989226, Table 3): all 8 parameters within 0.02% for Theoph Subject 8
30
+ (Cmax, Tmax, lambda_z, t1/2, AUClast, AUCinf, CL/F, Vz/F).
31
+
32
+ ### Fixed
33
+
34
+ - Mandated disclaimer text (CLAUDE.md rule 7) applied to all student module
35
+ report summaries.
36
+ - `test_all_default_models_fit` updated to expect 6 default models.
37
+
38
+ ---
39
+
40
+ ## [2.4.0] — 2026-05-30
41
+
42
+ ### Added
43
+
44
+ - Research-grade replicate bioequivalence screening via `replicate_be()`:
45
+ long-format full/partial replicate data parsing, GMR + conventional 90% CI,
46
+ CVwR estimation, EMA-style scaled-limit summaries, and FDA-style RSABE point
47
+ criterion screening. These outputs are explicitly documented as exploratory
48
+ and not a replacement for jurisdiction-specific validated SAS/R workflows.
49
+ - Replicate BE CLI/report workflow: `openpkflow be replicate`, HTML/Markdown
50
+ reports, JSON export, example partial-replicate CSV, and scalar reference
51
+ validation fixtures for the screening calculations.
52
+ - Release-readiness documentation and slow-validation workflow for heavyweight
53
+ reference checks, plus a read-only `scripts/release_readiness.py` checker.
54
+
55
+ ---
56
+
14
57
  ## [2.3.0] — 2026-05-24
15
58
 
16
59
  ### Breaking Changes
@@ -0,0 +1,383 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Scope and boundary (read this first)
6
+
7
+ **In scope — build, extend, polish:**
8
+ - `dissolution/` — f1, f2, MSD, model fitting, multi-media (greenfield moat vs competitors)
9
+ - `nca/` — sparse, steady-state, urinary, CDISC PP (greenfield moat)
10
+ - `ivivc/` — Level A (greenfield moat)
11
+ - `sim/` — analytical compartment models
12
+ - `bayes/` — MAP individual PK (scipy, screening tool, not regulatory primary)
13
+ - `be/` — paired TOST convenience layer + BioEqPy export
14
+ - `report/` — HTML, PDF, DOCX, Markdown
15
+ - `validation/` — cross-checks against published references
16
+
17
+ **Web app layer (ratified 2026-05-31 — see PIVOT_PLAN.md Option A):**
18
+ - `api/` — FastAPI REST adapter. Wraps `openpkflow` public APIs. No pharmacometric math.
19
+ In scope: adding endpoints, fixing bugs in the adapter layer, improving error handling.
20
+ Current routers: nca, dissolution, sim, ivivc, be.
21
+ - `webapp/` — React + Vite + Tailwind frontend. In scope: UI improvements, new pages for
22
+ modules already covered by the backend, bug fixes.
23
+ Current pages: Home, NCA (/nca), Dissolution (/dissolution), Simulation (/sim),
24
+ IVIVC (/ivivc), Bioequivalence (/be).
25
+ - Both dirs are separate from `src/openpkflow/` and do NOT modify the frozen library.
26
+ - Do not add new pharmacometric logic to `api/` or `webapp/`. If a new analysis is needed,
27
+ first add it to the appropriate `src/openpkflow/` module, then expose it in `api/`.
28
+ - See `progress_web_app.md` for the full file map, completed features, and next candidates.
29
+
30
+ **Out of scope — do not extend (existing code is frozen at v2.3.0):**
31
+ - `pop/estimation/` — FOCE-I and SAEM exist but must not be extended. Pharmpy and
32
+ nlmixr2 are validated NLME engines. Bug fixes only. No IOV, no 3-cmt, no covariate
33
+ selection, no iv_infusion route for estimation.
34
+ - RSABE / replicate-design BE — belongs in companion BioEqPy package, not here.
35
+ - WeasyPrint, Streamlit/Gradio GUI (as embedded GUI in the library), CDISC Define.xml, eCTD table formatting.
36
+ Note: The `api/` + `webapp/` web application is a separate layer, not a Streamlit/Gradio embed.
37
+
38
+ **Rules for AI agents:**
39
+ 1. Before adding any feature, verify it is on the in-scope list. If not, ask the user.
40
+ 2. Validation work outranks new features. Do not add a new module when existing
41
+ modules lack NONMEM/PKNCA cross-validation.
42
+ 3. The former covariate API in `pop/estimation/` (`CovariateModel`, `apply_covariates`)
43
+ was a non-functional v2.2.0 skeleton and was removed in v2.3.0. Do not reintroduce
44
+ covariate estimation without a full external validation plan.
45
+ 4. When ROADMAP.md and CLAUDE.md disagree, CLAUDE.md wins. Flag the conflict to the user.
46
+ 5. Never use `--no-verify` to bypass pre-commit hooks. Fix the underlying issue instead.
47
+
48
+ ---
49
+
50
+ ## Identity
51
+
52
+ **Package:** `openpkflow`
53
+ **Author:** Priyam Thakar <priyamthakar1@gmail.com>
54
+ **GitHub:** https://github.com/priyamthakar/openpkflow
55
+ **PyPI target:** `pip install openpkflow`
56
+ **License:** MIT
57
+ **Philosophy:** Transparent, reproducible, open-source Python workflow for dissolution, NCA, PK/PD simulation, and pharmacometric reporting. Does not replace expert regulatory judgement or validated commercial platforms.
58
+
59
+ ---
60
+
61
+ ## Commands
62
+
63
+ ```bash
64
+ # Install in editable mode with dev tools
65
+ pip install -e ".[dev]"
66
+
67
+ # Run all tests (exclude slow MCMC tests)
68
+ pytest --ignore=tests/pop/test_saem.py --ignore=tests/bayes/test_bayes_be.py -k "not MCMC and not mcmc"
69
+
70
+ # Run NCA + validation tests (fast, complete coverage)
71
+ pytest tests/nca/ tests/validation/
72
+
73
+ # Run single module
74
+ pytest tests/nca/test_methods.py
75
+
76
+ # Run with coverage
77
+ pytest --cov=src/openpkflow --cov-report=term-missing
78
+
79
+ # Lint and auto-fix
80
+ ruff check src/ tests/ --fix
81
+ ruff format src/ tests/
82
+
83
+ # Type-check
84
+ mypy src/openpkflow
85
+
86
+ # Build wheel/sdist
87
+ python -m build
88
+
89
+ # PKNCA cross-validation (requires R + PKNCA)
90
+ "C:\Program Files\R\R-4.6.0\bin\Rscript.exe" -e ".libPaths('D:/R-library/4.6'); source('scripts/pknca_theoph_crossval.R')"
91
+
92
+ # CLI
93
+ openpkflow version
94
+ openpkflow similarity --reference "20,40,60,80" --test "21,39,61,79"
95
+ openpkflow dissolution compare data.csv --reference reference --test test --report out.html
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Architecture
101
+
102
+ - **Layout:** `src/` layout (PEP 517/518). Always import from `src/openpkflow/`, never from project root.
103
+ - **Build:** hatchling (`pyproject.toml`)
104
+ - **Python floor:** 3.10+
105
+ - **Core deps:** numpy, pandas, scipy, matplotlib, pydantic, typer, jinja2
106
+ - **Optional deps:** `[reports]` (openpyxl, reportlab, python-docx), `[bayes]` (pymc, arviz, cmdstanpy), `[ml]` (scikit-learn, torch), `[dev]` (pytest, ruff, mypy, build, twine)
107
+ - **Avoid WeasyPrint** — Windows/GTK dependencies are painful. Use ReportLab for PDF.
108
+
109
+ ### Module map
110
+
111
+ ```
112
+ dissolution/ -- f1, f2, bootstrap_f2, model fitting, loader, reporting <- DONE v0.1-v0.2
113
+ nca/ -- AUC, lambda_z, PK parameters, steady-state, urine, tlast <- DONE v0.4.0, v1.3.0
114
+ ivivc/ -- Wagner-Nelson, Loo-Riegelman, convolution, Levy, %PE <- DONE v1.2.0
115
+ sim/ -- analytical compartment models, dosing, superposition <- DONE v0.5.0
116
+ pop/ — GOF plots (4-panel), VPC (simulation-based), dataset ← DONE v0.6.0
117
+ bayes/ — ImportError guards; [bayes] extras wired; PyMC deferred ← v0.8.0 deferred
118
+ ml/ — PKSurrogate (torch MLP, EXPERIMENTAL) ← DONE v0.9.0
119
+ report/ — Markdown, HTML, PDF (ReportLab), Word (python-docx) ← DONE v0.3.0
120
+ datasets/ — example CSVs (dissolution + theoph NCA reference)
121
+ validation/ — reference comparison utilities
122
+ cli.py — Typer CLI entry point
123
+ ```
124
+
125
+ ### NCA module layout
126
+
127
+ ```
128
+ nca/
129
+ __init__.py — exports all public symbols
130
+ methods.py — pure math: auc_linear, auc_log, auc_linear_up_log_down,
131
+ cmax, tmax, lambda_z (BAR² auto + manual), auc_inf_obs,
132
+ auc_percent_extrapolated, clearance_volume_parameters
133
+ _validate_time_conc rejects NaN/Inf, negative conc
134
+ loader.py — load_nca_csv(): CSV load + BLQ handling
135
+ results.py — NCAResult (per-subject), NCASummaryResults dataclasses
136
+ study.py — NCAStudy: from_csv(), analyze() -> NCASummaryResults
137
+ tlast trimming: strips trailing conc <= 0 before AUClast
138
+ reporting.py — report_nca_single(), report_nca_summary() (HTML + Markdown)
139
+ ```
140
+
141
+ ### NCA data flow
142
+
143
+ ```
144
+ CSV file
145
+ -> load_nca_csv() BLQ-handled DataFrame (subject, time, conc, dose, route)
146
+ -> NCAStudy(df, auc_method, blq_method)
147
+ -> study.analyze() per-subject loop:
148
+ 1. tlast trimming: strip trailing conc <= 0 (FDA/EMA)
149
+ 2. AUClast via chosen method (linear/log/linear_up_log_down)
150
+ 3. Cmax, Tmax from full profile
151
+ 4. lambda_z BAR² auto (post-Cmax positive points)
152
+ 5. AUCinf = AUClast + Clast/lambda_z
153
+ 6. CL_F/Vz_F (oral) or CL/Vz (IV)
154
+ -> NCASummaryResults list of NCAResult
155
+ -> summary.to_dataframe() pandas DataFrame
156
+ -> summary.report("out.html") -> report_nca_summary() -> nca_summary_report.html
157
+ -> result.report("sub.html") -> report_nca_single() -> nca_single_report.html
158
+ ```
159
+
160
+ ### Sim module layout
161
+
162
+ ```
163
+ sim/
164
+ __init__.py — exports all public symbols
165
+ methods.py — pure math: c_1cmt_iv_bolus, c_1cmt_iv_infusion, c_1cmt_oral,
166
+ c_2cmt_iv_bolus, c_2cmt_oral, superpose
167
+ dosing.py — Dose, DoseRegimen dataclasses; DoseRegimen.from_repeated()
168
+ models.py — OneCompartmentModel, TwoCompartmentModel (CL/V parameterization)
169
+ simulate.py — simulate(model, regimen, times) -> SimulationResult
170
+ results.py — SimulationResult: times, concs, model, regimen, .summary(), .plot(), .report()
171
+ plotting.py — pk_profile_plot_b64() base64 PNG helper
172
+ reporting.py — report_simulation() dispatcher (HTML + Markdown + PDF + DOCX)
173
+ ```
174
+
175
+ ### Sim data flow
176
+
177
+ ```
178
+ OneCompartmentModel(route, CL, Vz) or TwoCompartmentModel(...)
179
+ + DoseRegimen.from_repeated(amount, route, tau, n_doses)
180
+ + times array
181
+ -> simulate() per-dose analytical superposition (linear systems only)
182
+ -> SimulationResult .times, .concs, .Cmax, .Tmax
183
+ -> result.report("sim.html") -> report_simulation() -> sim_report.html
184
+ ```
185
+
186
+ ### Dissolution data flow
187
+
188
+ ```
189
+ CSV file
190
+ → load_dissolution_csv() # pydantic-validated DataFrame
191
+ → DissolutionStudy.from_csv() # groups by formulation label
192
+ → study.compare(ref, test) # calls get_formulation_means(), then f1/f2
193
+ → ComparisonResult # dataclass: f1_value, f2_value, means, time_points
194
+ → result.summary() # text to stdout
195
+ → result.report("out.html") # → report_dissolution() → render_html_report()
196
+ ```
197
+
198
+ ### Report rendering
199
+
200
+ - HTML template lives at `src/openpkflow/report/templates/dissolution_report.html`
201
+ - Jinja2 renderer is `src/openpkflow/report/html.py` — note: `zip` is manually injected into `env.globals` because Jinja2 does not expose Python builtins
202
+ - Markdown renderer is `src/openpkflow/dissolution/reporting.py`
203
+ - Format is inferred from file extension in `report_dissolution()`
204
+
205
+ ### Windows console constraint
206
+
207
+ All CLI output and docstrings must use ASCII-only characters. Unicode punctuation (em dashes `—`, right arrows `→`, `>=`, `<=`) causes `UnicodeEncodeError` on Windows cp1252 consoles. Use plain ASCII equivalents (`>=`, `->`, `-`).
208
+
209
+ ---
210
+
211
+ ## Validation & Cross-Validation
212
+
213
+ ### PKNCA NCA cross-validation
214
+
215
+ The NCA module is cross-validated against PKNCA 0.12.1 (Denney et al., 2015) on the
216
+ 12-subject R nlme::Theoph theophylline dataset. AUClast matches within 2% relative
217
+ tolerance for every subject. Cmax matches exactly.
218
+
219
+ Run with:
220
+ ```bash
221
+ "C:\Program Files\R\R-4.6.0\bin\Rscript.exe" -e ".libPaths('D:/R-library/4.6'); source('scripts/pknca_theoph_crossval.R')"
222
+ ```
223
+
224
+ The R script outputs a `_PKNCA_REFERENCE` dict that goes into
225
+ `tests/validation/test_nca_theoph_reference.py`.
226
+
227
+ ### Validation test suite
228
+
229
+ - `tests/validation/test_nca_theoph_reference.py` — per-subject PKNCA cross-validation
230
+ - `tests/validation/test_nca_validation.py` — analytical truth recovery (IV bolus, oral)
231
+ - `tests/validation/test_sim_validation.py` — Gibaldi & Perrier analytical solutions
232
+ - `tests/nca/test_methods.py` — edge cases: all-zero, NaN/Inf, trailing zeros, mixed zeros
233
+
234
+ Each test cites a source: paper DOI, FDA guidance ID, or reference implementation.
235
+
236
+ ### Known edge cases tested
237
+
238
+ - All-zero concentrations → AUClast = 0 (no crash)
239
+ - NaN/Inf concentrations → ValueError (not silently propagated)
240
+ - Trailing zero concentrations → trimmed by tlast logic in study.py
241
+ - Single-point profiles → ValueError (need >= 2 for AUC)
242
+ - Empty arrays → ValueError
243
+ - Negative concentrations → ValueError
244
+ - Non-increasing times → ValueError
245
+
246
+ ---
247
+
248
+ ## Current focus
249
+
250
+ v2.3.0 is current — Pop PK FOCE-I validated against nlme reference, covariate skeleton removed.
251
+ Next focus: conda-forge distribution + PowerTOST cross-validation (nice-to-have).
252
+ See `ROADMAP.md` for the full ladder.
253
+
254
+ **Before any new feature:** run `python -m build && python -m twine check dist/*` to confirm the wheel is clean.
255
+
256
+ ---
257
+
258
+ ## Release Ladder
259
+
260
+ ```
261
+ 0.1.0 f1, f2, input validation, CSV loader, CLI, Markdown+HTML report stub, tests DONE
262
+ 0.1.1 bootstrap_f2, profile plots in HTML reports, CI, example datasets, py.typed DONE
263
+ 0.1.2 PyPI publish, Trusted Publishing workflow DONE
264
+ 0.1.3 README polish, f2_method="regulatory" option, CV% warning in compare()
265
+ 0.2.0 dissolution model fitting (Weibull, Korsmeyer-Peppas, Higuchi, first-order,
266
+ zero-order) — scipy curve_fit, AIC/BIC/R2, fit overlay in HTML report
267
+ 0.3.0 full Markdown + HTML + ReportLab PDF report generator
268
+ 0.4.0 NCA engine (AUC, Cmax, Tmax, lambda_z, t1/2, CL/F, Vz/F)
269
+ 0.5.0 PK simulation (1-comp, 2-comp, oral, IV, infusion, repeated dosing) DONE
270
+ 0.6.0 population PK diagnostics, GOF plots, VPC helpers DONE
271
+ 0.7.0 Pharmpy bridge SKIPPED (reserved)
272
+ 0.8.0 Bayesian PK (PyMC, CmdStanPy) DEFERRED (extras wired)
273
+ 0.9.0 ML surrogate (torch MLP, EXPERIMENTAL) DONE
274
+ 1.0.0 stable public release DONE
275
+ 1.1.0 dissolution regulatory toolkit: MSD, model-dependent comparison, RSD check DONE
276
+ 1.2.0 IVIVC Level A: Wagner-Nelson, Loo-Riegelman, convolution predict, %PE DONE
277
+ 1.3.0 NCA expansion: steady-state, urinary excretion, CDISC PP output DONE
278
+ 1.4.0 multi-media dissolution: pH 1.2/4.5/6.8 panel, alcohol dose-dumping DONE
279
+ 1.5.0 Sparse-sampling NCA: model-informed 1-cmt oral from 3-5 data points DONE
280
+ 2.0.0 Bayesian PK: MAP individual estimation + full posterior + Bayesian BE (PyMC) DONE
281
+ 2.1.0 FOCE-I & SAEM population PK (1-cmt, diagonal Omega) DONE
282
+ 2.2.0 2-cmt models, full Omega matrix, covariate skeleton DONE
283
+ 2.3.0 Remove covariate skeleton, FOCE-I nlme cross-validation, conda-forge prep DONE
284
+ ```
285
+
286
+ See `ROADMAP.md` for full milestone detail, scope rationale, and definition of done.
287
+
288
+ ---
289
+
290
+ ## Code Conventions
291
+
292
+ - **Type hints required** on all public API functions and methods.
293
+ - **Docstrings required** on all public functions — use NumPy docstring style.
294
+ - **No comments** unless the WHY is non-obvious (hidden constraint, subtle invariant, workaround).
295
+ - **No multi-paragraph docstrings** — one short description line, then Parameters/Returns/Raises sections only.
296
+ - Line length: 100 characters (ruff).
297
+ - Formatting: ruff (`ruff format`), linting: ruff lint, type-checking: mypy strict.
298
+
299
+ ---
300
+
301
+ ## Pharmacometric Correctness Rules
302
+
303
+ These are load-bearing. Do not violate them.
304
+
305
+ 1. **f1/f2 require matched time points.** Caller supplies aligned `reference` and `test` arrays. The functions do not silently reindex or interpolate. If arrays differ in length, raise `ValueError`.
306
+
307
+ 2. **AUC method must be explicit.** Never silently default. Always require the caller to pass the method name (`"linear"`, `"log"`, `"linear_up_log_down"`).
308
+
309
+ 3. **Apparent vs absolute parameters must be distinguished in output names.** Use `CL_F` for oral apparent clearance, `CL` for IV-derived clearance. Never mix them in the same output without labelling.
310
+
311
+ 4. **BLQ handling must be explicit.** Never silently drop BLQ values. Require the caller to specify the method.
312
+
313
+ 5. **AUClast stops at tlast.** Following FDA/EMA NCA guidance, AUClast integrates from time 0 to tlast — the last time point with a quantifiable (positive) concentration. Trailing zero or negative concentrations must be excluded from the trapezoidal sum. This is enforced in `study.py`.
314
+
315
+ 6. **NaN/Inf must be rejected.** `_validate_time_conc()` in `methods.py` rejects non-finite concentrations and times with explicit `ValueError` messages. Do not allow NaN to propagate silently through AUC calculations.
316
+
317
+ 7. **Disclaimer required in all generated reports:**
318
+ > This report was generated using OpenPKFlow (open-source). Final regulatory interpretation should be reviewed by qualified formulation, pharmacokinetic, and regulatory experts.
319
+
320
+ 8. **Do not copy code from R packages.** You may study R package behavior, formulas, documentation, and reference outputs. Do not copy source code unless the license explicitly allows it.
321
+
322
+ ---
323
+
324
+ ## Validation Discipline (mandatory from day one)
325
+
326
+ Every formula function must have at minimum two test cases:
327
+
328
+ 1. A **degenerate/sanity case** with a hand-checkable answer (e.g., identical input → f2 = 100).
329
+ 2. A **published reference example** with the citation in the test's docstring (paper DOI, FDA guidance ID, or R-package vignette name).
330
+
331
+ Tests must cite the source of the expected value. "I calculated it manually" is not a citation.
332
+
333
+ Known reference values:
334
+ - f2 = 100 when reference == test (by definition)
335
+ - f2 ≈ 50 when profiles differ by ~10 percentage points at each timepoint (FDA 1997 guidance threshold)
336
+ - f1 = 0 when reference == test (by definition)
337
+ - AUClast matches PKNCA 0.12.1 within 2% on all 12 theophylline subjects
338
+ - Cmax matches PKNCA 0.12.1 exactly
339
+
340
+ ---
341
+
342
+ ## Report Format Priority
343
+
344
+ ```
345
+ v0.1.x: console summary → Markdown report → HTML report with embedded profile plot
346
+ v0.2.x: dissolution model fitting results in reports
347
+ v0.3.0: ReportLab PDF export, python-docx Word export
348
+ ```
349
+
350
+ OpenPKFlow is **report-first**: the product delivers clean, professional, regulatory-style reports. Calculation correctness is necessary but not sufficient — the output must be shareable with supervisors, clients, CROs, and regulatory teams.
351
+
352
+ ---
353
+
354
+ ## Git Conventions
355
+
356
+ - Never force-push. Never `--no-verify`. Never amend published commits.
357
+ - Commit message format: `<type>(<scope>): <short description>` (e.g., `feat(dissolution): add f1 and f2 with validation`)
358
+ - Version bumps: update `pyproject.toml` version and `CHANGELOG.md` together in one commit.
359
+ - Tag releases: `git tag v0.1.1`
360
+
361
+ ## PyPI Upload Order
362
+
363
+ 1. tests passing locally
364
+ 2. `pip install -e .` works
365
+ 3. `python -m build` succeeds
366
+ 4. `python -m twine check dist/*` clean
367
+ 5. Upload to TestPyPI: `twine upload --repository testpypi dist/*`
368
+ 6. Fresh venv install: `pip install -i https://test.pypi.org/simple/ openpkflow` — verify `openpkflow version` and `openpkflow similarity` work
369
+ 7. Upload to real PyPI: `twine upload dist/*`
370
+
371
+ **Preferred: PyPI Trusted Publishing** — no stored token, scoped to the repo. Set up at pypi.org/manage/account/publishing/ then add a `publish.yml` GitHub Actions workflow that triggers on version tags. Only the repo owner can configure this — it requires a one-time manual step at pypi.org.
372
+
373
+ Do not upload broken or untested wheels.
374
+
375
+ ---
376
+
377
+ ## Positioning Reminder
378
+
379
+ Use:
380
+ > **A transparent, reproducible, open-source Python workflow for dissolution, NCA, PK/PD simulation, and pharmacometric reporting.**
381
+
382
+ Never say:
383
+ > "FDA-approved", "replaces Certara", "AI discovers the perfect formulation."
@@ -0,0 +1,25 @@
1
+ # Dockerfile for OpenPKFlow with Jupyter and all extras
2
+ # Build: docker build -t openpkflow .
3
+ # Run: docker run -p 8888:8888 -v $(pwd):/workspace openpkflow
4
+
5
+ FROM python:3.12-slim
6
+
7
+ LABEL org.opencontainers.image.title="OpenPKFlow"
8
+ LABEL org.opencontainers.image.description="Python-first pharmacy toolkit for dissolution, NCA, PK/PD simulation, and pharmacometric reporting."
9
+ LABEL org.opencontainers.image.url="https://github.com/priyamthakar/openpkflow"
10
+ LABEL org.opencontainers.image.documentation="https://priyamthakar.github.io/openpkflow/"
11
+
12
+ WORKDIR /workspace
13
+
14
+ RUN apt-get update && apt-get install -y --no-install-recommends \
15
+ gcc g++ make \
16
+ && rm -rf /var/lib/apt/lists/*
17
+
18
+ COPY pyproject.toml README.md ./
19
+ COPY src/ src/
20
+
21
+ RUN pip install --no-cache-dir ".[reports,bayes,ml]" jupyter
22
+
23
+ EXPOSE 8888
24
+
25
+ ENTRYPOINT ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--allow-root", "--no-browser"]