openpkflow 2.2.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 (340) hide show
  1. openpkflow-2.5.0/.dockerignore +11 -0
  2. {openpkflow-2.2.0 → openpkflow-2.5.0}/.github/workflows/ci.yml +11 -8
  3. {openpkflow-2.2.0 → openpkflow-2.5.0}/.github/workflows/docs.yml +2 -2
  4. {openpkflow-2.2.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.2.0 → openpkflow-2.5.0}/.gitignore +5 -0
  7. {openpkflow-2.2.0 → openpkflow-2.5.0}/.pre-commit-config.yaml +1 -0
  8. openpkflow-2.2.0/CLAUDE.md → openpkflow-2.5.0/AGENTS.md +125 -34
  9. {openpkflow-2.2.0 → openpkflow-2.5.0}/CHANGELOG.md +69 -0
  10. openpkflow-2.5.0/CLAUDE.md +383 -0
  11. openpkflow-2.5.0/Dockerfile +25 -0
  12. {openpkflow-2.2.0 → openpkflow-2.5.0}/FUTURE_PLANS.md +48 -3
  13. openpkflow-2.5.0/HANDOFF.md +264 -0
  14. {openpkflow-2.2.0 → openpkflow-2.5.0}/PKG-INFO +62 -35
  15. {openpkflow-2.2.0 → openpkflow-2.5.0}/README.md +60 -34
  16. openpkflow-2.5.0/RELEASE.md +48 -0
  17. {openpkflow-2.2.0 → openpkflow-2.5.0}/ROADMAP.md +55 -10
  18. {openpkflow-2.2.0 → openpkflow-2.5.0}/VALIDATION.md +84 -1
  19. openpkflow-2.5.0/api/README.md +41 -0
  20. openpkflow-2.5.0/api/app/__init__.py +1 -0
  21. openpkflow-2.5.0/api/app/config.py +23 -0
  22. openpkflow-2.5.0/api/app/deps.py +40 -0
  23. openpkflow-2.5.0/api/app/main.py +48 -0
  24. openpkflow-2.5.0/api/app/routers/__init__.py +1 -0
  25. openpkflow-2.5.0/api/app/routers/be.py +57 -0
  26. openpkflow-2.5.0/api/app/routers/dissolution.py +79 -0
  27. openpkflow-2.5.0/api/app/routers/ivivc.py +47 -0
  28. openpkflow-2.5.0/api/app/routers/nca.py +59 -0
  29. openpkflow-2.5.0/api/app/routers/sim.py +67 -0
  30. openpkflow-2.5.0/api/app/schemas/__init__.py +1 -0
  31. openpkflow-2.5.0/api/app/schemas/be.py +40 -0
  32. openpkflow-2.5.0/api/app/schemas/dissolution.py +30 -0
  33. openpkflow-2.5.0/api/app/schemas/ivivc.py +41 -0
  34. openpkflow-2.5.0/api/app/schemas/nca.py +40 -0
  35. openpkflow-2.5.0/api/app/schemas/sim.py +57 -0
  36. openpkflow-2.5.0/api/app/services/__init__.py +1 -0
  37. openpkflow-2.5.0/api/app/services/be_service.py +79 -0
  38. openpkflow-2.5.0/api/app/services/dissolution_service.py +70 -0
  39. openpkflow-2.5.0/api/app/services/ivivc_service.py +84 -0
  40. openpkflow-2.5.0/api/app/services/nca_service.py +99 -0
  41. openpkflow-2.5.0/api/app/services/sim_service.py +57 -0
  42. openpkflow-2.5.0/api/requirements.txt +6 -0
  43. openpkflow-2.5.0/api/tests/__init__.py +1 -0
  44. openpkflow-2.5.0/api/tests/conftest.py +26 -0
  45. openpkflow-2.5.0/api/tests/test_dissolution.py +95 -0
  46. openpkflow-2.5.0/api/tests/test_nca.py +83 -0
  47. openpkflow-2.5.0/api/tests/test_sim.py +79 -0
  48. openpkflow-2.5.0/docker-compose.yml +16 -0
  49. openpkflow-2.5.0/docs/changelog.md +205 -0
  50. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/index.md +29 -3
  51. openpkflow-2.5.0/docs/migration-cheatsheet.md +196 -0
  52. openpkflow-2.5.0/docs/openfit/CLAUDE.md +402 -0
  53. openpkflow-2.5.0/docs/openfit/OPENASSAYFLOW_CLAUDE.md +228 -0
  54. openpkflow-2.5.0/docs/openfit/ROADMAP.md +230 -0
  55. openpkflow-2.5.0/docs/reference/bayes.md +98 -0
  56. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/reference/be.md +53 -3
  57. openpkflow-2.5.0/docs/reference/ivivc.md +66 -0
  58. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/reference/nca.md +27 -0
  59. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/reference/pop.md +9 -2
  60. openpkflow-2.5.0/docs/theory.md +482 -0
  61. openpkflow-2.5.0/docs/tutorials/bayes.md +262 -0
  62. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/tutorials/be.md +39 -4
  63. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/tutorials/dissolution.md +5 -0
  64. openpkflow-2.5.0/docs/tutorials/ivivc.md +197 -0
  65. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/tutorials/nca.md +18 -3
  66. openpkflow-2.5.0/docs/tutorials/pop.md +225 -0
  67. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/tutorials/sim.md +5 -0
  68. openpkflow-2.5.0/docs/validation-matrix.md +46 -0
  69. openpkflow-2.5.0/examples/be_report.html +761 -0
  70. openpkflow-2.5.0/examples/dissolution_comparison.html +362 -0
  71. openpkflow-2.5.0/examples/ivivc_report.md +63 -0
  72. openpkflow-2.5.0/examples/openpkflow_tour.ipynb +2352 -0
  73. openpkflow-2.5.0/examples/replicate_be_partial.csv +10 -0
  74. openpkflow-2.5.0/examples/report_dissolution.html +362 -0
  75. {openpkflow-2.2.0 → openpkflow-2.5.0}/mkdocs.yml +8 -1
  76. openpkflow-2.5.0/progress_web_app.md +96 -0
  77. {openpkflow-2.2.0 → openpkflow-2.5.0}/pyproject.toml +8 -2
  78. openpkflow-2.5.0/scripts/bootf2_dissolution_crossval.R +118 -0
  79. openpkflow-2.5.0/scripts/conda-forge/meta.yaml +64 -0
  80. openpkflow-2.5.0/scripts/crossval_winnonlin.py +252 -0
  81. openpkflow-2.5.0/scripts/dissolution_models_crossval.R +185 -0
  82. openpkflow-2.5.0/scripts/ivivc_wn_lr_crossval.R +171 -0
  83. openpkflow-2.5.0/scripts/nlmixr2_popk_crossval.R +194 -0
  84. openpkflow-2.5.0/scripts/noncompart_theoph_crossval.R +114 -0
  85. openpkflow-2.5.0/scripts/pknca_ss_crossval.R +149 -0
  86. openpkflow-2.5.0/scripts/pknca_theoph_crossval.R +178 -0
  87. openpkflow-2.5.0/scripts/pknca_theoph_crossval_extended.R +171 -0
  88. openpkflow-2.5.0/scripts/powertost_crossval.R +95 -0
  89. openpkflow-2.5.0/scripts/probe_bootf2.R +9 -0
  90. openpkflow-2.5.0/scripts/probe_foce_i.py +102 -0
  91. openpkflow-2.5.0/scripts/probe_noncompart.R +7 -0
  92. openpkflow-2.5.0/scripts/release_readiness.py +78 -0
  93. openpkflow-2.5.0/scripts/replicate_be_crossval.R +154 -0
  94. openpkflow-2.5.0/scripts/urine_nca_crossval.R +220 -0
  95. openpkflow-2.5.0/site/app/index.html +3685 -0
  96. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/__init__.py +1 -1
  97. openpkflow-2.5.0/src/openpkflow/be/__init__.py +28 -0
  98. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/be/methods.py +152 -2
  99. openpkflow-2.5.0/src/openpkflow/be/replicate.py +311 -0
  100. openpkflow-2.5.0/src/openpkflow/be/reporting.py +289 -0
  101. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/cli.py +232 -0
  102. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/datasets/__init__.py +6 -0
  103. openpkflow-2.5.0/src/openpkflow/datasets/ss_crossval.csv +28 -0
  104. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/models.py +20 -3
  105. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/study.py +2 -2
  106. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/nca/__init__.py +2 -0
  107. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/nca/methods.py +80 -2
  108. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/nca/results.py +5 -1
  109. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/nca/study.py +10 -3
  110. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/__init__.py +1 -1
  111. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/foce_i.py +66 -18
  112. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/foce_inner.py +10 -2
  113. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/model.py +14 -53
  114. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/objective.py +1 -3
  115. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/plotting.py +1 -1
  116. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/result.py +53 -49
  117. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/saem.py +111 -30
  118. openpkflow-2.5.0/src/openpkflow/student/__init__.py +36 -0
  119. openpkflow-2.5.0/src/openpkflow/student/dissolution.py +506 -0
  120. openpkflow-2.5.0/src/openpkflow/student/nca.py +404 -0
  121. openpkflow-2.5.0/src/openpkflow/student/sim.py +492 -0
  122. openpkflow-2.5.0/tests/be/test_methods_hypothesis.py +112 -0
  123. openpkflow-2.5.0/tests/be/test_replicate.py +150 -0
  124. openpkflow-2.5.0/tests/dissolution/test_bootstrap_hypothesis.py +68 -0
  125. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_models.py +1 -1
  126. openpkflow-2.5.0/tests/dissolution/test_similarity_hypothesis.py +107 -0
  127. openpkflow-2.5.0/tests/ivivc/test_methods_hypothesis.py +71 -0
  128. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_methods.py +72 -0
  129. openpkflow-2.5.0/tests/nca/test_methods_hypothesis.py +621 -0
  130. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_theoph_reference.py +2 -2
  131. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_estimation_model.py +4 -1
  132. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_foce_i.py +6 -2
  133. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_saem.py +6 -2
  134. openpkflow-2.5.0/tests/sim/test_dosing_hypothesis.py +153 -0
  135. openpkflow-2.5.0/tests/sim/test_methods_hypothesis.py +313 -0
  136. openpkflow-2.5.0/tests/sim/test_models_hypothesis.py +118 -0
  137. openpkflow-2.5.0/tests/sim/test_simulate_hypothesis.py +133 -0
  138. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/test_cli.py +75 -1
  139. openpkflow-2.5.0/tests/test_student.py +352 -0
  140. openpkflow-2.5.0/tests/validation/test_be_power_reference.py +199 -0
  141. openpkflow-2.5.0/tests/validation/test_be_replicate_reference.py +133 -0
  142. openpkflow-2.5.0/tests/validation/test_dissolution_bootf2_reference.py +122 -0
  143. openpkflow-2.5.0/tests/validation/test_dissolution_models_reference.py +319 -0
  144. openpkflow-2.5.0/tests/validation/test_ivivc_wn_lr_reference.py +245 -0
  145. openpkflow-2.5.0/tests/validation/test_nca_noncompart_reference.py +314 -0
  146. openpkflow-2.5.0/tests/validation/test_nca_ss_reference.py +188 -0
  147. openpkflow-2.5.0/tests/validation/test_nca_theoph_reference.py +419 -0
  148. openpkflow-2.5.0/tests/validation/test_nca_urine_reference.py +252 -0
  149. openpkflow-2.5.0/tests/validation/test_nca_winnonlin_reference.py +788 -0
  150. openpkflow-2.5.0/tests/validation/test_pop_foce_reference.py +378 -0
  151. openpkflow-2.5.0/webapp/.gitignore +24 -0
  152. openpkflow-2.5.0/webapp/README.md +33 -0
  153. openpkflow-2.5.0/webapp/eslint.config.js +22 -0
  154. openpkflow-2.5.0/webapp/index.html +13 -0
  155. openpkflow-2.5.0/webapp/package-lock.json +3679 -0
  156. openpkflow-2.5.0/webapp/package.json +43 -0
  157. openpkflow-2.5.0/webapp/playwright.config.ts +23 -0
  158. openpkflow-2.5.0/webapp/public/favicon.svg +1 -0
  159. openpkflow-2.5.0/webapp/public/icons.svg +24 -0
  160. openpkflow-2.5.0/webapp/src/App.tsx +45 -0
  161. openpkflow-2.5.0/webapp/src/components/layout/AppShell.tsx +25 -0
  162. openpkflow-2.5.0/webapp/src/components/layout/ErrorBoundary.tsx +45 -0
  163. openpkflow-2.5.0/webapp/src/components/layout/PageLoader.tsx +12 -0
  164. openpkflow-2.5.0/webapp/src/components/layout/Sidebar.tsx +82 -0
  165. openpkflow-2.5.0/webapp/src/components/layout/ThemeProvider.tsx +19 -0
  166. openpkflow-2.5.0/webapp/src/components/layout/ThemeToggle.tsx +22 -0
  167. openpkflow-2.5.0/webapp/src/components/layout/TopBar.tsx +66 -0
  168. openpkflow-2.5.0/webapp/src/components/layout/theme-context.ts +16 -0
  169. openpkflow-2.5.0/webapp/src/components/shared/AnalysisShell.tsx +81 -0
  170. openpkflow-2.5.0/webapp/src/components/shared/ColumnMapper.tsx +42 -0
  171. openpkflow-2.5.0/webapp/src/components/shared/Disclaimer.tsx +10 -0
  172. openpkflow-2.5.0/webapp/src/components/shared/DownloadReportButton.tsx +175 -0
  173. openpkflow-2.5.0/webapp/src/components/shared/ErrorBanner.tsx +32 -0
  174. openpkflow-2.5.0/webapp/src/components/shared/FileDropzone.tsx +134 -0
  175. openpkflow-2.5.0/webapp/src/components/shared/MetricCard.tsx +39 -0
  176. openpkflow-2.5.0/webapp/src/components/shared/PKChart.tsx +218 -0
  177. openpkflow-2.5.0/webapp/src/components/shared/PasteDataGrid.tsx +297 -0
  178. openpkflow-2.5.0/webapp/src/components/ui/Badge.tsx +30 -0
  179. openpkflow-2.5.0/webapp/src/components/ui/Button.tsx +42 -0
  180. openpkflow-2.5.0/webapp/src/components/ui/Card.tsx +6 -0
  181. openpkflow-2.5.0/webapp/src/components/ui/Input.tsx +16 -0
  182. openpkflow-2.5.0/webapp/src/components/ui/SegmentedControl.tsx +44 -0
  183. openpkflow-2.5.0/webapp/src/components/ui/Select.tsx +21 -0
  184. openpkflow-2.5.0/webapp/src/components/ui/Skeleton.tsx +5 -0
  185. openpkflow-2.5.0/webapp/src/index.css +249 -0
  186. openpkflow-2.5.0/webapp/src/lib/api.ts +199 -0
  187. openpkflow-2.5.0/webapp/src/lib/gridCsv.ts +18 -0
  188. openpkflow-2.5.0/webapp/src/lib/types.ts +95 -0
  189. openpkflow-2.5.0/webapp/src/lib/utils.ts +6 -0
  190. openpkflow-2.5.0/webapp/src/main.tsx +10 -0
  191. openpkflow-2.5.0/webapp/src/pages/BePage.tsx +512 -0
  192. openpkflow-2.5.0/webapp/src/pages/DissolutionPage.tsx +348 -0
  193. openpkflow-2.5.0/webapp/src/pages/Home.tsx +156 -0
  194. openpkflow-2.5.0/webapp/src/pages/IvIvcPage.tsx +388 -0
  195. openpkflow-2.5.0/webapp/src/pages/NcaPage.tsx +486 -0
  196. openpkflow-2.5.0/webapp/src/pages/NotFound.tsx +20 -0
  197. openpkflow-2.5.0/webapp/src/pages/SimPage.tsx +503 -0
  198. openpkflow-2.5.0/webapp/tests/paste-run.spec.ts +144 -0
  199. openpkflow-2.5.0/webapp/tsconfig.app.json +28 -0
  200. openpkflow-2.5.0/webapp/tsconfig.json +7 -0
  201. openpkflow-2.5.0/webapp/tsconfig.node.json +24 -0
  202. openpkflow-2.5.0/webapp/vite.config.ts +17 -0
  203. openpkflow-2.5.0/wrangler.toml +6 -0
  204. openpkflow-2.2.0/HANDOFF.md +0 -204
  205. openpkflow-2.2.0/docs/tutorials/pop.md +0 -100
  206. openpkflow-2.2.0/src/openpkflow/be/__init__.py +0 -14
  207. openpkflow-2.2.0/src/openpkflow/be/reporting.py +0 -133
  208. openpkflow-2.2.0/src/openpkflow/pop/estimation/covariate.py +0 -225
  209. openpkflow-2.2.0/tests/validation/test_nca_theoph_reference.py +0 -278
  210. {openpkflow-2.2.0 → openpkflow-2.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  211. {openpkflow-2.2.0 → openpkflow-2.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  212. {openpkflow-2.2.0 → openpkflow-2.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  213. {openpkflow-2.2.0 → openpkflow-2.5.0}/.github/dependabot.yml +0 -0
  214. {openpkflow-2.2.0 → openpkflow-2.5.0}/CITATION.cff +0 -0
  215. {openpkflow-2.2.0 → openpkflow-2.5.0}/CODE_OF_CONDUCT.md +0 -0
  216. {openpkflow-2.2.0 → openpkflow-2.5.0}/CONTRIBUTING.md +0 -0
  217. {openpkflow-2.2.0 → openpkflow-2.5.0}/LICENSE +0 -0
  218. {openpkflow-2.2.0 → openpkflow-2.5.0}/SECURITY.md +0 -0
  219. {openpkflow-2.2.0 → openpkflow-2.5.0}/V2_ARCHITECTURE_DECISION.md +0 -0
  220. {openpkflow-2.2.0 → openpkflow-2.5.0}/codecov.yml +0 -0
  221. {openpkflow-2.2.0 → openpkflow-2.5.0}/demo.ipynb +0 -0
  222. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/logo.png +0 -0
  223. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/reference/dissolution.md +0 -0
  224. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/reference/ml.md +0 -0
  225. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/reference/sim.md +0 -0
  226. {openpkflow-2.2.0 → openpkflow-2.5.0}/docs/reference/validation.md +0 -0
  227. {openpkflow-2.2.0 → openpkflow-2.5.0}/examples/dissolution_advanced.py +0 -0
  228. {openpkflow-2.2.0 → openpkflow-2.5.0}/examples/dissolution_basic.py +0 -0
  229. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/bayes/__init__.py +0 -0
  230. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/bayes/bayes_be.py +0 -0
  231. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/bayes/bayes_pk.py +0 -0
  232. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/bayes/map_pk.py +0 -0
  233. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/bayes/priors.py +0 -0
  234. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/bayes/reporting.py +0 -0
  235. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/bayes/results.py +0 -0
  236. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/be/results.py +0 -0
  237. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/be/study.py +0 -0
  238. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/datasets/example_dissolution.csv +0 -0
  239. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/datasets/example_not_similar.csv +0 -0
  240. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/datasets/example_similar.csv +0 -0
  241. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/datasets/theoph.csv +0 -0
  242. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/__init__.py +0 -0
  243. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/bootstrap.py +0 -0
  244. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/loader.py +0 -0
  245. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/multi_media.py +0 -0
  246. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/plotting.py +0 -0
  247. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/reporting.py +0 -0
  248. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/dissolution/similarity.py +0 -0
  249. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/__init__.py +0 -0
  250. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/methods.py +0 -0
  251. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/reporting.py +0 -0
  252. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/results.py +0 -0
  253. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/ivivc/study.py +0 -0
  254. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/ml/__init__.py +0 -0
  255. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/ml/surrogate.py +0 -0
  256. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/nca/loader.py +0 -0
  257. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/nca/reporting.py +0 -0
  258. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/nca/sparse.py +0 -0
  259. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/__init__.py +0 -0
  260. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/dataset.py +0 -0
  261. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/diagnostics.py +0 -0
  262. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/omega.py +0 -0
  263. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/reporting.py +0 -0
  264. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/estimation/saem_kernel.py +0 -0
  265. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/gof.py +0 -0
  266. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/plotting.py +0 -0
  267. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/reporting.py +0 -0
  268. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/pop/vpc.py +0 -0
  269. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/py.typed +0 -0
  270. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/__init__.py +0 -0
  271. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/docx.py +0 -0
  272. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/html.py +0 -0
  273. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/pdf.py +0 -0
  274. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/bayes_be_report.html +0 -0
  275. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/be_report.html +0 -0
  276. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/dissolution_report.html +0 -0
  277. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/fit_report.html +0 -0
  278. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/ivivc_report.html +0 -0
  279. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/map_pk_report.html +0 -0
  280. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/multi_media_report.html +0 -0
  281. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/nca_single_report.html +0 -0
  282. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/nca_summary_report.html +0 -0
  283. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/pop_gof_report.html +0 -0
  284. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/pop_vpc_report.html +0 -0
  285. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/report/templates/sim_report.html +0 -0
  286. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/__init__.py +0 -0
  287. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/dosing.py +0 -0
  288. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/methods.py +0 -0
  289. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/models.py +0 -0
  290. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/plotting.py +0 -0
  291. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/reporting.py +0 -0
  292. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/results.py +0 -0
  293. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/sim/simulate.py +0 -0
  294. {openpkflow-2.2.0 → openpkflow-2.5.0}/src/openpkflow/validation/__init__.py +0 -0
  295. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/__init__.py +0 -0
  296. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/bayes/__init__.py +0 -0
  297. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/bayes/test_bayes_be.py +0 -0
  298. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/bayes/test_map_pk.py +0 -0
  299. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/be/__init__.py +0 -0
  300. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/be/test_methods.py +0 -0
  301. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/be/test_study.py +0 -0
  302. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/__init__.py +0 -0
  303. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_alternatives.py +0 -0
  304. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_bootstrap.py +0 -0
  305. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_excel_loader.py +0 -0
  306. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_m13b.py +0 -0
  307. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_model_comparison.py +0 -0
  308. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_multi_media.py +0 -0
  309. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_similarity.py +0 -0
  310. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/dissolution/test_study.py +0 -0
  311. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/ivivc/test_ivivc.py +0 -0
  312. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/ml/__init__.py +0 -0
  313. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/ml/test_surrogate.py +0 -0
  314. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/__init__.py +0 -0
  315. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_loader.py +0 -0
  316. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_nca_pdf_docx.py +0 -0
  317. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_nca_reporting.py +0 -0
  318. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_nca_results.py +0 -0
  319. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_sparse_nca.py +0 -0
  320. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_steady_state_urine.py +0 -0
  321. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/nca/test_study.py +0 -0
  322. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/__init__.py +0 -0
  323. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_dataset.py +0 -0
  324. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_estimation_diagnostics.py +0 -0
  325. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_estimation_objective.py +0 -0
  326. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_gof.py +0 -0
  327. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_pop_vpc.py +0 -0
  328. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/pop/test_vpc.py +0 -0
  329. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/report/__init__.py +0 -0
  330. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/report/test_docx.py +0 -0
  331. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/report/test_pdf.py +0 -0
  332. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/sim/__init__.py +0 -0
  333. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/sim/test_methods.py +0 -0
  334. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/sim/test_roundtrip_nca.py +0 -0
  335. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/sim/test_sim_models.py +0 -0
  336. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/sim/test_simulate.py +0 -0
  337. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/test_benchmark.py +0 -0
  338. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/validation/__init__.py +0 -0
  339. {openpkflow-2.2.0 → openpkflow-2.5.0}/tests/validation/test_nca_validation.py +0 -0
  340. {openpkflow-2.2.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
@@ -10,6 +10,7 @@ repos:
10
10
  rev: v5.0.0
11
11
  hooks:
12
12
  - id: check-yaml
13
+ exclude: ^scripts/conda-forge/
13
14
  - id: check-toml
14
15
  - id: check-merge-conflict
15
16
  - id: end-of-file-fixer
@@ -1,6 +1,45 @@
1
- # CLAUDE.md
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to Codex (Codex.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 (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
+
25
+ **Out of scope — do not extend (existing code is frozen at v2.3.0):**
26
+ - `pop/estimation/` — FOCE-I and SAEM exist but must not be extended. Pharmpy and
27
+ nlmixr2 are validated NLME engines. Bug fixes only. No IOV, no 3-cmt, no covariate
28
+ selection, no iv_infusion route for estimation.
29
+ - RSABE / replicate-design BE — belongs in companion BioEqPy package, not here.
30
+ - WeasyPrint, Streamlit/Gradio GUI, CDISC Define.xml, eCTD table formatting.
31
+
32
+ **Rules for AI agents:**
33
+ 1. Before adding any feature, verify it is on the in-scope list. If not, ask the user.
34
+ 2. Validation work outranks new features. Do not add a new module when existing
35
+ modules lack NONMEM/PKNCA cross-validation.
36
+ 3. The former covariate API in `pop/estimation/` (`CovariateModel`, `apply_covariates`)
37
+ was a non-functional v2.2.0 skeleton and was removed in v2.3.0. Do not reintroduce
38
+ covariate estimation without a full external validation plan.
39
+ 4. When ROADMAP.md and AGENTS.md disagree, AGENTS.md wins. Flag the conflict to the user.
40
+ 5. Never use `--no-verify` to bypass pre-commit hooks. Fix the underlying issue instead.
2
41
 
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
42
+ ---
4
43
 
5
44
  ## Identity
6
45
 
@@ -19,17 +58,17 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
19
58
  # Install in editable mode with dev tools
20
59
  pip install -e ".[dev]"
21
60
 
22
- # Run all tests
23
- pytest
61
+ # Run all tests (exclude slow MCMC tests)
62
+ pytest --ignore=tests/pop/test_saem.py --ignore=tests/bayes/test_bayes_be.py -k "not MCMC and not mcmc"
24
63
 
25
- # Run tests with coverage
26
- pytest --cov=src/openpkflow --cov-report=term-missing
64
+ # Run NCA + validation tests (fast, complete coverage)
65
+ pytest tests/nca/ tests/validation/
27
66
 
28
- # Run a single test file
29
- pytest tests/dissolution/test_similarity.py
67
+ # Run single module
68
+ pytest tests/nca/test_methods.py
30
69
 
31
- # Run a single test by name
32
- pytest tests/dissolution/test_similarity.py::TestF2::test_identical_profiles
70
+ # Run with coverage
71
+ pytest --cov=src/openpkflow --cov-report=term-missing
33
72
 
34
73
  # Lint and auto-fix
35
74
  ruff check src/ tests/ --fix
@@ -41,8 +80,8 @@ mypy src/openpkflow
41
80
  # Build wheel/sdist
42
81
  python -m build
43
82
 
44
- # Verify wheel before upload
45
- python -m twine check dist/*
83
+ # PKNCA cross-validation (requires R + PKNCA)
84
+ "C:\Program Files\R\R-4.6.0\bin\Rscript.exe" -e ".libPaths('D:/R-library/4.6'); source('scripts/pknca_theoph_crossval.R')"
46
85
 
47
86
  # CLI
48
87
  openpkflow version
@@ -65,7 +104,7 @@ openpkflow dissolution compare data.csv --reference reference --test test --repo
65
104
 
66
105
  ```
67
106
  dissolution/ -- f1, f2, bootstrap_f2, model fitting, loader, reporting <- DONE v0.1-v0.2
68
- nca/ -- AUC, lambda_z, PK parameters, steady-state, urine <- DONE v0.4.0, v1.3.0
107
+ nca/ -- AUC, lambda_z, PK parameters, steady-state, urine, tlast <- DONE v0.4.0, v1.3.0
69
108
  ivivc/ -- Wagner-Nelson, Loo-Riegelman, convolution, Levy, %PE <- DONE v1.2.0
70
109
  sim/ -- analytical compartment models, dosing, superposition <- DONE v0.5.0
71
110
  pop/ — GOF plots (4-panel), VPC (simulation-based), dataset ← DONE v0.6.0
@@ -85,12 +124,33 @@ nca/
85
124
  methods.py — pure math: auc_linear, auc_log, auc_linear_up_log_down,
86
125
  cmax, tmax, lambda_z (BAR² auto + manual), auc_inf_obs,
87
126
  auc_percent_extrapolated, clearance_volume_parameters
127
+ _validate_time_conc rejects NaN/Inf, negative conc
88
128
  loader.py — load_nca_csv(): CSV load + BLQ handling
89
129
  results.py — NCAResult (per-subject), NCASummaryResults dataclasses
90
130
  study.py — NCAStudy: from_csv(), analyze() -> NCASummaryResults
131
+ tlast trimming: strips trailing conc <= 0 before AUClast
91
132
  reporting.py — report_nca_single(), report_nca_summary() (HTML + Markdown)
92
133
  ```
93
134
 
135
+ ### NCA data flow
136
+
137
+ ```
138
+ CSV file
139
+ -> load_nca_csv() BLQ-handled DataFrame (subject, time, conc, dose, route)
140
+ -> NCAStudy(df, auc_method, blq_method)
141
+ -> study.analyze() per-subject loop:
142
+ 1. tlast trimming: strip trailing conc <= 0 (FDA/EMA)
143
+ 2. AUClast via chosen method (linear/log/linear_up_log_down)
144
+ 3. Cmax, Tmax from full profile
145
+ 4. lambda_z BAR² auto (post-Cmax positive points)
146
+ 5. AUCinf = AUClast + Clast/lambda_z
147
+ 6. CL_F/Vz_F (oral) or CL/Vz (IV)
148
+ -> NCASummaryResults list of NCAResult
149
+ -> summary.to_dataframe() pandas DataFrame
150
+ -> summary.report("out.html") -> report_nca_summary() -> nca_summary_report.html
151
+ -> result.report("sub.html") -> report_nca_single() -> nca_single_report.html
152
+ ```
153
+
94
154
  ### Sim module layout
95
155
 
96
156
  ```
@@ -117,20 +177,6 @@ OneCompartmentModel(route, CL, Vz) or TwoCompartmentModel(...)
117
177
  -> result.report("sim.html") -> report_simulation() -> sim_report.html
118
178
  ```
119
179
 
120
- ### NCA data flow
121
-
122
- ```
123
- CSV file
124
- -> load_nca_csv() BLQ-handled DataFrame (subject, time, conc, dose, route)
125
- -> NCAStudy(df, auc_method, blq_method)
126
- -> study.analyze() per-subject loop: AUClast, Cmax, Tmax, lambda_z, AUCinf,
127
- CL_F/Vz_F (oral) or CL/Vz (IV), warnings
128
- -> NCASummaryResults list of NCAResult
129
- -> summary.to_dataframe() pandas DataFrame
130
- -> summary.report("out.html") -> report_nca_summary() -> nca_summary_report.html
131
- -> result.report("sub.html") -> report_nca_single() -> nca_single_report.html
132
- ```
133
-
134
180
  ### Dissolution data flow
135
181
 
136
182
  ```
@@ -156,11 +202,48 @@ All CLI output and docstrings must use ASCII-only characters. Unicode punctuatio
156
202
 
157
203
  ---
158
204
 
205
+ ## Validation & Cross-Validation
206
+
207
+ ### PKNCA NCA cross-validation
208
+
209
+ The NCA module is cross-validated against PKNCA 0.12.1 (Denney et al., 2015) on the
210
+ 12-subject R nlme::Theoph theophylline dataset. AUClast matches within 2% relative
211
+ tolerance for every subject. Cmax matches exactly.
212
+
213
+ Run with:
214
+ ```bash
215
+ "C:\Program Files\R\R-4.6.0\bin\Rscript.exe" -e ".libPaths('D:/R-library/4.6'); source('scripts/pknca_theoph_crossval.R')"
216
+ ```
217
+
218
+ The R script outputs a `_PKNCA_REFERENCE` dict that goes into
219
+ `tests/validation/test_nca_theoph_reference.py`.
220
+
221
+ ### Validation test suite
222
+
223
+ - `tests/validation/test_nca_theoph_reference.py` — per-subject PKNCA cross-validation
224
+ - `tests/validation/test_nca_validation.py` — analytical truth recovery (IV bolus, oral)
225
+ - `tests/validation/test_sim_validation.py` — Gibaldi & Perrier analytical solutions
226
+ - `tests/nca/test_methods.py` — edge cases: all-zero, NaN/Inf, trailing zeros, mixed zeros
227
+
228
+ Each test cites a source: paper DOI, FDA guidance ID, or reference implementation.
229
+
230
+ ### Known edge cases tested
231
+
232
+ - All-zero concentrations → AUClast = 0 (no crash)
233
+ - NaN/Inf concentrations → ValueError (not silently propagated)
234
+ - Trailing zero concentrations → trimmed by tlast logic in study.py
235
+ - Single-point profiles → ValueError (need >= 2 for AUC)
236
+ - Empty arrays → ValueError
237
+ - Negative concentrations → ValueError
238
+ - Non-increasing times → ValueError
239
+
240
+ ---
241
+
159
242
  ## Current focus
160
243
 
161
- v1.5.0 is current (sparse-sampling NCA). v1.1.0-v1.4.0 also complete.
162
- Next milestone is v2.0.0: Bayesian PK.
163
- See `ROADMAP.md` for the full post-1.0.0 ladder.
244
+ v2.3.0 is current Pop PK FOCE-I validated against nlme reference, covariate skeleton removed.
245
+ Next focus: conda-forge distribution + PowerTOST cross-validation (nice-to-have).
246
+ See `ROADMAP.md` for the full ladder.
164
247
 
165
248
  **Before any new feature:** run `python -m build && python -m twine check dist/*` to confirm the wheel is clean.
166
249
 
@@ -172,8 +255,7 @@ See `ROADMAP.md` for the full post-1.0.0 ladder.
172
255
  0.1.0 f1, f2, input validation, CSV loader, CLI, Markdown+HTML report stub, tests DONE
173
256
  0.1.1 bootstrap_f2, profile plots in HTML reports, CI, example datasets, py.typed DONE
174
257
  0.1.2 PyPI publish, Trusted Publishing workflow DONE
175
- 0.1.3 README polish, f2_method="regulatory" option, CV% warning in compare(),
176
- validation claims softened
258
+ 0.1.3 README polish, f2_method="regulatory" option, CV% warning in compare()
177
259
  0.2.0 dissolution model fitting (Weibull, Korsmeyer-Peppas, Higuchi, first-order,
178
260
  zero-order) — scipy curve_fit, AIC/BIC/R2, fit overlay in HTML report
179
261
  0.3.0 full Markdown + HTML + ReportLab PDF report generator
@@ -190,6 +272,9 @@ See `ROADMAP.md` for the full post-1.0.0 ladder.
190
272
  1.4.0 multi-media dissolution: pH 1.2/4.5/6.8 panel, alcohol dose-dumping DONE
191
273
  1.5.0 Sparse-sampling NCA: model-informed 1-cmt oral from 3-5 data points DONE
192
274
  2.0.0 Bayesian PK: MAP individual estimation + full posterior + Bayesian BE (PyMC) DONE
275
+ 2.1.0 FOCE-I & SAEM population PK (1-cmt, diagonal Omega) DONE
276
+ 2.2.0 2-cmt models, full Omega matrix, covariate skeleton DONE
277
+ 2.3.0 Remove covariate skeleton, FOCE-I nlme cross-validation, conda-forge prep DONE
193
278
  ```
194
279
 
195
280
  See `ROADMAP.md` for full milestone detail, scope rationale, and definition of done.
@@ -219,10 +304,14 @@ These are load-bearing. Do not violate them.
219
304
 
220
305
  4. **BLQ handling must be explicit.** Never silently drop BLQ values. Require the caller to specify the method.
221
306
 
222
- 5. **Disclaimer required in all generated reports:**
307
+ 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`.
308
+
309
+ 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.
310
+
311
+ 7. **Disclaimer required in all generated reports:**
223
312
  > This report was generated using OpenPKFlow (open-source). Final regulatory interpretation should be reviewed by qualified formulation, pharmacokinetic, and regulatory experts.
224
313
 
225
- 6. **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.
314
+ 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.
226
315
 
227
316
  ---
228
317
 
@@ -239,6 +328,8 @@ Known reference values:
239
328
  - f2 = 100 when reference == test (by definition)
240
329
  - f2 ≈ 50 when profiles differ by ~10 percentage points at each timepoint (FDA 1997 guidance threshold)
241
330
  - f1 = 0 when reference == test (by definition)
331
+ - AUClast matches PKNCA 0.12.1 within 2% on all 12 theophylline subjects
332
+ - Cmax matches PKNCA 0.12.1 exactly
242
333
 
243
334
  ---
244
335
 
@@ -11,6 +11,75 @@ 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
+
57
+ ## [2.3.0] — 2026-05-24
58
+
59
+ ### Breaking Changes
60
+
61
+ - **`pop/estimation/covariate.py` removed** — `CovariateModel`, `CovariateDef`, `apply_covariates`,
62
+ `pack_betas`, `unpack_betas` are deleted. These symbols were a non-functional skeleton in v2.2.0
63
+ that silently did nothing during `run_foce_i()` or `run_saem()` estimation. Users who imported
64
+ any of these symbols must remove those imports. No estimation results are affected.
65
+ - **`PopPKModel.covariate_model` field removed** — `PopPKModel` no longer accepts a
66
+ `covariate_model` keyword argument. Existing `PopPKModel` definitions should drop that argument.
67
+ - **`PopPKResult.covariate_betas` field removed** — `PopPKResult.to_dict()` no longer includes
68
+ the `covariate_betas` key.
69
+
70
+ ### Added
71
+
72
+ - Pop PK cross-validation on the 12-subject Theophylline dataset:
73
+ `tests/validation/test_pop_foce_reference.py`. `run_foce_i()` typical values match
74
+ the `nlme` reference values from Pinheiro & Bates (2000), Table 8.1, within 20%
75
+ relative tolerance. A waiting nlmixr2 5.0.0 script is included at
76
+ `scripts/nlmixr2_popk_crossval.R` for rerun once Rtools/C compiler support is available.
77
+
78
+ ### Changed
79
+
80
+ - `PopPKModel.n_betas` always returns 0 (property retained for API compatibility with existing
81
+ code that reads it; will be removed in v3.0.0).
82
+
14
83
  ## [2.2.0] — 2026-05-23
15
84
 
16
85
  ### Added