rexafs 0.2.3__tar.gz → 0.2.4__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 (370) hide show
  1. {rexafs-0.2.3 → rexafs-0.2.4}/Cargo.lock +10 -10
  2. {rexafs-0.2.3 → rexafs-0.2.4}/Cargo.toml +2 -2
  3. {rexafs-0.2.3 → rexafs-0.2.4}/PKG-INFO +6 -2
  4. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/Cargo.toml +2 -1
  5. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/benches/autobk_stage_benchmark.rs +5 -6
  6. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/benches/feff_fitting_batch_benchmark.rs +5 -6
  7. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/benches/perf.rs +2 -0
  8. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/lib.rs +1 -1
  9. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/prelude.rs +1 -1
  10. rexafs-0.2.4/crates/rexafs/src/xafs/background/legacy_clamp_tests.rs +120 -0
  11. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/background.rs +44 -13
  12. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/background_ndarray.rs +44 -13
  13. rexafs-0.2.4/crates/rexafs/src/xafs/fft_grid.rs +13 -0
  14. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/mod.rs +1 -0
  15. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/xasspectrum.rs +16 -0
  16. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/xrayfft.rs +69 -14
  17. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/xrayfft_ndarray.rs +42 -7
  18. rexafs-0.2.4/crates/rexafs/tests/fft_grid.rs +156 -0
  19. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/processing_api.rs +21 -0
  20. rexafs-0.2.4/crates/rexafs/tests/testfiles/fft_grid_larch_reference.json +1 -0
  21. {rexafs-0.2.3 → rexafs-0.2.4/py-rexafs}/LICENSE-MIT +1 -1
  22. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/README.md +5 -1
  23. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/python/rexafs/__init__.pyi +3 -0
  24. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/src/lib.rs +27 -0
  25. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/tests/test_api.py +23 -0
  26. {rexafs-0.2.3 → rexafs-0.2.4}/LICENSE +0 -0
  27. {rexafs-0.2.3 → rexafs-0.2.4}/LICENSE-APACHE +0 -0
  28. {rexafs-0.2.3/crates/rexafs → rexafs-0.2.4}/LICENSE-MIT +0 -0
  29. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/.gitignore +0 -0
  30. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/LICENSE-APACHE +0 -0
  31. {rexafs-0.2.3/py-rexafs → rexafs-0.2.4/crates/rexafs}/LICENSE-MIT +0 -0
  32. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/README.md +0 -0
  33. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/benches/xas_group_benchmark_parallel.rs +0 -0
  34. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/benches/xas_group_benchmark_single.rs +0 -0
  35. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/benchmarks/baseline.json +0 -0
  36. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ag.cif +0 -0
  37. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ag.json +0 -0
  38. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ag2o.cif +0 -0
  39. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ag2o.json +0 -0
  40. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/al.cif +0 -0
  41. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/al.json +0 -0
  42. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/al2o3_corundum.cif +0 -0
  43. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/al2o3_corundum.json +0 -0
  44. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/aspirin.cif +0 -0
  45. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/aspirin.json +0 -0
  46. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/au.cif +0 -0
  47. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/au.json +0 -0
  48. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/caf2_fluorite.cif +0 -0
  49. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/caf2_fluorite.json +0 -0
  50. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/catalog.json +0 -0
  51. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ceo2.cif +0 -0
  52. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ceo2.json +0 -0
  53. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/co3o4.cif +0 -0
  54. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/co3o4.json +0 -0
  55. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/co_hcp.cif +0 -0
  56. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/co_hcp.json +0 -0
  57. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/coo.cif +0 -0
  58. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/coo.json +0 -0
  59. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cos2_cattierite.cif +0 -0
  60. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cos2_cattierite.json +0 -0
  61. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cr.cif +0 -0
  62. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cr.json +0 -0
  63. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cr2o3_eskolaite.cif +0 -0
  64. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cr2o3_eskolaite.json +0 -0
  65. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cu.cif +0 -0
  66. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cu.json +0 -0
  67. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cu2o_cuprite.cif +0 -0
  68. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cu2o_cuprite.json +0 -0
  69. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cufes2_chalcopyrite.cif +0 -0
  70. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cufes2_chalcopyrite.json +0 -0
  71. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cuo_tenorite.cif +0 -0
  72. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/cuo_tenorite.json +0 -0
  73. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fe2o3_hematite.cif +0 -0
  74. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fe2o3_hematite.json +0 -0
  75. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fe3o4_magnetite.cif +0 -0
  76. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fe3o4_magnetite.json +0 -0
  77. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fe_bcc.cif +0 -0
  78. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fe_bcc.json +0 -0
  79. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/feo_wustite.cif +0 -0
  80. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/feo_wustite.json +0 -0
  81. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fes2_pyrite.cif +0 -0
  82. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/fes2_pyrite.json +0 -0
  83. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ir.cif +0 -0
  84. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ir.json +0 -0
  85. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/licoo2.cif +0 -0
  86. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/licoo2.json +0 -0
  87. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/lifepo4_triphylite.cif +0 -0
  88. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/lifepo4_triphylite.json +0 -0
  89. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mn2o3_bixbyite.cif +0 -0
  90. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mn2o3_bixbyite.json +0 -0
  91. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mn_alpha.cif +0 -0
  92. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mn_alpha.json +0 -0
  93. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mno.cif +0 -0
  94. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mno.json +0 -0
  95. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mno2_pyrolusite.cif +0 -0
  96. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mno2_pyrolusite.json +0 -0
  97. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mo.cif +0 -0
  98. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mo.json +0 -0
  99. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/moo3.cif +0 -0
  100. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/moo3.json +0 -0
  101. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mos2_2h.cif +0 -0
  102. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/mos2_2h.json +0 -0
  103. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nacl_halite.cif +0 -0
  104. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nacl_halite.json +0 -0
  105. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nb.cif +0 -0
  106. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nb.json +0 -0
  107. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ni.cif +0 -0
  108. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ni.json +0 -0
  109. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nio.cif +0 -0
  110. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nio.json +0 -0
  111. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nis_millerite.cif +0 -0
  112. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/nis_millerite.json +0 -0
  113. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/os.cif +0 -0
  114. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/os.json +0 -0
  115. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pd.cif +0 -0
  116. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pd.json +0 -0
  117. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pdo.cif +0 -0
  118. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pdo.json +0 -0
  119. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pt.cif +0 -0
  120. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pt.json +0 -0
  121. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pto2_alpha.cif +0 -0
  122. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/pto2_alpha.json +0 -0
  123. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/re.cif +0 -0
  124. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/re.json +0 -0
  125. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/rh.cif +0 -0
  126. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/rh.json +0 -0
  127. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ru_hcp.cif +0 -0
  128. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ru_hcp.json +0 -0
  129. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ruo2.cif +0 -0
  130. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ruo2.json +0 -0
  131. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/sn_beta.cif +0 -0
  132. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/sn_beta.json +0 -0
  133. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/sno2_cassiterite.cif +0 -0
  134. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/sno2_cassiterite.json +0 -0
  135. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ta.cif +0 -0
  136. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ta.json +0 -0
  137. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ti_hcp.cif +0 -0
  138. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/ti_hcp.json +0 -0
  139. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/tio2_anatase.cif +0 -0
  140. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/tio2_anatase.json +0 -0
  141. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/tio2_rutile.cif +0 -0
  142. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/tio2_rutile.json +0 -0
  143. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/urea.cif +0 -0
  144. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/urea.json +0 -0
  145. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/v.cif +0 -0
  146. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/v.json +0 -0
  147. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/v2o5.cif +0 -0
  148. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/v2o5.json +0 -0
  149. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/w.cif +0 -0
  150. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/w.json +0 -0
  151. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/wo3_monoclinic.cif +0 -0
  152. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/wo3_monoclinic.json +0 -0
  153. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zn.cif +0 -0
  154. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zn.json +0 -0
  155. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zno_wurtzite.cif +0 -0
  156. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zno_wurtzite.json +0 -0
  157. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zns_sphalerite.cif +0 -0
  158. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zns_sphalerite.json +0 -0
  159. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zro2_monoclinic.cif +0 -0
  160. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/data/builtin_cifs/zro2_monoclinic.json +0 -0
  161. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/doc/feff-fitting-mvp.md +0 -0
  162. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/doc/img/flamegraph_rexafs_analytical_optimization.svg +0 -0
  163. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/doc/img/flamegraph_rexafs_numerical_optimization.svg +0 -0
  164. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/doc/img/flamegraph_xraylarch.svg +0 -0
  165. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/doc/migration-performance-logic-hardening.md +0 -0
  166. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/doc/profiling.md +0 -0
  167. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/examples/alloc_baseline.rs +0 -0
  168. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/examples/feff_backend_probe.rs +0 -0
  169. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/examples/feff_compare_export.rs +0 -0
  170. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/examples/plot_demo.rs +0 -0
  171. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/flamegraph.svg +0 -0
  172. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/pythontest.py +0 -0
  173. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/bench_regression_gate.sh +0 -0
  174. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/build_builtin_library.py +0 -0
  175. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/fetch_builtin_cifs.py +0 -0
  176. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/generate_element_table.py +0 -0
  177. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/generate_larch_fit_references.py +0 -0
  178. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/generate_larch_fitspace_references.py +0 -0
  179. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/generate_spacegroup_table.py +0 -0
  180. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/scripts/generate_structure_fixtures.py +0 -0
  181. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/parser/mod.rs +0 -0
  182. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/builder.rs +0 -0
  183. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/config.rs +0 -0
  184. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/errors.rs +0 -0
  185. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/fitting_plots.rs +0 -0
  186. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/group_plots.rs +0 -0
  187. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/mod.rs +0 -0
  188. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/panels.rs +0 -0
  189. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/spectrum_plots.rs +0 -0
  190. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/plot/traits.rs +0 -0
  191. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/analysis/lcf.rs +0 -0
  192. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/analysis/mod.rs +0 -0
  193. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/analysis/pca.rs +0 -0
  194. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/background/fixed.rs +0 -0
  195. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/bessel_i0.rs +0 -0
  196. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/constants.rs +0 -0
  197. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/errors.rs +0 -0
  198. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/builder.rs +0 -0
  199. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/errors.rs +0 -0
  200. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/expression.pest +0 -0
  201. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/expression.rs +0 -0
  202. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/feffdat.rs +0 -0
  203. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/mod.rs +0 -0
  204. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/path_model.rs +0 -0
  205. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/runner.rs +0 -0
  206. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/solver.rs +0 -0
  207. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/template.rs +0 -0
  208. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/transform.rs +0 -0
  209. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/types.rs +0 -0
  210. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/fitting/variables.rs +0 -0
  211. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/inverse_fft.rs +0 -0
  212. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/io/QAS.rs +0 -0
  213. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/io/athena.rs +0 -0
  214. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/io/mod.rs +0 -0
  215. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/io/xafs_bson.rs +0 -0
  216. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/io/xafs_json.rs +0 -0
  217. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/io/xasdatatype.rs +0 -0
  218. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/io/xdi.rs +0 -0
  219. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/lmutils.rs +0 -0
  220. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/mathutils.rs +0 -0
  221. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/mathutils_ndarray.rs +0 -0
  222. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/normalization.rs +0 -0
  223. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/normalization_ndarray.rs +0 -0
  224. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/nshare.rs +0 -0
  225. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/spline.rs +0 -0
  226. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/builtin.rs +0 -0
  227. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/builtin_library.json.gz +0 -0
  228. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/cif.rs +0 -0
  229. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/cluster.rs +0 -0
  230. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/db/amcsd.rs +0 -0
  231. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/db/cod.rs +0 -0
  232. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/db/mp.rs +0 -0
  233. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/db.rs +0 -0
  234. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/element.rs +0 -0
  235. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/element_table.rs +0 -0
  236. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/feffinp.rs +0 -0
  237. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/lattice.rs +0 -0
  238. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/mod.rs +0 -0
  239. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/model.rs +0 -0
  240. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/pathrank.rs +0 -0
  241. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/paths.rs +0 -0
  242. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/spacegroups.json.gz +0 -0
  243. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/symmetry.rs +0 -0
  244. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/structure/xyz.rs +0 -0
  245. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/tools.rs +0 -0
  246. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/xafsutils.rs +0 -0
  247. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/xafsutils_ndarray.rs +0 -0
  248. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/xasgroup.rs +0 -0
  249. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/src/xafs/xasparameters.rs +0 -0
  250. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/supportinginfo/uncertainty.md +0 -0
  251. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/supportinginfo/uncertainty.pdf +0 -0
  252. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/supportinginfo/uncertainty2.md +0 -0
  253. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/supportinginfo/uncertainty2.pdf +0 -0
  254. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/analysis.rs +0 -0
  255. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/athena_prj.rs +0 -0
  256. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/feff10_s02k_parity.rs +0 -0
  257. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/feff_builder_examples.rs +0 -0
  258. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/feff_larch_fitspace_parity.rs +0 -0
  259. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/feff_larch_parity.rs +0 -0
  260. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/inverse_fft.rs +0 -0
  261. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/pythonscript/.gitignore +0 -0
  262. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/pythonscript/README.md +0 -0
  263. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/pythonscript/generate_spline_reference.py +0 -0
  264. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/pythonscript/generate_test.py +0 -0
  265. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/pythonscript/pyproject.toml +0 -0
  266. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/pythonscript/requirements.txt +0 -0
  267. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/pythonscript/uv.lock +0 -0
  268. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/refeff_runner.rs +0 -0
  269. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/structure.rs +0 -0
  270. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS.dat +0 -0
  271. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_athena.prj +0 -0
  272. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_athena_k_chi.dat +0 -0
  273. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_autobk_bkg_larch.txt +0 -0
  274. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_autobk_k_larch.txt +0 -0
  275. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_pre_post_edge_expected.dat +0 -0
  276. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_preedge_larch.txt +0 -0
  277. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_smooth.txt +0 -0
  278. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_smooth_larch.txt +0 -0
  279. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/Ru_QAS_xftf_larch.txt +0 -0
  280. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/amcsd_subset.db +0 -0
  281. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/athena.chir +0 -0
  282. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/autobk_fixed_reference.json +0 -0
  283. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/fes2_pyrite.cif +0 -0
  284. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/fes2_pyrite.json +0 -0
  285. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/fes2_pyrite_nosymops.cif +0 -0
  286. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/fes2_pyrite_p1.cif +0 -0
  287. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ru_hcp.cif +0 -0
  288. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ru_hcp.json +0 -0
  289. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ru_hcp_nosymops.cif +0 -0
  290. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ru_hcp_p1.cif +0 -0
  291. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ruo2_rutile.cif +0 -0
  292. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ruo2_rutile.json +0 -0
  293. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ruo2_rutile_nosymops.cif +0 -0
  294. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/ruo2_rutile_p1.cif +0 -0
  295. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/zro2_baddeleyite.cif +0 -0
  296. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/zro2_baddeleyite.json +0 -0
  297. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/zro2_baddeleyite_nosymops.cif +0 -0
  298. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cif/zro2_baddeleyite_p1.cif +0 -0
  299. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cod_9000594_pyrite.cif +0 -0
  300. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/cod_result_fes2.json +0 -0
  301. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/feff0002.dat +0 -0
  302. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/feff_ff2chi_larch_ref.txt +0 -0
  303. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/feff_fit_target_larch.txt +0 -0
  304. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/feff_path_chi_larch_ref.txt +0 -0
  305. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/feffcu01.dat +0 -0
  306. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/README.md +0 -0
  307. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/cu_fit_kspace.txt +0 -0
  308. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/cu_fit_qspace.txt +0 -0
  309. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/cu_fit_rspace.txt +0 -0
  310. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/fitspace_metadata.json +0 -0
  311. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/metadata.json +0 -0
  312. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/znse_fit_kspace.txt +0 -0
  313. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/larch_fit_refs/znse_fit_rspace.txt +0 -0
  314. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/mp_summary_ru.json +0 -0
  315. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/spline_scipy_reference.json +0 -0
  316. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/test.bson +0 -0
  317. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/test.json +0 -0
  318. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/test.json.gz +0 -0
  319. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/window_Gaussian.txt +0 -0
  320. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/window_Hanning.txt +0 -0
  321. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/window_Kaiser-Bessel.txt +0 -0
  322. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/window_Parzen.txt +0 -0
  323. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/window_Sine.txt +0 -0
  324. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/window_Welch.txt +0 -0
  325. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/README.md +0 -0
  326. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feff8l/Co/feff.inp +0 -0
  327. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feff8l/FeO_withPb/README +0 -0
  328. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feff8l/FeO_withPb/feff.inp +0 -0
  329. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feff8l/MnO2/feff.inp +0 -0
  330. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0001.dat +0 -0
  331. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0002.dat +0 -0
  332. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0003.dat +0 -0
  333. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0004.dat +0 -0
  334. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0005.dat +0 -0
  335. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0006.dat +0 -0
  336. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0007.dat +0 -0
  337. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0008.dat +0 -0
  338. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0009.dat +0 -0
  339. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0010.dat +0 -0
  340. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0011.dat +0 -0
  341. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0012.dat +0 -0
  342. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_Cu/feff0013.dat +0 -0
  343. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff.inp +0 -0
  344. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znas.dat +0 -0
  345. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znbr.dat +0 -0
  346. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znga.dat +0 -0
  347. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znge.dat +0 -0
  348. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znkr.dat +0 -0
  349. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znrb.dat +0 -0
  350. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znse.dat +0 -0
  351. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/feffit/Feff_ZnSe/feff_znzn.dat +0 -0
  352. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/xafsdata/cu_150k.xmu +0 -0
  353. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/xafsdata/ni_metal_rt.xdi +0 -0
  354. {rexafs-0.2.3 → rexafs-0.2.4}/crates/rexafs/tests/testfiles/xraylarch_d867/xafsdata/znse_zn_xafs.001 +0 -0
  355. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/.github/workflows/CI.yml +0 -0
  356. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/.gitignore +0 -0
  357. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/Cargo.toml +0 -0
  358. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/LICENSE-APACHE +0 -0
  359. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/python/rexafs/__init__.py +0 -0
  360. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/python/rexafs/io.py +0 -0
  361. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/python/rexafs/io.pyi +0 -0
  362. {rexafs-0.2.3 → rexafs-0.2.4}/py-rexafs/python/rexafs/py.typed +0 -0
  363. {rexafs-0.2.3 → rexafs-0.2.4}/pyproject.toml +0 -0
  364. {rexafs-0.2.3 → rexafs-0.2.4}/vendor/sum_tree/Cargo.toml +0 -0
  365. {rexafs-0.2.3 → rexafs-0.2.4}/vendor/sum_tree/LICENSE-APACHE +0 -0
  366. {rexafs-0.2.3 → rexafs-0.2.4}/vendor/sum_tree/README.rexafs.md +0 -0
  367. {rexafs-0.2.3 → rexafs-0.2.4}/vendor/sum_tree/src/cursor.rs +0 -0
  368. {rexafs-0.2.3 → rexafs-0.2.4}/vendor/sum_tree/src/property_test.rs +0 -0
  369. {rexafs-0.2.3 → rexafs-0.2.4}/vendor/sum_tree/src/sum_tree.rs +0 -0
  370. {rexafs-0.2.3 → rexafs-0.2.4}/vendor/sum_tree/src/tree_map.rs +0 -0
@@ -6887,7 +6887,7 @@ checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
6887
6887
 
6888
6888
  [[package]]
6889
6889
  name = "py-rexafs"
6890
- version = "0.2.3"
6890
+ version = "0.2.4"
6891
6891
  dependencies = [
6892
6892
  "ndarray",
6893
6893
  "numpy",
@@ -7484,7 +7484,7 @@ dependencies = [
7484
7484
 
7485
7485
  [[package]]
7486
7486
  name = "rexafs"
7487
- version = "0.2.3"
7487
+ version = "0.2.4"
7488
7488
  dependencies = [
7489
7489
  "apex-solver",
7490
7490
  "approx",
@@ -7517,13 +7517,13 @@ dependencies = [
7517
7517
  "serde",
7518
7518
  "serde_json",
7519
7519
  "thiserror 2.0.20",
7520
- "ureq 3.4.0",
7520
+ "ureq 3.4.1",
7521
7521
  "version",
7522
7522
  ]
7523
7523
 
7524
7524
  [[package]]
7525
7525
  name = "rexafs-gui"
7526
- version = "0.2.3"
7526
+ version = "0.2.4"
7527
7527
  dependencies = [
7528
7528
  "accesskit",
7529
7529
  "accesskit_macos",
@@ -7549,7 +7549,7 @@ dependencies = [
7549
7549
  "serde_json",
7550
7550
  "sha2 0.11.0",
7551
7551
  "unicode-segmentation",
7552
- "ureq 3.4.0",
7552
+ "ureq 3.4.1",
7553
7553
  "walkdir",
7554
7554
  "windows 0.62.2",
7555
7555
  "xraydb",
@@ -7557,7 +7557,7 @@ dependencies = [
7557
7557
 
7558
7558
  [[package]]
7559
7559
  name = "rexafs-wasm"
7560
- version = "0.2.3"
7560
+ version = "0.2.4"
7561
7561
  dependencies = [
7562
7562
  "js-sys",
7563
7563
  "rexafs",
@@ -9730,9 +9730,9 @@ dependencies = [
9730
9730
 
9731
9731
  [[package]]
9732
9732
  name = "ureq"
9733
- version = "3.4.0"
9733
+ version = "3.4.1"
9734
9734
  source = "registry+https://github.com/rust-lang/crates.io-index"
9735
- checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d"
9735
+ checksum = "af5546be8f5378d5414f83733f5c9a2526f4645829edbc1c41790aeef1b38e8b"
9736
9736
  dependencies = [
9737
9737
  "base64 0.23.1",
9738
9738
  "cookie_store",
@@ -9750,9 +9750,9 @@ dependencies = [
9750
9750
 
9751
9751
  [[package]]
9752
9752
  name = "ureq-proto"
9753
- version = "0.6.1"
9753
+ version = "0.6.2"
9754
9754
  source = "registry+https://github.com/rust-lang/crates.io-index"
9755
- checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613"
9755
+ checksum = "fabc3e92916c89c95b20eef7b06b00b066bc217ef9ea3a4ac9bf1a7e35261e10"
9756
9756
  dependencies = [
9757
9757
  "base64 0.23.1",
9758
9758
  "http",
@@ -10,7 +10,7 @@ exclude = [
10
10
  ]
11
11
 
12
12
  [workspace.package]
13
- version = "0.2.3"
13
+ version = "0.2.4"
14
14
  edition = "2021"
15
15
  rust-version = "1.98"
16
16
  authors = ["Ameyanagi <contact@ameyanagi.com>"]
@@ -62,7 +62,7 @@ ruviz-gpui = { version = "0.14.1", features = ["gpu", "3d"] }
62
62
  refeff = { version = "0.3.0", default-features = false, features = ["exafs"] }
63
63
  feff10 = { version = "0.2.3", features = ["prebuilt"] }
64
64
 
65
- rexafs = { version = "0.2.3", path = "crates/rexafs" }
65
+ rexafs = { version = "0.2.4", path = "crates/rexafs" }
66
66
 
67
67
  [patch.crates-io]
68
68
  gpui = { git = "https://github.com/zed-industries/zed", rev = "3060e4170ea5ef0e6886b9ac1853aaead9ddd59f" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rexafs
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -62,7 +62,7 @@ still unimplemented placeholders; selecting them raises an error.
62
62
 
63
63
  ## Build
64
64
 
65
- Registry publication is pending. Build from the repository root with CPython
65
+ Build from the repository root with CPython
66
66
  3.10–3.14:
67
67
 
68
68
  ```bash
@@ -80,3 +80,7 @@ is `clamp_lo = 0`, the high-end weight is `clamp_hi = 1`, and `nclamp = 3`.
80
80
  The `FixedPenalty` model does not add a separate ridge penalty on coefficients.
81
81
  See the [full definition](../doc/autobk-fixed-penalty.md).
82
82
 
83
+ `XrayFFTF.grid` defaults to `"Input"`. Set it to `"Larch"` to resample on the
84
+ extended FFT window grid used by XrayLarch. `Spectrum.kwin_k()` and `.kwin()`
85
+ return the matching window axis and values. See [compatibility and migration](../doc/fft-grid-compatibility.md).
86
+
@@ -61,9 +61,10 @@ http = ["dep:ureq"]
61
61
  materials-project = ["http"]
62
62
  cod = ["http"]
63
63
 
64
- [dev-dependencies]
64
+ [target.'cfg(unix)'.dev-dependencies]
65
65
  pprof = { version = "0.15", features = ["flamegraph"] }
66
66
 
67
+ [dev-dependencies]
67
68
  criterion = { version = "0.8.2", features = ["html_reports"] }
68
69
 
69
70
  [[bench]]
@@ -3,6 +3,7 @@ mod perf;
3
3
  use criterion::{criterion_group, criterion_main, Criterion};
4
4
  use std::hint::black_box;
5
5
 
6
+ #[cfg(unix)]
6
7
  use perf::FlamegraphProfiler;
7
8
  use rexafs::xafs::background::{AUTOBKClampScalePolicy, AUTOBKSolver, AUTOBK};
8
9
  use rexafs::xafs::normalization::{NormalizationMethod, PrePostEdge};
@@ -73,13 +74,11 @@ fn criterion_benchmark(c: &mut Criterion) {
73
74
 
74
75
  fn custom() -> Criterion {
75
76
  let base = Criterion::default().sample_size(20);
76
- let enable_profiler =
77
- std::env::args().any(|arg| arg == "--profile-time" || arg.starts_with("--profile-time="));
78
- if enable_profiler {
79
- base.with_profiler(FlamegraphProfiler::new(1000))
80
- } else {
81
- base
77
+ #[cfg(unix)]
78
+ if std::env::args().any(|arg| arg == "--profile-time" || arg.starts_with("--profile-time=")) {
79
+ return base.with_profiler(FlamegraphProfiler::new(1000));
82
80
  }
81
+ base
83
82
  }
84
83
 
85
84
  criterion_group! {
@@ -2,6 +2,7 @@ mod perf;
2
2
 
3
3
  use criterion::{criterion_group, criterion_main, Criterion, Throughput};
4
4
  use nalgebra::DVector;
5
+ #[cfg(unix)]
5
6
  use perf::FlamegraphProfiler;
6
7
  use rexafs::xafs::fitting::{
7
8
  feffit_independent, feffit_joint_with_options, feffpath, ff2chi, FeffBatchOptions,
@@ -160,13 +161,11 @@ fn criterion_benchmark(c: &mut Criterion) {
160
161
 
161
162
  fn custom() -> Criterion {
162
163
  let base = Criterion::default().sample_size(10);
163
- let enable_profiler =
164
- std::env::args().any(|arg| arg == "--profile-time" || arg.starts_with("--profile-time="));
165
- if enable_profiler {
166
- base.with_profiler(FlamegraphProfiler::new(1000))
167
- } else {
168
- base
164
+ #[cfg(unix)]
165
+ if std::env::args().any(|arg| arg == "--profile-time" || arg.starts_with("--profile-time=")) {
166
+ return base.with_profiler(FlamegraphProfiler::new(1000));
169
167
  }
168
+ base
170
169
  }
171
170
 
172
171
  criterion_group! {
@@ -1,3 +1,5 @@
1
+ #![cfg(unix)]
2
+
1
3
  use std::{fs::File, os::raw::c_int, path::Path};
2
4
 
3
5
  use criterion::profiler::Profiler;
@@ -14,5 +14,5 @@ pub use xafs::background::{BackgroundMethod, AUTOBK};
14
14
  pub use xafs::normalization::{NormalizationMethod, PrePostEdge};
15
15
  pub use xafs::xasgroup::XASGroup as Group;
16
16
  pub use xafs::xasspectrum::XASSpectrum as Spectrum;
17
- pub use xafs::xrayfft::XrayFFTF;
17
+ pub use xafs::xrayfft::{FFTGrid, XrayFFTF};
18
18
  pub use xafs::{analysis, fitting, io, structure, tools, Result, XAFSError as Error};
@@ -20,7 +20,7 @@ pub use crate::xafs::normalization::{Normalization, NormalizationMethod};
20
20
  #[cfg(feature = "ndarray-compat")]
21
21
  pub use crate::xafs::nshare::{ToNalgebra, ToNdarray1};
22
22
  pub use crate::xafs::xafsutils::{FTWindow, XAFSUtils};
23
- pub use crate::xafs::xrayfft::{FFTUtils, XrayFFTF, XrayFFTR};
23
+ pub use crate::xafs::xrayfft::{FFTGrid, FFTUtils, XrayFFTF, XrayFFTR};
24
24
 
25
25
  #[cfg(feature = "plotting")]
26
26
  pub use crate::plot::{PlotError, PlotXAS, XASPlotBuilder};
@@ -0,0 +1,120 @@
1
+ //! Regression for issue #20. This exercises the legacy objective independently
2
+ //! of the fixed-penalty solver, for both array backends.
3
+ use super::*;
4
+
5
+ fn problem() -> AUTOBKSpline {
6
+ let x: Vec<f64> = (0..9).map(|i| i as f64 * 1.5).collect();
7
+ let y: Vec<f64> = x.iter().map(|v| 1.0 + 0.03 * v.cos()).collect();
8
+ let (knots, coefs) = spline::interpolate(&x, &y, 3).unwrap();
9
+ let k = DVector::from_iterator(241, (0..241).map(|i| i as f64 * 0.05));
10
+ AUTOBKSpline {
11
+ coefs: DVector::from_vec(coefs),
12
+ knots: DVector::from_vec(knots),
13
+ kraw: k.clone(),
14
+ kout: k.clone(),
15
+ mu: k.map(|v| 1.0 + 0.1 * (2.0 * v).sin()),
16
+ chi_std: Some(k.map(|v| 0.01 * v.cos())),
17
+ ftwin: k,
18
+ nclamp: 3,
19
+ clamp_lo: 2,
20
+ clamp_hi: 1,
21
+ irbkg: 35,
22
+ ..Default::default()
23
+ }
24
+ }
25
+
26
+ fn numerical_jacobian(p: &AUTOBKSpline, scale: Option<f64>) -> DMatrix<f64> {
27
+ DMatrix::from_columns(
28
+ &(0..p.coefs.len())
29
+ .map(|j| {
30
+ let mut plus = p.coefs.clone();
31
+ let mut minus = p.coefs.clone();
32
+ plus[j] += 1e-6;
33
+ minus[j] -= 1e-6;
34
+ (p.residual_vec_with_scale(&plus, scale) - p.residual_vec_with_scale(&minus, scale))
35
+ / 2e-6
36
+ })
37
+ .collect::<Vec<_>>(),
38
+ )
39
+ }
40
+
41
+ #[test]
42
+ fn legacy_clamp_jacobian_matches_central_differences() {
43
+ for nclamp in [0, 1, 3, 500] {
44
+ for weights in [(0, 0), (0, 1), (2, 0), (2, 3)] {
45
+ for irbkg in [0, 35] {
46
+ let mut p = problem();
47
+ p.nclamp = nclamp;
48
+ (p.clamp_lo, p.clamp_hi) = weights;
49
+ p.irbkg = irbkg;
50
+ #[cfg(not(feature = "ndarray-compat"))]
51
+ if nclamp == 3 {
52
+ p.ensure_precomputed_basis();
53
+ }
54
+ for scale in [None, Some(1.7)] {
55
+ let analytic = p.residual_jacobian_with_scale(&p.coefs, scale);
56
+ let numeric = numerical_jacobian(&p, scale);
57
+ let error = (&analytic - &numeric).norm() / numeric.norm().max(1.0);
58
+ assert!(
59
+ error < 1e-7,
60
+ "n={nclamp}, weights={weights:?}, R={irbkg}, scale={scale:?}: {error}"
61
+ );
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ #[derive(Clone)]
69
+ struct NumericProblem(AUTOBKSpline, bool);
70
+ impl LeastSquaresProblem<f64, Dyn, Dyn> for NumericProblem {
71
+ type ParameterStorage = Owned<f64, Dyn>;
72
+ type ResidualStorage = Owned<f64, Dyn>;
73
+ type JacobianStorage = Owned<f64, Dyn, Dyn>;
74
+ fn set_params(&mut self, p: &DVector<f64>) {
75
+ self.0.coefs.copy_from(p);
76
+ }
77
+ fn params(&self) -> DVector<f64> {
78
+ self.0.coefs.clone()
79
+ }
80
+ fn residuals(&self) -> Option<DVector<f64>> {
81
+ Some(self.0.residual_vec(&self.0.coefs))
82
+ }
83
+ fn jacobian(&self) -> Option<DMatrix<f64>> {
84
+ if self.1 {
85
+ Some(self.0.residual_jacobian_with_scale(
86
+ &self.0.coefs,
87
+ Some(self.0.clamp_scale(&self.0.coefs)),
88
+ ))
89
+ } else {
90
+ Some(numerical_jacobian(&self.0, None))
91
+ }
92
+ }
93
+ }
94
+
95
+ #[test]
96
+ fn legacy_lm_agrees_with_numerical_derivative_solution() {
97
+ let solver = LevenbergMarquardt::new()
98
+ .with_tol(1e-10)
99
+ .with_patience(1000);
100
+ let (analytic, report) = solver.minimize(problem());
101
+ let (numeric, reference) = solver.minimize(NumericProblem(problem(), false));
102
+ let (old, old_report) = solver.minimize(NumericProblem(problem(), true));
103
+ let old_chi = old.0.chi_for_coefs(&old.0.coefs);
104
+ let new_chi = analytic.chi_for_coefs(&analytic.coefs);
105
+ println!("analytic: {report:?}; numerical: {reference:?}; omitted-chain-rule: {old_report:?}; old/new chi relative L2: {}", (&old_chi - &new_chi).norm()/new_chi.norm());
106
+ assert!(report.termination.was_successful());
107
+ assert!(reference.termination.was_successful());
108
+ let a = analytic.chi_for_coefs(&analytic.coefs);
109
+ let n = numeric.0.chi_for_coefs(&numeric.0.coefs);
110
+ let error = (&a - &n).norm() / n.norm();
111
+ assert!(error < 1e-6, "chi relative L2: {error}");
112
+ assert!((report.objective_function - reference.objective_function).abs() < 1e-10);
113
+ }
114
+
115
+ #[test]
116
+ fn legacy_lm_does_not_return_nonfinite_fit_as_success() {
117
+ let mut invalid = problem();
118
+ invalid.mu[0] = f64::NAN;
119
+ assert!(AUTOBK::solve_lm_problem(invalid).is_err());
120
+ }
@@ -442,13 +442,20 @@ impl AUTOBK {
442
442
  }
443
443
 
444
444
  fn solve_lm_problem(problem: AUTOBKSpline) -> Result<AUTOBKSpline, BackgroundError> {
445
- let (fit_result, _report) = LevenbergMarquardt::new()
445
+ let (fit_result, report) = LevenbergMarquardt::new()
446
446
  .with_gtol(1.0e-5)
447
447
  .with_ftol(1.0e-5)
448
448
  .with_xtol(1.0e-5)
449
449
  .with_stepbound(1.0e-5)
450
450
  .minimize(problem);
451
-
451
+ if !report.termination.was_successful() || !report.objective_function.is_finite() {
452
+ return Err(BackgroundError::OptimizationFailed {
453
+ reason: format!(
454
+ "legacy LM terminated with {:?} after {} evaluations",
455
+ report.termination, report.number_of_evaluations
456
+ ),
457
+ });
458
+ }
452
459
  Ok(fit_result)
453
460
  }
454
461
 
@@ -1215,11 +1222,24 @@ impl AUTOBKSpline {
1215
1222
  coefs: &DVector<f64>,
1216
1223
  clamp_scale_override: Option<f64>,
1217
1224
  ) -> DMatrix<f64> {
1218
- let scale = if self.nclamp != 0 {
1219
- clamp_scale_override.unwrap_or_else(|| self.clamp_scale(coefs))
1220
- } else {
1221
- 1.0
1222
- };
1225
+ // For a dynamic clamp s(c) * chi(c), include both terms of the
1226
+ // product rule. A frozen scale is used by the linear solver and has
1227
+ // zero derivative. Compute the shared residual only once per Jacobian.
1228
+ let dynamic =
1229
+ (self.clamp_len(self.kout.len()) > 0 && clamp_scale_override.is_none()).then(|| {
1230
+ let chi = self.chi_for_coefs(coefs);
1231
+ let head = self.fft_residual_head(&chi);
1232
+ (chi, head)
1233
+ });
1234
+ let scale = clamp_scale_override.unwrap_or_else(|| {
1235
+ dynamic.as_ref().map_or(1.0, |(_, head)| {
1236
+ if head.is_empty() {
1237
+ 1.0
1238
+ } else {
1239
+ 1.0 + 100.0 * head.norm_squared() / head.len() as f64
1240
+ }
1241
+ })
1242
+ });
1223
1243
 
1224
1244
  // `splev_jacobian` reads `coefs` only for its length (it sizes the
1225
1245
  // output and bounds-checks the write); every entry comes from
@@ -1262,13 +1282,20 @@ impl AUTOBKSpline {
1262
1282
  return out;
1263
1283
  }
1264
1284
 
1265
- let low_clamp = self.clamp_lo as f64 * scale * chi_der.view((0, 0), (nclamp, 1));
1285
+ let scale_der = dynamic.as_ref().map_or(0.0, |(_, head)| {
1286
+ if head.is_empty() {
1287
+ 0.0
1288
+ } else {
1289
+ 200.0 * head.dot(&out) / head.len() as f64
1290
+ }
1291
+ });
1266
1292
  let high_start = chi_der.len() - nclamp - 1;
1267
- let high_clamp =
1268
- self.clamp_hi as f64 * scale * chi_der.view((high_start, 0), (nclamp, 1));
1269
-
1270
- out.extend(low_clamp.data.as_vec().to_owned());
1271
- out.extend(high_clamp.data.as_vec().to_owned());
1293
+ for (start, weight) in [(0, self.clamp_lo), (high_start, self.clamp_hi)] {
1294
+ out.extend((start..start + nclamp).map(|i| {
1295
+ let chi = dynamic.as_ref().map_or(0.0, |(chi, _)| chi[i]);
1296
+ weight as f64 * (scale * chi_der[i] + scale_der * chi)
1297
+ }));
1298
+ }
1272
1299
  out
1273
1300
  })
1274
1301
  .collect::<Vec<DVector<f64>>>();
@@ -2035,3 +2062,7 @@ mod tests {
2035
2062
  Ok(())
2036
2063
  }
2037
2064
  }
2065
+
2066
+ #[cfg(test)]
2067
+ #[path = "background/legacy_clamp_tests.rs"]
2068
+ mod legacy_clamp_tests;
@@ -446,13 +446,20 @@ impl AUTOBK {
446
446
  }
447
447
 
448
448
  fn solve_lm_problem(problem: AUTOBKSpline) -> Result<AUTOBKSpline, BackgroundError> {
449
- let (fit_result, _report) = LevenbergMarquardt::new()
449
+ let (fit_result, report) = LevenbergMarquardt::new()
450
450
  .with_gtol(1.0e-5)
451
451
  .with_ftol(1.0e-5)
452
452
  .with_xtol(1.0e-5)
453
453
  .with_stepbound(1.0e-5)
454
454
  .minimize(problem);
455
-
455
+ if !report.termination.was_successful() || !report.objective_function.is_finite() {
456
+ return Err(BackgroundError::OptimizationFailed {
457
+ reason: format!(
458
+ "legacy LM terminated with {:?} after {} evaluations",
459
+ report.termination, report.number_of_evaluations
460
+ ),
461
+ });
462
+ }
456
463
  Ok(fit_result)
457
464
  }
458
465
 
@@ -1155,11 +1162,24 @@ impl AUTOBKSpline {
1155
1162
  coefs: &DVector<f64>,
1156
1163
  clamp_scale_override: Option<f64>,
1157
1164
  ) -> DMatrix<f64> {
1158
- let scale = if self.nclamp != 0 {
1159
- clamp_scale_override.unwrap_or_else(|| self.clamp_scale(coefs))
1160
- } else {
1161
- 1.0
1162
- };
1165
+ // For a dynamic clamp s(c) * chi(c), include both terms of the
1166
+ // product rule. A frozen scale is used by the linear solver and has
1167
+ // zero derivative. Compute the shared residual only once per Jacobian.
1168
+ let dynamic =
1169
+ (self.clamp_len(self.kout.len()) > 0 && clamp_scale_override.is_none()).then(|| {
1170
+ let chi = self.chi_for_coefs(coefs);
1171
+ let head = self.fft_residual_head(&chi);
1172
+ (chi, head)
1173
+ });
1174
+ let scale = clamp_scale_override.unwrap_or_else(|| {
1175
+ dynamic.as_ref().map_or(1.0, |(_, head)| {
1176
+ if head.is_empty() {
1177
+ 1.0
1178
+ } else {
1179
+ 1.0 + 100.0 * head.norm_squared() / head.len() as f64
1180
+ }
1181
+ })
1182
+ });
1163
1183
 
1164
1184
  let spline_jacobian = -splev_jacobian(
1165
1185
  self.knots.data.as_vec().clone(),
@@ -1187,13 +1207,20 @@ impl AUTOBKSpline {
1187
1207
  return out;
1188
1208
  }
1189
1209
 
1190
- let low_clamp = self.clamp_lo as f64 * scale * chi_der.view((0, 0), (nclamp, 1));
1210
+ let scale_der = dynamic.as_ref().map_or(0.0, |(_, head)| {
1211
+ if head.is_empty() {
1212
+ 0.0
1213
+ } else {
1214
+ 200.0 * head.dot(&out) / head.len() as f64
1215
+ }
1216
+ });
1191
1217
  let high_start = chi_der.len() - nclamp - 1;
1192
- let high_clamp =
1193
- self.clamp_hi as f64 * scale * chi_der.view((high_start, 0), (nclamp, 1));
1194
-
1195
- out.extend(low_clamp.data.as_vec().to_owned());
1196
- out.extend(high_clamp.data.as_vec().to_owned());
1218
+ for (start, weight) in [(0, self.clamp_lo), (high_start, self.clamp_hi)] {
1219
+ out.extend((start..start + nclamp).map(|i| {
1220
+ let chi = dynamic.as_ref().map_or(0.0, |(chi, _)| chi[i]);
1221
+ weight as f64 * (scale * chi_der[i] + scale_der * chi)
1222
+ }));
1223
+ }
1197
1224
  out
1198
1225
  })
1199
1226
  .collect::<Vec<DVector<f64>>>();
@@ -1907,3 +1934,7 @@ mod tests {
1907
1934
  Ok(())
1908
1935
  }
1909
1936
  }
1937
+
1938
+ #[cfg(test)]
1939
+ #[path = "background/legacy_clamp_tests.rs"]
1940
+ mod legacy_clamp_tests;
@@ -0,0 +1,13 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ /// Forward-transform sampling and window domain. The default backend retains
4
+ /// `Input`; the optional ndarray backend retains its extended `Larch` grid.
5
+ #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
6
+ pub enum FFTGrid {
7
+ /// Weight/window the supplied grid without resampling (historical default).
8
+ #[default]
9
+ Input,
10
+ /// Resample from k=0, extend the window through kmax+dk2, then truncate
11
+ /// the weighted chi and window to the measured range, as in Larch.
12
+ Larch,
13
+ }
@@ -25,6 +25,7 @@ pub mod background;
25
25
  pub mod background;
26
26
  pub mod bessel_i0;
27
27
  pub mod errors;
28
+ mod fft_grid;
28
29
  pub mod fitting;
29
30
  mod inverse_fft;
30
31
  pub mod io;
@@ -1006,6 +1006,22 @@ impl XASSpectrum {
1006
1006
  }
1007
1007
  }
1008
1008
 
1009
+ /// Wavenumbers for `kwin()`. In Larch mode these follow the resampled
1010
+ /// FFT grid, which can differ from the background's `k()` spacing.
1011
+ pub fn kwin_k(&self) -> Option<DVector<f64>> {
1012
+ let ft = self.xftf.as_ref()?;
1013
+ let len = ft.get_kwin()?.len();
1014
+ if ft.grid == super::xrayfft::FFTGrid::Larch {
1015
+ let step = *ft.get_kstep()?;
1016
+ Some(DVector::from_iterator(
1017
+ len,
1018
+ (0..len).map(|i| i as f64 * step),
1019
+ ))
1020
+ } else {
1021
+ Some(DVector::from_column_slice(self.k()?))
1022
+ }
1023
+ }
1024
+
1009
1025
  pub fn kwin(&self) -> Option<DVector<f64>> {
1010
1026
  #[cfg(feature = "ndarray-compat")]
1011
1027
  {