148 Commits

Author SHA1 Message Date
9f8eee5d08 fixed python bindings - only read headers of modules that are in the roi
All checks were successful
Build on RHEL8 / build (push) Successful in 2m51s
Build on RHEL9 / build (push) Successful in 2m53s
2025-06-16 11:07:00 +02:00
35114cde9d fatal error did not open any subfiles
All checks were successful
Build on RHEL8 / build (push) Successful in 2m56s
Build on RHEL9 / build (push) Successful in 2m59s
2025-06-13 18:12:47 +02:00
b13f864b2b need n_modules 2025-06-13 17:01:13 +02:00
05828baa54 removed n_modules from python bindings 2025-06-13 16:38:46 +02:00
0f56846e3d deleted some commented lines 2025-06-13 16:33:25 +02:00
be67bbab6b extended DetectorGeometry class with find_geometry, update_geometry (refactoring) 2025-06-13 16:16:23 +02:00
bd7870e75a review comments
All checks were successful
Build on RHEL9 / build (push) Successful in 2m53s
Build on RHEL8 / build (push) Successful in 2m55s
2025-06-12 18:14:48 +02:00
75f63607fc friend_test macro 2025-06-12 17:46:10 +02:00
cfe7c31fe4 changed data path of test data
All checks were successful
Build on RHEL9 / build (push) Successful in 2m54s
Build on RHEL8 / build (push) Successful in 2m55s
2025-06-12 09:53:15 +02:00
a9a55fb27d Merge branch 'main' into dev/fix/rawfilereader_with_roi
All checks were successful
Build on RHEL8 / build (push) Successful in 2m57s
Build on RHEL9 / build (push) Successful in 3m1s
2025-06-11 13:23:01 +02:00
3cc44f780f Added branching strategy etc. to docs (#191)
All checks were successful
Build on RHEL9 / build (push) Successful in 2m56s
Build on RHEL8 / build (push) Successful in 2m57s
Added a section on the ideas behind the library and also explaining the
branching strategy.

---------

Co-authored-by: Dhanya Thattil <dhanya.thattil@psi.ch>
2025-06-11 13:21:21 +02:00
19ecc82fff solved merge conflict
All checks were successful
Build on RHEL8 / build (push) Successful in 2m52s
Build on RHEL9 / build (push) Successful in 3m12s
2025-06-10 17:01:40 +02:00
2a069f3b6e formatted main branch (#195)
All checks were successful
Build on RHEL8 / build (push) Successful in 2m53s
Build on RHEL9 / build (push) Successful in 3m7s
2025-06-10 16:24:11 +02:00
f9751902a2 formatted main branch 2025-06-10 16:09:06 +02:00
923f7d22b8 Merge branch 'main' into dev/fix/rawfilereader_with_roi 2025-06-10 15:59:52 +02:00
6438a4bef1 updated python bindings
All checks were successful
Build on RHEL9 / build (push) Successful in 2m21s
Build on RHEL8 / build (push) Successful in 2m29s
2025-06-10 12:00:07 +02:00
ad7525cd02 considered num_udp_interafces for jungfrau and quad structure for eiger 2025-06-10 11:35:15 +02:00
87d8682b1e added test cases 2025-06-06 11:31:39 +02:00
efd2338f54 deploy docs on release only
All checks were successful
Build on RHEL8 / build (push) Successful in 2m56s
Build on RHEL9 / build (push) Successful in 2m57s
2025-06-05 14:55:00 +02:00
b97f1e24f9 merged developer 2025-06-05 14:42:37 +02:00
1bc2fd770a Binding 5x5, 7x7 and 9x9 clusters in python (#188)
All checks were successful
Build on RHEL8 / build (push) Successful in 2m55s
Build on RHEL9 / build (push) Successful in 2m58s
- New binding code with macros to bind all cluster templates
- Simplified factory function on the python side
- 5x5, 7x7 and 9x9 bindings in python
2025-06-05 08:57:59 +02:00
9c6e629298 only files within the ROI are opened & geometry always read in RawMasterFile 2025-06-04 16:34:40 +02:00
69964e08d5 Refactor cluster bindings (#185)
All checks were successful
Build on RHEL9 / build (push) Successful in 2m19s
Build on RHEL8 / build (push) Successful in 2m34s
- Split up the file for cluster bindings
- new file names according to bind_ClassName.hpp
2025-06-03 08:43:40 +02:00
94ac58b09e For 2025.5.22 release (#181)
All checks were successful
Build on RHEL9 / build (push) Successful in 2m22s
Build on RHEL8 / build (push) Successful in 2m29s
Co-authored-by: Patrick <patrick.sieberer@psi.ch>
Co-authored-by: JulianHeymes <julian.heymes@psi.ch>
Co-authored-by: Dhanya Thattil <dhanya.thattil@psi.ch>
Co-authored-by: Xiangyu Xie <45243914+xiangyuxie@users.noreply.github.com>
Co-authored-by: xiangyu.xie <xiangyu.xie@psi.ch>
Co-authored-by: AliceMazzoleni99 <alice.mazzoleni@psi.ch>
Co-authored-by: Mazzoleni Alice Francesca <mazzol_a@pc17378.psi.ch>
Co-authored-by: siebsi <sieb.patr@gmail.com>
2025-05-22 11:40:39 +02:00
9ecf4f4b44 merge
All checks were successful
Build on RHEL9 / build (push) Successful in 2m22s
Build on RHEL8 / build (push) Successful in 2m30s
2025-05-22 11:23:57 +02:00
f2a024644b bumped version upload on release 2025-05-22 11:10:23 +02:00
9e1b8731b0 RawSubFile support multi file access (#173)
This PR is a fix/improvement to a problem that Jonathan had. (#156) The
original implementation opened all subfiles at once witch works for
normal sized datasets but fails at a certain point (thousands of files).

- This solution uses RawSubFile to manage the different file indicies
and only opens the file we need
- Added logger.h from slsDetectorPackage for debug printing (in
production no messages should be visible)
2025-05-22 11:00:03 +02:00
a6eebbe9bd removed extra const on return type, added cast (#177)
All checks were successful
Build on RHEL9 / build (push) Successful in 2m31s
Build on RHEL8 / build (push) Successful in 2m34s
Fixed warnings on apple clang:

- removed extra const on return type
- added cast to suppress a float to double conversion warning
2025-05-20 15:27:38 +02:00
81588fba3b linking to threads and removed extra ; (#176)
All checks were successful
Build on RHEL9 / build (push) Successful in 2m14s
Build on RHEL8 / build (push) Successful in 2m32s
- Fixing broken build of tests on RH8 by linking pthreads
- Removed extra ; causing warnings with -Wpedantic
2025-05-06 17:18:54 +02:00
276283ff14 automated versioning (#175)
Some checks failed
Build on RHEL9 / build (push) Successful in 2m20s
Build on RHEL8 / build (push) Failing after 2m24s
Co-authored-by: mazzol_a <mazzol_a@pc17378.psi.ch>
Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2025-05-06 14:48:54 +02:00
cf158e2dcd Added scurve fitting (#168)
Some checks failed
Build on RHEL9 / build (push) Successful in 2m21s
Build on RHEL8 / build (push) Failing after 2m26s
- added scurve fitting with two different signs (scurve, scurve2)
- at the moment no option to set initial parameters

---------

Co-authored-by: JulianHeymes <julian.heymes@psi.ch>
2025-05-05 11:40:04 +02:00
12ae1424fb consistent use of ssize_t instead of int64_t (#167)
Some checks failed
Build on RHEL9 / build (push) Successful in 2m10s
Build on RHEL8 / build (push) Failing after 2m33s
- Consistent use of ssize_t to avoid issues on 32 bit platforms and also
mac with (long long int as ssize_t)
2025-04-25 15:52:02 +02:00
6db201f397 updated conda environment (#169)
- updated dev-env.yml conda environment file
- added boost-histogram as a requirement for the python tests
- added environment file in conda build process
2025-04-25 15:24:45 +02:00
d5226909fe Api cluster vector (#147)
Cluster is newly templated on ClusterSize, Cluster data type and cluster coordinate type, accepting arbitrary cluster sizes.
2025-04-25 12:29:39 +02:00
eb6862ff99 changed name of GainMap to InvertedGainMap
Some checks failed
Build on RHEL9 / build (push) Successful in 2m16s
Build on RHEL8 / build (push) Failing after 2m34s
2025-04-25 12:03:59 +02:00
f06e722dce changes from PR review 2025-04-25 11:38:56 +02:00
2e0424254c removed uneccecary codna numpy variants (#165)
With numpy 2.0 we no longer need to build against every supported numpy
version. This way we can save up to 6 builds.

- https://numpy.org/doc/stable/dev/depending_on_numpy.html
-
https://conda-forge.org/docs/maintainer/knowledge_base/#building-against-numpy
2025-04-25 10:31:40 +02:00
7b5e32a824 Api extra (#166)
Changes to be able to run the example notebooks: 

- Invert gain map on setting (multiplication is faster but user supplies
ADU/energy)
- Cast after applying gain map not to loose precision (Important for
int32 clusters)
- "factor" for ClusterFileSink 
- Cluster size available to be able to create the right file sink
2025-04-25 10:31:16 +02:00
86d343f5f5 merged with developer
Some checks failed
Build on RHEL9 / build (push) Successful in 2m9s
Build on RHEL8 / build (push) Failing after 2m32s
2025-04-23 11:45:04 +02:00
129e7e9f9d Merge branch 'developer' of github.com:slsdetectorgroup/aare into developer
All checks were successful
Build on RHEL9 / build (push) Successful in 1m59s
Build on RHEL8 / build (push) Successful in 2m29s
2025-04-22 16:24:32 +02:00
58c934d9cf added mpl to conda specs 2025-04-22 16:24:15 +02:00
4088b0889d Merge branch 'main' into developer 2025-04-22 16:18:48 +02:00
d5f8daf194 removed debug option in CMakelist
All checks were successful
Build on RHEL9 / buildh (push) Successful in 2m36s
2025-04-22 16:16:31 +02:00
c6e8e5f6a1 inverted gain map 2025-04-22 16:16:27 +02:00
b501c31e38 added missed commit 2025-04-22 15:22:47 +02:00
326941e2b4 Custom base for decoding ADC data (#163)
New function apply_custom_weights (can we find a better name) that takes
a uint16 and a NDView<double,1> of bases for the conversion. For each
supplied weight it is used as base (instead of 2) to convert from bits
to a double.

---------

Co-authored-by: siebsi <sieb.patr@gmail.com>
2025-04-22 15:20:46 +02:00
84aafa75f6 Building wheels and uploading to pypi (#160)
All checks were successful
Build on RHEL9 / build (push) Successful in 1m56s
Build on RHEL8 / build (push) Successful in 2m13s
Still to be resolved in another PR: 

- Consistent versioning across compiled code, conda and pypi
2025-04-22 08:36:34 +02:00
177459c98a added multithreaded cluster finder test
All checks were successful
Build on RHEL9 / buildh (push) Successful in 2m20s
2025-04-17 17:09:53 +02:00
c49a2fdf8e removed cluster_2x2 and cluster3x3 specializations
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m58s
2025-04-16 16:40:42 +02:00
14211047ff added function warpper around ClusterFinderMT and ClusterCollector to construct object 2025-04-16 14:22:44 +02:00
acd9d5d487 moved parts of ClusterFile implementation into declaration
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m55s
2025-04-15 15:15:34 +02:00
d4050ec557 enum is now enum class 2025-04-15 14:57:25 +02:00
fca9d5d2fa replaced extract template parameters 2025-04-15 14:40:09 +02:00
1174f7f434 fixed calculate eta 2025-04-15 13:18:25 +02:00
2bb7d360bf Adding more tests, fixing hitmap and reading with cuts (#161)
- Fix for hitmap
- Fix for reading clusters with cut
- Added more tests around eta
- Added factory function for creating the cluster finder
2025-04-15 12:25:01 +02:00
a90e532b21 removed extra sum after merge
Some checks failed
Build on RHEL9 / buildh (push) Failing after 1m57s
2025-04-15 08:08:59 +02:00
8d8182c632 qMerge branch 'testing_clusters' of github.com:slsdetectorgroup/aare into testing_clusters 2025-04-15 08:05:12 +02:00
5f34ab6df1 minor comment 2025-04-15 08:05:05 +02:00
5c8a5099fd Merge branch 'api_cluster_vector' into testing_clusters 2025-04-14 16:40:47 +02:00
7c93632605 tests and fix 2025-04-14 16:38:25 +02:00
54def26334 added ClusterFile tests fixed some bugs in ClusterFile
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m55s
2025-04-14 15:48:09 +02:00
a59e9656be Making RawSubFile usable from Python (#158)
All checks were successful
Build on RHEL8 / build (push) Successful in 1m55s
Build on RHEL9 / build (push) Successful in 1m44s
- Removed a printout left from debugging
- return also header when reading
- added read_n 
- check for error in ifstream
2025-04-11 16:54:21 +02:00
3f753ec900 Some fixes (need more testing later) (#159)
Some checks failed
Build on RHEL9 / buildh (push) Failing after 1m51s
- Change of pointer size caused out of bounds write
- UB to write to memory reserved by std::vector::reserver --> allocate
dummy clusters by using resize instead
   - but now we can't reserve like we want to, need a fix. 
- format string not working, fixed
2025-04-11 14:43:12 +02:00
15e52565a9 dont convert to byte 2025-04-11 14:35:20 +02:00
e71569b15e resize before read 2025-04-11 13:38:33 +02:00
92f5421481 np test 2025-04-10 16:58:47 +02:00
113f34cc98 fixes 2025-04-10 16:50:04 +02:00
53a90e197e added additional tests
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m52s
2025-04-10 10:41:58 +02:00
6e4db45b57 Activated RH8 build on PSI gitea (#155)
All checks were successful
Build on RHEL8 / build (push) Successful in 1m56s
Build on RHEL9 / build (push) Successful in 1m44s
2025-04-10 10:17:16 +02:00
76f050f69f solved merge conflict
Some checks failed
Build on RHEL9 / buildh (push) Failing after 1m22s
2025-04-10 09:21:50 +02:00
a13affa4d3 changed template arguments added tests 2025-04-10 09:13:58 +02:00
8b0eee1e66 fixed warnings and removed ambiguous read_frame (#154)
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m47s
Fixed warnings:
- unused variable in Interpolator
- Narrowing conversions uint64-->int64

Removed an ambiguous function from JungfrauDataFile
- NDarry read_frame(header&=nullptr)
- Frame read_frame()

NDArray and NDView size() is now signed
2025-04-09 17:54:55 +02:00
894065fe9c added utility plot
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m48s
2025-04-09 12:19:14 +02:00
f16273a566 Adding support for Jungfrau .dat files (#152)
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m48s
closes #150 

**Not addressed in this PR:** 

- pixels_per_frame, bytes_per_frame and tell should be made cost in
FileInterface
2025-04-08 15:31:04 +02:00
20d1d02fda function signature for push back (#153)
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 48s
This example now works:
```python
cl = Cluster3x3i(5,7,np.array((1,2,3,4,5,6,7,8,9), dtype = np.int32))
cv = ClusterVector_Cluster3x3i()
cv.push_back(cl)
```
2025-04-07 17:18:17 +02:00
10e4e10431 function signature for push back 2025-04-07 15:33:37 +02:00
017960d963 added push_back property
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 37s
2025-04-07 13:41:14 +02:00
a12e43b176 underlying container of ClusterVcetor is now a std::vector 2025-04-07 12:27:44 +02:00
9de84a7f87 added some python tests
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 41s
2025-04-04 17:19:15 +02:00
885309d97c fix build
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 43s
2025-04-03 17:14:28 +02:00
e24ed68416 fixed include 2025-04-03 16:50:02 +02:00
248d25486f refactored python files 2025-04-03 16:38:12 +02:00
7db1ae4d94 Dev/gitea ci (#151)
All checks were successful
Build on RHEL9 / buildh (push) Successful in 1m41s
Build and test on internal PSI gitea
2025-04-03 13:18:55 +02:00
a24bbd9cf9 started to do python refactoring
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 44s
2025-04-03 11:56:25 +02:00
d7ef9bb1d8 missed some refactoring of datatypes
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 49s
2025-04-03 11:36:15 +02:00
de9fc16e89 generalize is_selected 2025-04-03 09:28:54 +02:00
85a6b5b95e suppress compiler warnings 2025-04-03 09:28:02 +02:00
50eeba4005 restructured GainMap to have own class and generalized
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 40s
2025-04-02 17:58:26 +02:00
98d2d6098e refactored other cpp files 2025-04-02 16:00:46 +02:00
61af1105a1 templated eta and updated test 2025-04-02 14:42:38 +02:00
240960d3e7 generalized FindCluster to read in general cluster sizes - assuming that finding cluster center is equal for all clusters 2025-04-02 12:05:16 +02:00
04728929cb implemented sum_2x2() for general clusters, only one calculate_eta2 function for all clusters
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 37s
2025-04-01 18:29:08 +02:00
3083d51699 merge conflict 2025-04-01 17:50:11 +02:00
4240942cec solved merge conflict 2025-04-01 17:48:48 +02:00
745d09fbe9 changed push_back to take Cluster as input argument 2025-04-01 15:30:10 +02:00
8cad7a50a6 fixed py
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 42s
2025-04-01 15:00:03 +02:00
9d8e803474 Merge branch 'main' into developer 2025-04-01 14:35:27 +02:00
a42c0d645b added roi, noise and gain (#143)
- Moved definitions of Cluster_2x2 and Cluster_3x3 to it's own file
- Added optional members for ROI, noise_map and gain_map in ClusterFile

**API:**

After creating the ClusterFile the user can set one or all of: roi,
noise_map, gain_map

```python
f = ClusterFile(fname)
f.set_roi(roi) #aare.ROI
f.set_noise_map(noise_map) #numpy array
f.set_gain_map(gain_map) #numpy array
```

**When reading clusters they are evaluated in the order:**

1. If ROI is enabled check that the cluster is within the ROI
1. If noise_map is enabled check that the cluster meets one of the
conditions
    - Center pixel above noise
    - Highest 2x2 sum above 2x noise
    - 3x3 sum above 3x noise
1. If gain_map is set apply the gain map before returning the clusters
(not used for noise cut)

**Open questions:**
1. Check for out of bounds access in noise and gain map?

closes #139 
closes #135 
closes #90
2025-04-01 14:31:25 +02:00
508adf5016 refactoring of remaining files
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 40s
Build the package using cmake then documentation / deploy (push) Has been skipped
2025-04-01 10:01:23 +02:00
e038bd1646 refactored and put calculate_eta function in seperate file 2025-03-31 17:35:39 +02:00
7e5f91c6ec added benchmark to time generalize calculate_eta - twice as long so will keep specific version for 2x2 and 3x3 clusters 2025-03-31 17:04:57 +02:00
ed9ef7c600 removed analyze_cluster function as not used anymore
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 52s
Build the package using cmake then documentation / deploy (push) Has been skipped
2025-03-31 12:26:29 +02:00
57bb6c71ae ClusterSize should be larger than 1
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 51s
Build the package using cmake then documentation / deploy (push) Has been skipped
2025-03-28 14:49:55 +01:00
f8f98b6ec3 Generalized calculate_eta2 function to work with general cluster types 2025-03-28 14:29:20 +01:00
0876b6891a cpp Cluster and ClusterVector and ClusterFile are templated now, they support generic cluster types 2025-03-25 21:42:50 +01:00
6ad76f63c1 Fixed reading clusters with ROI (#142)
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 9s
Fixed incorrect reading of clusters with ROI


closes #141
2025-03-24 14:28:10 +01:00
6e7e81b36b complete mess but need to install RedHat 9 2025-03-21 16:32:54 +01:00
b529b6d33b Merge branch 'main' into developer
All checks were successful
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Successful in 1m33s
2025-03-19 19:29:15 +01:00
602b04e49f bumped version number
All checks were successful
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Successful in 1m35s
2025-03-18 17:47:05 +01:00
11cd2ec654 Interpolate (#137)
- added eta based interpolation
2025-03-18 17:45:38 +01:00
e59a361b51 removed workspace
Some checks failed
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Failing after 48s
Build the package using cmake then documentation / deploy (push) Has been skipped
2025-03-17 15:23:55 +01:00
1ad362ccfc added action for gitea (#136)
All checks were successful
Build the package using cmake then documentation / build (ubuntu-latest, 3.12) (push) Successful in 1m30s
2025-03-17 15:21:59 +01:00
332bdeb02b modified algo 2025-03-14 11:07:09 +01:00
3a987319d4 WIP 2025-03-05 21:51:23 +01:00
5614cb4673 WIP 2025-03-05 17:40:08 +01:00
8ae6bb76f8 removed warnings added clang-tidy 2025-02-21 11:18:39 +01:00
1d2c38c1d4 Enable VarClusterFinder (#134)
Co-authored-by: xiangyu.xie <xiangyu.xie@psi.ch>
2025-02-19 16:11:24 +01:00
fc1c9f35d6 Merge branch 'main' into developer 2025-02-18 21:52:20 +01:00
5d2f25a6e9 bumped version number 2025-02-18 21:44:03 +01:00
6a83988485 Added chi2 to fit results (#131)
- fit_gaus and fit_pol1 now return a dict
- calculate chi2 after fit
- cleaned up code
2025-02-18 21:13:27 +01:00
8abfc68138 fixed linking to lmfit (#130)
using "$<BUILD_INTERFACE:lmfit>" to exclude the target lmfit from being
included in the installed aare target
2025-02-18 15:54:52 +01:00
8ff6f9f506 fixed linking to lmfit 2025-02-18 15:49:46 +01:00
dcb9a98faa bumped version 2025-02-12 16:49:30 +01:00
7309cff47c Added fitting with lmfit (#128)
- added stand alone fitting using:
https://jugit.fz-juelich.de/mlz/lmfit.git
- fit_gaus, fit_pol1 with and without errors
- multi threaded fitting

---------

Co-authored-by: JulianHeymes <julian.heymes@psi.ch>
2025-02-12 16:35:48 +01:00
c0c5e07ad8 added decoding of adc_sar_04 (#127) 2025-02-12 16:17:32 +01:00
2faa317bdf removed debug line 2025-02-12 10:59:18 +01:00
f7031d7f87 Update CMakeLists.txt
Removed flto=auto which caused issues with gcc 8.5
2025-02-12 10:52:55 +01:00
d86cb533c8 Fix minor warnings (#126)
- Unused variables
- signed vs. unsigned
- added -flto=auto
2025-02-11 11:48:01 +01:00
4c750cc3be Fixing ROI read of RawFile (#125)
- Bugfixes
- New abstraction for detector geometry
- Tests for updating geo with ROI
2025-02-11 11:08:22 +01:00
e96fe31f11 removed main and token 2025-02-05 15:55:55 +01:00
cd5a738696 disable upload on dev 2025-02-05 15:44:45 +01:00
1ba43b69d3 fix 2025-02-05 15:16:16 +01:00
fff536782b disable auto upload 2025-02-05 15:13:53 +01:00
5a3ca2ae2d Decoding for ADC SAR 05 64->16bit (#124)
Co-authored-by: Patrick <patrick.sieberer@psi.ch>
2025-02-05 14:40:26 +01:00
078e5d81ec docs 2025-01-15 16:40:34 +01:00
6cde968c60 summing 2x2 2025-01-15 16:12:06 +01:00
f6d736facd docs for ClusterFile 2025-01-15 09:15:41 +01:00
e1cc774d6c Multi threaded cluster finder (#117) 2025-01-14 21:36:25 +01:00
d0f435a7ab bounds checking on subfiles 2025-01-10 19:02:50 +01:00
7ce02006f2 clear pedestal 2025-01-10 17:26:23 +01:00
7550a2cb97 fixing read bug 2025-01-10 15:33:56 +01:00
caf7b4ecdb added docs for ClusterFinderMT 2025-01-10 10:22:04 +01:00
72d10b7735 Multi threaded cluster finder. (#115)
Added a prototype for the multi threaded cluster finder including python
bindings
2025-01-09 16:55:35 +01:00
cc95561eda MultiThreaded Cluster finder 2025-01-09 16:53:22 +01:00
dc9e10016d WIP 2025-01-08 16:45:24 +01:00
21ce7a3efa bumped version 2025-01-07 16:33:16 +01:00
acdce8454b moved pd to double 2025-01-07 15:01:43 +01:00
d07da42745 bitdepths 2025-01-07 12:27:01 +01:00
139 changed files with 6407 additions and 3340 deletions

View File

@ -1,9 +1,9 @@
name: Build pkgs and deploy if on main name: Build pkgs and deploy if on main
on: on:
push: release:
branches: types:
- main - published
jobs: jobs:
build: build:
@ -24,13 +24,13 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Get conda - name: Get conda
uses: conda-incubator/setup-miniconda@v3.0.4 uses: conda-incubator/setup-miniconda@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
environment-file: etc/dev-env.yml
miniforge-version: latest
channels: conda-forge channels: conda-forge
conda-remove-defaults: "true"
- name: Prepare
run: conda install conda-build=24.9 conda-verify pytest anaconda-client
- name: Enable upload - name: Enable upload
run: conda config --set anaconda_upload yes run: conda config --set anaconda_upload yes

View File

@ -24,13 +24,14 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Get conda - name: Get conda
uses: conda-incubator/setup-miniconda@v3.0.4 uses: conda-incubator/setup-miniconda@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
environment-file: etc/dev-env.yml
miniforge-version: latest
channels: conda-forge channels: conda-forge
conda-remove-defaults: "true"
- name: Prepare
run: conda install conda-build=24.9 conda-verify pytest anaconda-client
- name: Disable upload - name: Disable upload
run: conda config --set anaconda_upload no run: conda config --set anaconda_upload no

View File

@ -2,7 +2,10 @@ name: Build the package using cmake then documentation
on: on:
workflow_dispatch: workflow_dispatch:
push: pull_request:
release:
types:
- published
permissions: permissions:
@ -55,7 +58,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }} url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build needs: build
if: github.ref == 'refs/heads/main' if: (github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_dispatch' )
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment

View File

@ -1,12 +1,17 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(aare project(aare
VERSION 1.0.0
DESCRIPTION "Data processing library for PSI detectors" DESCRIPTION "Data processing library for PSI detectors"
HOMEPAGE_URL "https://github.com/slsdetectorgroup/aare" HOMEPAGE_URL "https://github.com/slsdetectorgroup/aare"
LANGUAGES C CXX LANGUAGES C CXX
) )
# Read VERSION file into project version
set(VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/VERSION")
file(READ "${VERSION_FILE}" VERSION_CONTENT)
string(STRIP "${VERSION_CONTENT}" PROJECT_VERSION_STRING)
set(PROJECT_VERSION ${PROJECT_VERSION_STRING})
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
@ -39,7 +44,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
# General options # General options
option(AARE_PYTHON_BINDINGS "Build python bindings" ON) option(AARE_PYTHON_BINDINGS "Build python bindings" OFF)
option(AARE_TESTS "Build tests" OFF) option(AARE_TESTS "Build tests" OFF)
option(AARE_BENCHMARKS "Build benchmarks" OFF) option(AARE_BENCHMARKS "Build benchmarks" OFF)
option(AARE_EXAMPLES "Build examples" OFF) option(AARE_EXAMPLES "Build examples" OFF)
@ -74,6 +79,9 @@ endif()
if(AARE_VERBOSE) if(AARE_VERBOSE)
add_compile_definitions(AARE_VERBOSE) add_compile_definitions(AARE_VERBOSE)
add_compile_definitions(AARE_LOG_LEVEL=aare::logDEBUG5)
else()
add_compile_definitions(AARE_LOG_LEVEL=aare::logERROR)
endif() endif()
if(AARE_CUSTOM_ASSERT) if(AARE_CUSTOM_ASSERT)
@ -85,6 +93,7 @@ if(AARE_BENCHMARKS)
endif() endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(AARE_FETCH_LMFIT) if(AARE_FETCH_LMFIT)
@ -326,13 +335,10 @@ if(AARE_ASAN)
) )
endif() endif()
if(AARE_TESTS) if(AARE_TESTS)
enable_testing() enable_testing()
add_subdirectory(tests) add_subdirectory(tests)
target_compile_definitions(tests PRIVATE AARE_TESTS)
endif() endif()
###------------------------------------------------------------------------------MAIN LIBRARY ###------------------------------------------------------------------------------MAIN LIBRARY
@ -340,6 +346,8 @@ endif()
set(PUBLICHEADERS set(PUBLICHEADERS
include/aare/ArrayExpr.hpp include/aare/ArrayExpr.hpp
include/aare/CalculateEta.hpp
include/aare/Cluster.hpp
include/aare/ClusterFinder.hpp include/aare/ClusterFinder.hpp
include/aare/ClusterFile.hpp include/aare/ClusterFile.hpp
include/aare/CtbRawFile.hpp include/aare/CtbRawFile.hpp
@ -352,7 +360,8 @@ set(PUBLICHEADERS
include/aare/FileInterface.hpp include/aare/FileInterface.hpp
include/aare/FilePtr.hpp include/aare/FilePtr.hpp
include/aare/Frame.hpp include/aare/Frame.hpp
include/aare/geo_helpers.hpp include/aare/GainMap.hpp
include/aare/DetectorGeometry.hpp
include/aare/JungfrauDataFile.hpp include/aare/JungfrauDataFile.hpp
include/aare/NDArray.hpp include/aare/NDArray.hpp
include/aare/NDView.hpp include/aare/NDView.hpp
@ -365,13 +374,11 @@ set(PUBLICHEADERS
include/aare/RawSubFile.hpp include/aare/RawSubFile.hpp
include/aare/VarClusterFinder.hpp include/aare/VarClusterFinder.hpp
include/aare/utils/task.hpp include/aare/utils/task.hpp
) )
set(SourceFiles set(SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/CtbRawFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/CtbRawFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/decode.cpp
@ -379,7 +386,7 @@ set(SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/File.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/File.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/FilePtr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/FilePtr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Fit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Fit.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp
@ -388,19 +395,18 @@ set(SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/ifstream_helpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/ifstream_helpers.cpp
) )
add_library(aare_core STATIC ${SourceFiles}) add_library(aare_core STATIC ${SourceFiles})
target_include_directories(aare_core PUBLIC target_include_directories(aare_core PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
) )
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries( target_link_libraries(
aare_core aare_core
@ -410,10 +416,15 @@ target_link_libraries(
${STD_FS_LIB} # from helpers.cmake ${STD_FS_LIB} # from helpers.cmake
PRIVATE PRIVATE
aare_compiler_flags aare_compiler_flags
"$<BUILD_INTERFACE:lmfit>" Threads::Threads
$<BUILD_INTERFACE:lmfit>
) )
if(AARE_TESTS)
target_compile_definitions(aare_core PRIVATE AARE_TESTS)
endif()
set_target_properties(aare_core PROPERTIES set_target_properties(aare_core PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
PUBLIC_HEADER "${PUBLICHEADERS}" PUBLIC_HEADER "${PUBLICHEADERS}"
@ -430,18 +441,22 @@ if(AARE_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/decode.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NDArray.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NDArray.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NDView.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NDView.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFinder.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFinder.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterVector.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterVector.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Cluster.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/CalculateEta.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFinderMT.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Pedestal.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pedestal.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.test.cpp
) )

22
RELEASE.md Normal file
View File

@ -0,0 +1,22 @@
# Release notes
### head
Features:
- Cluster finder now works with 5x5, 7x7 and 9x9 clusters
### 2025.05.22
Features:
- Added scurve fitting
Bugfixes:
- Fixed crash when opening raw files with large number of data files

1
VERSION Normal file
View File

@ -0,0 +1 @@
2025.5.22

View File

@ -1,11 +1,27 @@
find_package(benchmark REQUIRED)
add_executable(ndarray_benchmark ndarray_benchmark.cpp) include(FetchContent)
target_link_libraries(ndarray_benchmark benchmark::benchmark aare_core aare_compiler_flags)
# target_link_libraries(tests PRIVATE aare_core aare_compiler_flags)
set_target_properties(ndarray_benchmark PROPERTIES FetchContent_Declare(
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} benchmark
# OUTPUT_NAME run_tests GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.8.3 # Change to the latest version if needed
)
# Ensure Google Benchmark is built correctly
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(benchmark)
add_executable(benchmarks)
target_sources(benchmarks PRIVATE ndarray_benchmark.cpp calculateeta_benchmark.cpp)
# Link Google Benchmark and other necessary libraries
target_link_libraries(benchmarks PRIVATE benchmark::benchmark aare_core aare_compiler_flags)
# Set output properties
set_target_properties(benchmarks PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
OUTPUT_NAME run_benchmarks
) )

View File

@ -0,0 +1,70 @@
#include "aare/CalculateEta.hpp"
#include "aare/ClusterFile.hpp"
#include <benchmark/benchmark.h>
using namespace aare;
class ClusterFixture : public benchmark::Fixture {
public:
Cluster<int, 2, 2> cluster_2x2{};
Cluster<int, 3, 3> cluster_3x3{};
private:
using benchmark::Fixture::SetUp;
void SetUp([[maybe_unused]] const benchmark::State &state) override {
int temp_data[4] = {1, 2, 3, 1};
std::copy(std::begin(temp_data), std::end(temp_data),
std::begin(cluster_2x2.data));
cluster_2x2.x = 0;
cluster_2x2.y = 0;
int temp_data2[9] = {1, 2, 3, 1, 3, 4, 5, 1, 20};
std::copy(std::begin(temp_data2), std::end(temp_data2),
std::begin(cluster_3x3.data));
cluster_3x3.x = 0;
cluster_3x3.y = 0;
}
// void TearDown(::benchmark::State& state) {
// }
};
BENCHMARK_F(ClusterFixture, Calculate2x2Eta)(benchmark::State &st) {
for (auto _ : st) {
// This code gets timed
Eta2 eta = calculate_eta2(cluster_2x2);
benchmark::DoNotOptimize(eta);
}
}
// almost takes double the time
BENCHMARK_F(ClusterFixture, CalculateGeneralEtaFor2x2Cluster)
(benchmark::State &st) {
for (auto _ : st) {
// This code gets timed
Eta2 eta = calculate_eta2<int, 2, 2>(cluster_2x2);
benchmark::DoNotOptimize(eta);
}
}
BENCHMARK_F(ClusterFixture, Calculate3x3Eta)(benchmark::State &st) {
for (auto _ : st) {
// This code gets timed
Eta2 eta = calculate_eta2(cluster_3x3);
benchmark::DoNotOptimize(eta);
}
}
// almost takes double the time
BENCHMARK_F(ClusterFixture, CalculateGeneralEtaFor3x3Cluster)
(benchmark::State &st) {
for (auto _ : st) {
// This code gets timed
Eta2 eta = calculate_eta2<int, 3, 3>(cluster_3x3);
benchmark::DoNotOptimize(eta);
}
}
// BENCHMARK_MAIN();

View File

@ -1,136 +1,132 @@
#include <benchmark/benchmark.h>
#include "aare/NDArray.hpp" #include "aare/NDArray.hpp"
#include <benchmark/benchmark.h>
using aare::NDArray; using aare::NDArray;
constexpr ssize_t size = 1024; constexpr ssize_t size = 1024;
class TwoArrays : public benchmark::Fixture { class TwoArrays : public benchmark::Fixture {
public: public:
NDArray<int,2> a{{size,size},0}; NDArray<int, 2> a{{size, size}, 0};
NDArray<int,2> b{{size,size},0}; NDArray<int, 2> b{{size, size}, 0};
void SetUp(::benchmark::State& state) { void SetUp(::benchmark::State &state) {
for(uint32_t i = 0; i < size; i++){ for (uint32_t i = 0; i < size; i++) {
for(uint32_t j = 0; j < size; j++){ for (uint32_t j = 0; j < size; j++) {
a(i, j)= i*j+1; a(i, j) = i * j + 1;
b(i, j)= i*j+1; b(i, j) = i * j + 1;
} }
}
} }
}
// void TearDown(::benchmark::State& state) { // void TearDown(::benchmark::State& state) {
// } // }
}; };
BENCHMARK_F(TwoArrays, AddWithOperator)(benchmark::State &st) {
for (auto _ : st) {
// This code gets timed
BENCHMARK_F(TwoArrays, AddWithOperator)(benchmark::State& st) { NDArray<int, 2> res = a + b;
for (auto _ : st) { benchmark::DoNotOptimize(res);
// This code gets timed }
NDArray<int,2> res = a+b; }
benchmark::DoNotOptimize(res); BENCHMARK_F(TwoArrays, AddWithIndex)(benchmark::State &st) {
} for (auto _ : st) {
} // This code gets timed
BENCHMARK_F(TwoArrays, AddWithIndex)(benchmark::State& st) { NDArray<int, 2> res(a.shape());
for (auto _ : st) { for (uint32_t i = 0; i < a.size(); i++) {
// This code gets timed res(i) = a(i) + b(i);
NDArray<int,2> res(a.shape()); }
for (uint32_t i = 0; i < a.size(); i++) { benchmark::DoNotOptimize(res);
res(i) = a(i) + b(i);
} }
benchmark::DoNotOptimize(res);
}
} }
BENCHMARK_F(TwoArrays, SubtractWithOperator)(benchmark::State& st) { BENCHMARK_F(TwoArrays, SubtractWithOperator)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res = a-b; NDArray<int, 2> res = a - b;
benchmark::DoNotOptimize(res); benchmark::DoNotOptimize(res);
} }
} }
BENCHMARK_F(TwoArrays, SubtractWithIndex)(benchmark::State& st) { BENCHMARK_F(TwoArrays, SubtractWithIndex)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res(a.shape()); NDArray<int, 2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) { for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) - b(i); res(i) = a(i) - b(i);
}
benchmark::DoNotOptimize(res);
} }
benchmark::DoNotOptimize(res);
}
} }
BENCHMARK_F(TwoArrays, MultiplyWithOperator)(benchmark::State& st) { BENCHMARK_F(TwoArrays, MultiplyWithOperator)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res = a*b; NDArray<int, 2> res = a * b;
benchmark::DoNotOptimize(res); benchmark::DoNotOptimize(res);
} }
} }
BENCHMARK_F(TwoArrays, MultiplyWithIndex)(benchmark::State& st) { BENCHMARK_F(TwoArrays, MultiplyWithIndex)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res(a.shape()); NDArray<int, 2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) { for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) * b(i); res(i) = a(i) * b(i);
}
benchmark::DoNotOptimize(res);
} }
benchmark::DoNotOptimize(res);
}
} }
BENCHMARK_F(TwoArrays, DivideWithOperator)(benchmark::State& st) { BENCHMARK_F(TwoArrays, DivideWithOperator)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res = a/b; NDArray<int, 2> res = a / b;
benchmark::DoNotOptimize(res); benchmark::DoNotOptimize(res);
} }
} }
BENCHMARK_F(TwoArrays, DivideWithIndex)(benchmark::State& st) { BENCHMARK_F(TwoArrays, DivideWithIndex)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res(a.shape()); NDArray<int, 2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) { for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) / b(i); res(i) = a(i) / b(i);
}
benchmark::DoNotOptimize(res);
} }
benchmark::DoNotOptimize(res);
}
} }
BENCHMARK_F(TwoArrays, FourAddWithOperator)(benchmark::State& st) { BENCHMARK_F(TwoArrays, FourAddWithOperator)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res = a+b+a+b; NDArray<int, 2> res = a + b + a + b;
benchmark::DoNotOptimize(res); benchmark::DoNotOptimize(res);
} }
} }
BENCHMARK_F(TwoArrays, FourAddWithIndex)(benchmark::State& st) { BENCHMARK_F(TwoArrays, FourAddWithIndex)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res(a.shape()); NDArray<int, 2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) { for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) + b(i) + a(i) + b(i); res(i) = a(i) + b(i) + a(i) + b(i);
}
benchmark::DoNotOptimize(res);
} }
benchmark::DoNotOptimize(res);
}
} }
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithOperator)(benchmark::State& st) { BENCHMARK_F(TwoArrays, MultiplyAddDivideWithOperator)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res = a*a+b/a; NDArray<int, 2> res = a * a + b / a;
benchmark::DoNotOptimize(res); benchmark::DoNotOptimize(res);
} }
} }
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithIndex)(benchmark::State& st) { BENCHMARK_F(TwoArrays, MultiplyAddDivideWithIndex)(benchmark::State &st) {
for (auto _ : st) { for (auto _ : st) {
// This code gets timed // This code gets timed
NDArray<int,2> res(a.shape()); NDArray<int, 2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) { for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) * a(i) + b(i) / a(i); res(i) = a(i) * a(i) + b(i) / a(i);
}
benchmark::DoNotOptimize(res);
} }
benchmark::DoNotOptimize(res);
}
} }
BENCHMARK_MAIN(); BENCHMARK_MAIN();

View File

@ -1,28 +1,5 @@
python: python:
- 3.11 - 3.11
- 3.11
- 3.11
- 3.12
- 3.12
- 3.12 - 3.12
- 3.13 - 3.13
numpy:
- 1.26
- 2.0
- 2.1
- 1.26
- 2.0
- 2.1
- 2.1
zip_keys:
- python
- numpy
pin_run_as_build:
numpy: x.x
python: x.x

View File

@ -1,11 +1,10 @@
source:
path: ../
{% set version = load_file_regex(load_file = 'VERSION', regex_pattern = '(\d+(?:\.\d+)*(?:[\+\w\.]+))').group(1) %}
package: package:
name: aare name: aare
version: 2025.4.22 #TODO! how to not duplicate this? version: {{version}}
source: source:
path: .. path: ..
@ -13,45 +12,39 @@ source:
build: build:
number: 0 number: 0
script: script:
- unset CMAKE_GENERATOR && {{ PYTHON }} -m pip install . -vv # [not win] - unset CMAKE_GENERATOR && {{ PYTHON }} -m pip install . -vv
- {{ PYTHON }} -m pip install . -vv # [win]
requirements: requirements:
build: build:
- python {{python}}
- numpy {{ numpy }}
- {{ compiler('cxx') }} - {{ compiler('cxx') }}
host:
- cmake - cmake
- ninja - ninja
- python {{python}}
- numpy {{ numpy }} host:
- python
- pip - pip
- numpy=2.1
- scikit-build-core - scikit-build-core
- pybind11 >=2.13.0 - pybind11 >=2.13.0
- fmt - matplotlib # needed in host to solve the environment for run
- zeromq
- nlohmann_json
- catch2
run: run:
- python {{python}} - python
- numpy {{ numpy }} - {{ pin_compatible('numpy') }}
- matplotlib - matplotlib
test: test:
imports: imports:
- aare - aare
# requires: requires:
# - pytest - pytest
# source_files: - boost-histogram
# - tests source_files:
# commands: - python/tests
# - pytest tests commands:
- python -m pytest python/tests
about: about:
summary: An example project built with pybind11 and scikit-build. summary: Data analysis library for hybrid pixel detectors from PSI
# license_file: LICENSE

47
docs/src/Philosophy.rst Normal file
View File

@ -0,0 +1,47 @@
****************
Philosophy
****************
Fast code with a simple interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Aare should be fast and efficient, but also easy to use. We strive to keep a simple interface that feels intuitive.
Internally we use C++ for performance and the ability to integrate the library in other programs, but we see most
users using the Python interface.
Live at head
~~~~~~~~~~~~~~~~~~
As a user of the library you should be able to, and is expected to, use the latest version. Bug fixes will rarely be backported
to older releases. By upgrading frequently you will benefit from the latest features and minimize the effort to maintain your scripts/code
by doing several small upgrades instead of one big upgrade.
API
~~~~~~~~~~~~~~~~~~
We aim to keep the API stable and only break it for good reasons. But specially now in the early stages of development
the API will change. On those occasions it will be clearly stated in the release notes. However, the norm should be a
backward compatible API.
Documentation
~~~~~~~~~~~~~~~~~~
Being a library it is important to have a well documented API. We use Doxygen to generate the C++ documentation
and Sphinx for the Python part. Breathe is used to integrate the two into one Sphinx html site. The documentation is built
automatically on release by the CI and published to GitHub pages. In addition to the generated API documentation,
certain classes might need more descriptions of the usage. This is then placed in the .rst files in the docs/src directory.
.. attention::
The code should be well documented, but using descriptive names is more important. In the same spirit
if a function is called `getNumberOfFrames()` you don't need to write a comment saying that it gets the
number of frames.
Dependencies
~~~~~~~~~~~~~~~~~~
Deployment in the scientific community is often tricky. Either due to old OS versions or the lack of package managers.
We strive to keep the dependencies to a minimum and will vendor some libraries to simplify deployment even though it comes
at a cost of build time.

View File

@ -2,18 +2,21 @@ Requirements
============================================== ==============================================
- C++17 compiler (gcc 8/clang 7) - C++17 compiler (gcc 8/clang 7)
- CMake 3.14+ - CMake 3.15+
**Internally used libraries** **Internally used libraries**
.. note :: .. note ::
These can also be picked up from the system/conda environment by specifying: To save compile time some of the dependencies can also be picked up from the system/conda environment by specifying:
-DAARE_SYSTEM_LIBRARIES=ON during the cmake configuration. -DAARE_SYSTEM_LIBRARIES=ON during the cmake configuration.
- pybind11 To simplify deployment we build and statically link a few libraries.
- fmt - fmt
- lmfit - https://jugit.fz-juelich.de/mlz/lmfit
- nlohmann_json - nlohmann_json
- pybind11
- ZeroMQ - ZeroMQ
**Extra dependencies for building documentation** **Extra dependencies for building documentation**

86
docs/src/Workflow.rst Normal file
View File

@ -0,0 +1,86 @@
****************
Workflow
****************
This page describes how we develop aare.
GitHub centric
~~~~~~~~~~~~~~~~~~
We use GitHub for all development. Issues and pull requests provide a platform for collaboration as well
as a record of the development process. Even if we discuss things in person, we record the outcome in an issue.
If a particular implementation is chosen over another, the reason should be recorded in the pull request.
Branches
~~~~~~~~~~~~~~~~~~
We aim for an as lightweight branching strategy as possible. Short-lived feature branches are merged back into main.
The main branch is expected to always be in a releasable state. A release is simply a tag on main which provides a
reference and triggers the CI to build the release artifacts (conda, pypi etc.). For large features consider merging
smaller chunks into main as they are completed, rather than waiting for the entire feature to be finished. Worst case
make sure your feature branch merges with main regularly to avoid large merge conflicts later on.
.. note::
The main branch is expected to always work. Feel free to pull from main instead of sticking to a
release
Releases
~~~~~~~~~~~~~~~~~~
Release early, release often. As soon as "enough" new features have been implemented, a release is created.
A release should not be a big thing, rather a routine part of development that does not require any special person or
unfamiliar steps.
Checklists for deployment
~~~~~~~~~~~~~~~~~~
**Feature:**
#. Create a new issue for the feature (label feature)
#. Create a new branch from main.
#. Implement the feature including test and documentation
#. Add the feature to RELEASE.md under head
#. Create a pull request linked to the issue
#. Code is reviewed by at least one other person
#. Once approved, the branch is merged into main
**BugFix:**
Essentially the same as for a feature, if possible start with
a failing test that demonstrates the bug.
#. Create a new issue for the bug (label bug)
#. Create a new branch from main.
#. **Write a test that fails for the bug**
#. Implement the fix
#. **Run the test to ensure it passes**
#. Add the bugfix to RELEASE.md under head
#. Create a pull request linked to the issue.
#. Code is reviewed by at least one other person
#. Once approved, the branch is merged into main
**Release:**
#. Once "enough" new features have been implemented, a release is created
#. Update RELEASE.md with the tag of the release and verify that it is complete
#. Create the release in GitHub describing the new features and bug fixes
#. CI makes magic
**Update documentation only:**
.. attention::
It's possible to update the documentation without changing the code, but take
care since the docs will reflect the code in main and not the latest release.
#. Create a PR to main with the documentation changes
#. Create a pull request linked to the issue.
#. Code is reviewed by at least one other person
#. Once merged you can manually trigger the CI workflow for documentation

View File

@ -63,4 +63,6 @@ AARE
:caption: Developer :caption: Developer
:maxdepth: 3 :maxdepth: 3
Philosophy
Workflow
Tests Tests

View File

@ -3,13 +3,11 @@ channels:
- conda-forge - conda-forge
dependencies: dependencies:
- anaconda-client - anaconda-client
- conda-build
- doxygen - doxygen
- sphinx=7.1.2 - sphinx=7.1.2
- breathe - breathe
- pybind11
- sphinx_rtd_theme - sphinx_rtd_theme
- furo - furo
- nlohmann_json
- zeromq - zeromq
- fmt
- numpy

View File

@ -1,22 +1,25 @@
#pragma once #pragma once
#include <cstdint> //int64_t #include "aare/defs.hpp"
#include <cstddef> //size_t
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <cstddef>
#include <cstdint>
namespace aare { namespace aare {
template <typename E, int64_t Ndim> class ArrayExpr { template <typename E, ssize_t Ndim> class ArrayExpr {
public: public:
static constexpr bool is_leaf = false; static constexpr bool is_leaf = false;
auto operator[](size_t i) const { return static_cast<E const &>(*this)[i]; } auto operator[](size_t i) const { return static_cast<E const &>(*this)[i]; }
auto operator()(size_t i) const { return static_cast<E const &>(*this)[i]; } auto operator()(size_t i) const { return static_cast<E const &>(*this)[i]; }
auto size() const { return static_cast<E const &>(*this).size(); } auto size() const { return static_cast<E const &>(*this).size(); }
std::array<int64_t, Ndim> shape() const { return static_cast<E const &>(*this).shape(); } std::array<ssize_t, Ndim> shape() const {
return static_cast<E const &>(*this).shape();
}
}; };
template <typename A, typename B, int64_t Ndim> template <typename A, typename B, ssize_t Ndim>
class ArrayAdd : public ArrayExpr<ArrayAdd<A, B, Ndim>, Ndim> { class ArrayAdd : public ArrayExpr<ArrayAdd<A, B, Ndim>, Ndim> {
const A &arr1_; const A &arr1_;
const B &arr2_; const B &arr2_;
@ -27,10 +30,10 @@ class ArrayAdd : public ArrayExpr<ArrayAdd<A, B, Ndim>, Ndim> {
} }
auto operator[](int i) const { return arr1_[i] + arr2_[i]; } auto operator[](int i) const { return arr1_[i] + arr2_[i]; }
size_t size() const { return arr1_.size(); } size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); } std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
}; };
template <typename A, typename B, int64_t Ndim> template <typename A, typename B, ssize_t Ndim>
class ArraySub : public ArrayExpr<ArraySub<A, B, Ndim>, Ndim> { class ArraySub : public ArrayExpr<ArraySub<A, B, Ndim>, Ndim> {
const A &arr1_; const A &arr1_;
const B &arr2_; const B &arr2_;
@ -41,11 +44,11 @@ class ArraySub : public ArrayExpr<ArraySub<A, B, Ndim>, Ndim> {
} }
auto operator[](int i) const { return arr1_[i] - arr2_[i]; } auto operator[](int i) const { return arr1_[i] - arr2_[i]; }
size_t size() const { return arr1_.size(); } size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); } std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
}; };
template <typename A, typename B, int64_t Ndim> template <typename A, typename B, ssize_t Ndim>
class ArrayMul : public ArrayExpr<ArrayMul<A, B, Ndim>,Ndim> { class ArrayMul : public ArrayExpr<ArrayMul<A, B, Ndim>, Ndim> {
const A &arr1_; const A &arr1_;
const B &arr2_; const B &arr2_;
@ -55,10 +58,10 @@ class ArrayMul : public ArrayExpr<ArrayMul<A, B, Ndim>,Ndim> {
} }
auto operator[](int i) const { return arr1_[i] * arr2_[i]; } auto operator[](int i) const { return arr1_[i] * arr2_[i]; }
size_t size() const { return arr1_.size(); } size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); } std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
}; };
template <typename A, typename B, int64_t Ndim> template <typename A, typename B, ssize_t Ndim>
class ArrayDiv : public ArrayExpr<ArrayDiv<A, B, Ndim>, Ndim> { class ArrayDiv : public ArrayExpr<ArrayDiv<A, B, Ndim>, Ndim> {
const A &arr1_; const A &arr1_;
const B &arr2_; const B &arr2_;
@ -69,31 +72,27 @@ class ArrayDiv : public ArrayExpr<ArrayDiv<A, B, Ndim>, Ndim> {
} }
auto operator[](int i) const { return arr1_[i] / arr2_[i]; } auto operator[](int i) const { return arr1_[i] / arr2_[i]; }
size_t size() const { return arr1_.size(); } size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); } std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
}; };
template <typename A, typename B, ssize_t Ndim>
template <typename A, typename B, int64_t Ndim>
auto operator+(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) { auto operator+(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArrayAdd<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2); return ArrayAdd<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
} }
template <typename A, typename B, int64_t Ndim> template <typename A, typename B, ssize_t Ndim>
auto operator-(const ArrayExpr<A,Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) { auto operator-(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArraySub<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2); return ArraySub<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
} }
template <typename A, typename B, int64_t Ndim> template <typename A, typename B, ssize_t Ndim>
auto operator*(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) { auto operator*(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArrayMul<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2); return ArrayMul<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
} }
template <typename A, typename B, int64_t Ndim> template <typename A, typename B, ssize_t Ndim>
auto operator/(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) { auto operator/(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArrayDiv<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2); return ArrayDiv<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
} }
} // namespace aare } // namespace aare

View File

@ -0,0 +1,170 @@
#pragma once
#include "aare/Cluster.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDArray.hpp"
namespace aare {
enum class corner : int {
cBottomLeft = 0,
cBottomRight = 1,
cTopLeft = 2,
cTopRight = 3
};
enum class pixel : int {
pBottomLeft = 0,
pBottom = 1,
pBottomRight = 2,
pLeft = 3,
pCenter = 4,
pRight = 5,
pTopLeft = 6,
pTop = 7,
pTopRight = 8
};
template <typename T> struct Eta2 {
double x;
double y;
int c;
T sum;
};
/**
* @brief Calculate the eta2 values for all clusters in a Clustervector
*/
template <typename ClusterType,
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
NDArray<double, 2> calculate_eta2(const ClusterVector<ClusterType> &clusters) {
NDArray<double, 2> eta2({static_cast<int64_t>(clusters.size()), 2});
for (size_t i = 0; i < clusters.size(); i++) {
auto e = calculate_eta2(clusters[i]);
eta2(i, 0) = e.x;
eta2(i, 1) = e.y;
}
return eta2;
}
/**
* @brief Calculate the eta2 values for a generic sized cluster and return them
* in a Eta2 struct containing etay, etax and the index of the respective 2x2
* subcluster.
*/
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType>
Eta2<T>
calculate_eta2(const Cluster<T, ClusterSizeX, ClusterSizeY, CoordType> &cl) {
Eta2<T> eta{};
auto max_sum = cl.max_sum_2x2();
eta.sum = max_sum.first;
auto c = max_sum.second;
size_t cluster_center_index =
(ClusterSizeX / 2) + (ClusterSizeY / 2) * ClusterSizeX;
size_t index_bottom_left_max_2x2_subcluster =
(int(c / (ClusterSizeX - 1))) * ClusterSizeX + c % (ClusterSizeX - 1);
// check that cluster center is in max subcluster
if (cluster_center_index != index_bottom_left_max_2x2_subcluster &&
cluster_center_index != index_bottom_left_max_2x2_subcluster + 1 &&
cluster_center_index !=
index_bottom_left_max_2x2_subcluster + ClusterSizeX &&
cluster_center_index !=
index_bottom_left_max_2x2_subcluster + ClusterSizeX + 1)
throw std::runtime_error("Photon center is not in max 2x2_subcluster");
if ((cluster_center_index - index_bottom_left_max_2x2_subcluster) %
ClusterSizeX ==
0) {
if ((cl.data[cluster_center_index + 1] +
cl.data[cluster_center_index]) != 0)
eta.x = static_cast<double>(cl.data[cluster_center_index + 1]) /
static_cast<double>((cl.data[cluster_center_index + 1] +
cl.data[cluster_center_index]));
} else {
if ((cl.data[cluster_center_index] +
cl.data[cluster_center_index - 1]) != 0)
eta.x = static_cast<double>(cl.data[cluster_center_index]) /
static_cast<double>((cl.data[cluster_center_index - 1] +
cl.data[cluster_center_index]));
}
if ((cluster_center_index - index_bottom_left_max_2x2_subcluster) /
ClusterSizeX <
1) {
assert(cluster_center_index + ClusterSizeX <
ClusterSizeX * ClusterSizeY); // suppress warning
if ((cl.data[cluster_center_index] +
cl.data[cluster_center_index + ClusterSizeX]) != 0)
eta.y = static_cast<double>(
cl.data[cluster_center_index + ClusterSizeX]) /
static_cast<double>(
(cl.data[cluster_center_index] +
cl.data[cluster_center_index + ClusterSizeX]));
} else {
if ((cl.data[cluster_center_index] +
cl.data[cluster_center_index - ClusterSizeX]) != 0)
eta.y = static_cast<double>(cl.data[cluster_center_index]) /
static_cast<double>(
(cl.data[cluster_center_index] +
cl.data[cluster_center_index - ClusterSizeX]));
}
eta.c = c; // TODO only supported for 2x2 and 3x3 clusters -> at least no
// underyling enum class
return eta;
}
// TODO! Look up eta2 calculation - photon center should be top right corner
template <typename T>
Eta2<T> calculate_eta2(const Cluster<T, 2, 2, int16_t> &cl) {
Eta2<T> eta{};
if ((cl.data[0] + cl.data[1]) != 0)
eta.x = static_cast<double>(cl.data[1]) / (cl.data[0] + cl.data[1]);
if ((cl.data[0] + cl.data[2]) != 0)
eta.y = static_cast<double>(cl.data[2]) / (cl.data[0] + cl.data[2]);
eta.sum = cl.sum();
eta.c = static_cast<int>(corner::cBottomLeft); // TODO! This is not correct,
// but need to put something
return eta;
}
// calculates Eta3 for 3x3 cluster based on code from analyze_cluster
// TODO only supported for 3x3 Clusters
template <typename T> Eta2<T> calculate_eta3(const Cluster<T, 3, 3> &cl) {
Eta2<T> eta{};
T sum = 0;
std::for_each(std::begin(cl.data), std::end(cl.data),
[&sum](T x) { sum += x; });
eta.sum = sum;
eta.c = corner::cBottomLeft;
if ((cl.data[3] + cl.data[4] + cl.data[5]) != 0)
eta.x = static_cast<double>(-cl.data[3] + cl.data[3 + 2]) /
(cl.data[3] + cl.data[4] + cl.data[5]);
if ((cl.data[1] + cl.data[4] + cl.data[7]) != 0)
eta.y = static_cast<double>(-cl.data[1] + cl.data[2 * 3 + 1]) /
(cl.data[1] + cl.data[4] + cl.data[7]);
return eta;
}
} // namespace aare

View File

@ -17,7 +17,8 @@ template <class ItemType> class CircularFifo {
public: public:
CircularFifo() : CircularFifo(100){}; CircularFifo() : CircularFifo(100){};
CircularFifo(uint32_t size) : fifo_size(size), free_slots(size + 1), filled_slots(size + 1) { CircularFifo(uint32_t size)
: fifo_size(size), free_slots(size + 1), filled_slots(size + 1) {
// TODO! how do we deal with alignment for writing? alignas??? // TODO! how do we deal with alignment for writing? alignas???
// Do we give the user a chance to provide memory locations? // Do we give the user a chance to provide memory locations?
@ -55,7 +56,8 @@ template <class ItemType> class CircularFifo {
bool try_pop_free(ItemType &v) { return free_slots.read(v); } bool try_pop_free(ItemType &v) { return free_slots.read(v); }
ItemType pop_value(std::chrono::nanoseconds wait, std::atomic<bool> &stopped) { ItemType pop_value(std::chrono::nanoseconds wait,
std::atomic<bool> &stopped) {
ItemType v; ItemType v;
while (!filled_slots.read(v) && !stopped) { while (!filled_slots.read(v) && !stopped) {
std::this_thread::sleep_for(wait); std::this_thread::sleep_for(wait);

View File

@ -1,36 +1,86 @@
/************************************************
* @file Cluster.hpp
* @short definition of cluster, where CoordType (x,y) give
* the cluster center coordinates and data the actual cluster data
* cluster size is given as template parameters
***********************************************/
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <numeric> #include <numeric>
#include <type_traits>
namespace aare { namespace aare {
//TODO! Template this? // requires clause c++20 maybe update
struct Cluster3x3 { template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
int16_t x; typename CoordType = int16_t>
int16_t y; struct Cluster {
int32_t data[9];
int32_t sum_2x2() const{ static_assert(std::is_arithmetic_v<T>, "T needs to be an arithmetic type");
std::array<int32_t, 4> total; static_assert(std::is_integral_v<CoordType>,
total[0] = data[0] + data[1] + data[3] + data[4]; "CoordType needs to be an integral type");
total[1] = data[1] + data[2] + data[4] + data[5]; static_assert(ClusterSizeX > 0 && ClusterSizeY > 0,
total[2] = data[3] + data[4] + data[6] + data[7]; "Cluster sizes must be bigger than zero");
total[3] = data[4] + data[5] + data[7] + data[8];
return *std::max_element(total.begin(), total.end());
}
int32_t sum() const{ CoordType x;
return std::accumulate(data, data + 9, 0); CoordType y;
std::array<T, ClusterSizeX * ClusterSizeY> data;
static constexpr uint8_t cluster_size_x = ClusterSizeX;
static constexpr uint8_t cluster_size_y = ClusterSizeY;
using value_type = T;
using coord_type = CoordType;
T sum() const { return std::accumulate(data.begin(), data.end(), T{}); }
std::pair<T, int> max_sum_2x2() const {
if constexpr (cluster_size_x == 3 && cluster_size_y == 3) {
std::array<T, 4> sum_2x2_subclusters;
sum_2x2_subclusters[0] = data[0] + data[1] + data[3] + data[4];
sum_2x2_subclusters[1] = data[1] + data[2] + data[4] + data[5];
sum_2x2_subclusters[2] = data[3] + data[4] + data[6] + data[7];
sum_2x2_subclusters[3] = data[4] + data[5] + data[7] + data[8];
int index = std::max_element(sum_2x2_subclusters.begin(),
sum_2x2_subclusters.end()) -
sum_2x2_subclusters.begin();
return std::make_pair(sum_2x2_subclusters[index], index);
} else if constexpr (cluster_size_x == 2 && cluster_size_y == 2) {
return std::make_pair(data[0] + data[1] + data[2] + data[3], 0);
} else {
constexpr size_t num_2x2_subclusters =
(ClusterSizeX - 1) * (ClusterSizeY - 1);
std::array<T, num_2x2_subclusters> sum_2x2_subcluster;
for (size_t i = 0; i < ClusterSizeY - 1; ++i) {
for (size_t j = 0; j < ClusterSizeX - 1; ++j)
sum_2x2_subcluster[i * (ClusterSizeX - 1) + j] =
data[i * ClusterSizeX + j] +
data[i * ClusterSizeX + j + 1] +
data[(i + 1) * ClusterSizeX + j] +
data[(i + 1) * ClusterSizeX + j + 1];
}
int index = std::max_element(sum_2x2_subcluster.begin(),
sum_2x2_subcluster.end()) -
sum_2x2_subcluster.begin();
return std::make_pair(sum_2x2_subcluster[index], index);
}
} }
}; };
struct Cluster2x2 {
int16_t x; // Type Traits for is_cluster_type
int16_t y; template <typename T>
int32_t data[4]; struct is_cluster : std::false_type {}; // Default case: Not a Cluster
};
template <typename T, uint8_t X, uint8_t Y, typename CoordType>
struct is_cluster<Cluster<T, X, Y, CoordType>> : std::true_type {}; // Cluster
template <typename T> constexpr bool is_cluster_v = is_cluster<T>::value;
} // namespace aare } // namespace aare

View File

@ -2,29 +2,31 @@
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include "aare/ProducerConsumerQueue.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/ClusterFinderMT.hpp" #include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/ProducerConsumerQueue.hpp"
namespace aare { namespace aare {
class ClusterCollector{ template <typename ClusterType,
ProducerConsumerQueue<ClusterVector<int>>* m_source; typename = std::enable_if_t<is_cluster_v<ClusterType>>>
std::atomic<bool> m_stop_requested{false}; class ClusterCollector {
std::atomic<bool> m_stopped{true}; ProducerConsumerQueue<ClusterVector<ClusterType>> *m_source;
std::chrono::milliseconds m_default_wait{1}; std::atomic<bool> m_stop_requested{false};
std::thread m_thread; std::atomic<bool> m_stopped{true};
std::vector<ClusterVector<int>> m_clusters; std::chrono::milliseconds m_default_wait{1};
std::thread m_thread;
std::vector<ClusterVector<ClusterType>> m_clusters;
void process(){ void process() {
m_stopped = false; m_stopped = false;
fmt::print("ClusterCollector started\n"); fmt::print("ClusterCollector started\n");
while (!m_stop_requested || !m_source->isEmpty()) { while (!m_stop_requested || !m_source->isEmpty()) {
if (ClusterVector<int> *clusters = m_source->frontPtr(); if (ClusterVector<ClusterType> *clusters = m_source->frontPtr();
clusters != nullptr) { clusters != nullptr) {
m_clusters.push_back(std::move(*clusters)); m_clusters.push_back(std::move(*clusters));
m_source->popFront(); m_source->popFront();
}else{ } else {
std::this_thread::sleep_for(m_default_wait); std::this_thread::sleep_for(m_default_wait);
} }
} }
@ -32,21 +34,25 @@ class ClusterCollector{
m_stopped = true; m_stopped = true;
} }
public: public:
ClusterCollector(ClusterFinderMT<uint16_t, double, int32_t>* source){ ClusterCollector(ClusterFinderMT<ClusterType, uint16_t, double> *source) {
m_source = source->sink(); m_source = source->sink();
m_thread = std::thread(&ClusterCollector::process, this); m_thread =
} std::thread(&ClusterCollector::process,
void stop(){ this); // only one process does that so why isnt it
m_stop_requested = true; // automatically written to m_cluster in collect
m_thread.join(); // - instead of writing first to m_sink?
} }
std::vector<ClusterVector<int>> steal_clusters(){ void stop() {
if(!m_stopped){ m_stop_requested = true;
throw std::runtime_error("ClusterCollector is still running"); m_thread.join();
} }
return std::move(m_clusters); std::vector<ClusterVector<ClusterType>> steal_clusters() {
if (!m_stopped) {
throw std::runtime_error("ClusterCollector is still running");
} }
return std::move(m_clusters);
}
}; };
} // namespace aare } // namespace aare

View File

@ -2,51 +2,29 @@
#include "aare/Cluster.hpp" #include "aare/Cluster.hpp"
#include "aare/ClusterVector.hpp" #include "aare/ClusterVector.hpp"
#include "aare/GainMap.hpp"
#include "aare/NDArray.hpp" #include "aare/NDArray.hpp"
#include "aare/defs.hpp" #include "aare/defs.hpp"
#include "aare/logger.hpp"
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <optional> #include <optional>
namespace aare { namespace aare {
/*
Binary cluster file. Expects data to be layed out as:
int32_t frame_number
uint32_t number_of_clusters
int16_t x, int16_t y, int32_t data[9] x number_of_clusters
int32_t frame_number
uint32_t number_of_clusters
....
*/
//TODO! Legacy enums, migrate to enum class // TODO: change to support any type of clusters, e.g. header line with
typedef enum { // clsuter_size_x, cluster_size_y,
cBottomLeft = 0,
cBottomRight = 1,
cTopLeft = 2,
cTopRight = 3
} corner;
typedef enum {
pBottomLeft = 0,
pBottom = 1,
pBottomRight = 2,
pLeft = 3,
pCenter = 4,
pRight = 5,
pTopLeft = 6,
pTop = 7,
pTopRight = 8
} pixel;
struct Eta2 {
double x;
double y;
corner c;
int32_t sum;
};
struct ClusterAnalysis {
uint32_t c;
int32_t tot;
double etax;
double etay;
};
/** /**
* @brief Class to read and write cluster files * @brief Class to read and write cluster files
* Expects data to be laid out as: * Expects data to be laid out as:
@ -59,14 +37,19 @@ struct ClusterAnalysis {
* uint32_t number_of_clusters * uint32_t number_of_clusters
* etc. * etc.
*/ */
template <typename ClusterType,
typename Enable = std::enable_if_t<is_cluster_v<ClusterType>>>
class ClusterFile { class ClusterFile {
FILE *fp{}; FILE *fp{};
uint32_t m_num_left{}; /*Number of photons left in frame*/ const std::string m_filename{};
size_t m_chunk_size{}; /*Number of clusters to read at a time*/ uint32_t m_num_left{}; /*Number of photons left in frame*/
const std::string m_mode; /*Mode to open the file in*/ size_t m_chunk_size{}; /*Number of clusters to read at a time*/
std::optional<ROI> m_roi; /*Region of interest, will be applied if set*/ std::string m_mode; /*Mode to open the file in*/
std::optional<NDArray<int32_t, 2>> m_noise_map; /*Noise map to cut photons, will be applied if set*/ std::optional<ROI> m_roi; /*Region of interest, will be applied if set*/
std::optional<NDArray<double, 2>> m_gain_map; /*Gain map to apply to the clusters, will be applied if set*/ std::optional<NDArray<int32_t, 2>>
m_noise_map; /*Noise map to cut photons, will be applied if set*/
std::optional<InvertedGainMap> m_gain_map; /*Gain map to apply to the
clusters, will be applied if set*/
public: public:
/** /**
@ -79,30 +62,81 @@ class ClusterFile {
* @throws std::runtime_error if the file could not be opened * @throws std::runtime_error if the file could not be opened
*/ */
ClusterFile(const std::filesystem::path &fname, size_t chunk_size = 1000, ClusterFile(const std::filesystem::path &fname, size_t chunk_size = 1000,
const std::string &mode = "r"); const std::string &mode = "r")
: m_filename(fname.string()), m_chunk_size(chunk_size), m_mode(mode) {
~ClusterFile(); if (mode == "r") {
fp = fopen(m_filename.c_str(), "rb");
if (!fp) {
throw std::runtime_error("Could not open file for reading: " +
m_filename);
}
} else if (mode == "w") {
fp = fopen(m_filename.c_str(), "wb");
if (!fp) {
throw std::runtime_error("Could not open file for writing: " +
m_filename);
}
} else if (mode == "a") {
fp = fopen(m_filename.c_str(), "ab");
if (!fp) {
throw std::runtime_error("Could not open file for appending: " +
m_filename);
}
} else {
throw std::runtime_error("Unsupported mode: " + mode);
}
}
~ClusterFile() { close(); }
/** /**
* @brief Read n_clusters clusters from the file discarding frame numbers. * @brief Read n_clusters clusters from the file discarding
* If EOF is reached the returned vector will have less than n_clusters * frame numbers. If EOF is reached the returned vector will
* clusters * have less than n_clusters clusters
*/ */
ClusterVector<int32_t> read_clusters(size_t n_clusters); ClusterVector<ClusterType> read_clusters(size_t n_clusters) {
if (m_mode != "r") {
ClusterVector<int32_t> read_clusters(size_t n_clusters, ROI roi); throw std::runtime_error("File not opened for reading");
}
if (m_noise_map || m_roi) {
return read_clusters_with_cut(n_clusters);
} else {
return read_clusters_without_cut(n_clusters);
}
}
/** /**
* @brief Read a single frame from the file and return the clusters. The * @brief Read a single frame from the file and return the
* cluster vector will have the frame number set. * clusters. The cluster vector will have the frame number
* @throws std::runtime_error if the file is not opened for reading or the file pointer not * set.
* at the beginning of a frame * @throws std::runtime_error if the file is not opened for
* reading or the file pointer not at the beginning of a
* frame
*/ */
ClusterVector<int32_t> read_frame(); ClusterVector<ClusterType> read_frame() {
if (m_mode != "r") {
throw std::runtime_error(LOCATION + "File not opened for reading");
}
if (m_noise_map || m_roi) {
return read_frame_with_cut();
} else {
return read_frame_without_cut();
}
}
void write_frame(const ClusterVector<ClusterType> &clusters) {
if (m_mode != "w" && m_mode != "a") {
throw std::runtime_error("File not opened for writing");
}
void write_frame(const ClusterVector<int32_t> &clusters); int32_t frame_number = clusters.frame_number();
fwrite(&frame_number, sizeof(frame_number), 1, fp);
uint32_t n_clusters = clusters.size();
fwrite(&n_clusters, sizeof(n_clusters), 1, fp);
fwrite(clusters.data(), clusters.item_size(), clusters.size(), fp);
}
/** /**
* @brief Return the chunk size * @brief Return the chunk size
@ -110,43 +144,312 @@ class ClusterFile {
size_t chunk_size() const { return m_chunk_size; } size_t chunk_size() const { return m_chunk_size; }
/** /**
* @brief Set the region of interest to use when reading clusters. If set only clusters within * @brief Set the region of interest to use when reading
* the ROI will be read. * clusters. If set only clusters within the ROI will be
* read.
*/ */
void set_roi(ROI roi); void set_roi(ROI roi) { m_roi = roi; }
/** /**
* @brief Set the noise map to use when reading clusters. If set clusters below the noise * @brief Set the noise map to use when reading clusters. If
* level will be discarded. Selection criteria one of: Central pixel above noise, highest * set clusters below the noise level will be discarded.
* 2x2 sum above 2 * noise, total sum above 3 * noise. * Selection criteria one of: Central pixel above noise,
* highest 2x2 sum above 2 * noise, total sum above 3 *
* noise.
*/ */
void set_noise_map(const NDView<int32_t, 2> noise_map); void set_noise_map(const NDView<int32_t, 2> noise_map) {
m_noise_map = NDArray<int32_t, 2>(noise_map);
}
/** /**
* @brief Set the gain map to use when reading clusters. If set the gain map will be applied * @brief Set the gain map to use when reading clusters. If set the gain map
* to the clusters that pass ROI and noise_map selection. The gain map is expected to be in ADU/energy. * will be applied to the clusters that pass ROI and noise_map selection.
* The gain map is expected to be in ADU/energy.
*/ */
void set_gain_map(const NDView<double, 2> gain_map); void set_gain_map(const NDView<double, 2> gain_map) {
m_gain_map = InvertedGainMap(gain_map);
}
void set_gain_map(const InvertedGainMap &gain_map) {
m_gain_map = gain_map;
}
void set_gain_map(const InvertedGainMap &&gain_map) {
m_gain_map = gain_map;
}
/** /**
* @brief Close the file. If not closed the file will be closed in the destructor * @brief Close the file. If not closed the file will be
* closed in the destructor
*/ */
void close(); void close() {
if (fp) {
fclose(fp);
fp = nullptr;
}
}
private: /** @brief Open the file in specific mode
ClusterVector<int32_t> read_clusters_with_cut(size_t n_clusters); *
ClusterVector<int32_t> read_clusters_without_cut(size_t n_clusters); */
ClusterVector<int32_t> read_frame_with_cut(); void open(const std::string &mode) {
ClusterVector<int32_t> read_frame_without_cut(); if (fp) {
bool is_selected(Cluster3x3 &cl); close();
Cluster3x3 read_one_cluster(); }
if (mode == "r") {
fp = fopen(m_filename.c_str(), "rb");
if (!fp) {
throw std::runtime_error("Could not open file for reading: " +
m_filename);
}
m_mode = "r";
} else if (mode == "w") {
fp = fopen(m_filename.c_str(), "wb");
if (!fp) {
throw std::runtime_error("Could not open file for writing: " +
m_filename);
}
m_mode = "w";
} else if (mode == "a") {
fp = fopen(m_filename.c_str(), "ab");
if (!fp) {
throw std::runtime_error("Could not open file for appending: " +
m_filename);
}
m_mode = "a";
} else {
throw std::runtime_error("Unsupported mode: " + mode);
}
}
private:
ClusterVector<ClusterType> read_clusters_with_cut(size_t n_clusters);
ClusterVector<ClusterType> read_clusters_without_cut(size_t n_clusters);
ClusterVector<ClusterType> read_frame_with_cut();
ClusterVector<ClusterType> read_frame_without_cut();
bool is_selected(ClusterType &cl);
ClusterType read_one_cluster();
}; };
//TODO! helper functions that doesn't really belong here template <typename ClusterType, typename Enable>
NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters); ClusterVector<ClusterType>
Eta2 calculate_eta2(Cluster3x3 &cl); ClusterFile<ClusterType, Enable>::read_clusters_without_cut(size_t n_clusters) {
Eta2 calculate_eta2(Cluster2x2 &cl); if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
ClusterVector<ClusterType> clusters(n_clusters);
clusters.resize(n_clusters);
int32_t iframe = 0; // frame number needs to be 4 bytes!
size_t nph_read = 0;
uint32_t nn = m_num_left;
uint32_t nph = m_num_left; // number of clusters in frame needs to be 4
auto buf = clusters.data();
// if there are photons left from previous frame read them first
if (nph) {
if (nph > n_clusters) {
// if we have more photons left in the frame then photons to
// read we read directly the requested number
nn = n_clusters;
} else {
nn = nph;
}
nph_read += fread((buf + nph_read), clusters.item_size(), nn, fp);
m_num_left = nph - nn; // write back the number of photons left
}
if (nph_read < n_clusters) {
// keep on reading frames and photons until reaching n_clusters
while (fread(&iframe, sizeof(iframe), 1, fp)) {
clusters.set_frame_number(iframe);
// read number of clusters in frame
if (fread(&nph, sizeof(nph), 1, fp)) {
if (nph > (n_clusters - nph_read))
nn = n_clusters - nph_read;
else
nn = nph;
nph_read +=
fread((buf + nph_read), clusters.item_size(), nn, fp);
m_num_left = nph - nn;
}
if (nph_read >= n_clusters)
break;
}
}
// Resize the vector to the number o f clusters.
// No new allocation, only change bounds.
clusters.resize(nph_read);
if (m_gain_map)
m_gain_map->apply_gain_map(clusters);
return clusters;
}
template <typename ClusterType, typename Enable>
ClusterVector<ClusterType>
ClusterFile<ClusterType, Enable>::read_clusters_with_cut(size_t n_clusters) {
ClusterVector<ClusterType> clusters;
clusters.reserve(n_clusters);
// if there are photons left from previous frame read them first
if (m_num_left) {
while (m_num_left && clusters.size() < n_clusters) {
ClusterType c = read_one_cluster();
if (is_selected(c)) {
clusters.push_back(c);
}
}
}
// we did not have enough clusters left in the previous frame
// keep on reading frames until reaching n_clusters
if (clusters.size() < n_clusters) {
// sanity check
if (m_num_left) {
throw std::runtime_error(
LOCATION + "Entered second loop with clusters left\n");
}
int32_t frame_number = 0; // frame number needs to be 4 bytes!
while (fread(&frame_number, sizeof(frame_number), 1, fp)) {
if (fread(&m_num_left, sizeof(m_num_left), 1, fp)) {
clusters.set_frame_number(
frame_number); // cluster vector will hold the last
// frame number
while (m_num_left && clusters.size() < n_clusters) {
ClusterType c = read_one_cluster();
if (is_selected(c)) {
clusters.push_back(c);
}
}
}
// we have enough clusters, break out of the outer while loop
if (clusters.size() >= n_clusters)
break;
}
}
if (m_gain_map)
m_gain_map->apply_gain_map(clusters);
return clusters;
}
template <typename ClusterType, typename Enable>
ClusterType ClusterFile<ClusterType, Enable>::read_one_cluster() {
ClusterType c;
auto rc = fread(&c, sizeof(c), 1, fp);
if (rc != 1) {
throw std::runtime_error(LOCATION + "Could not read cluster");
}
--m_num_left;
return c;
}
template <typename ClusterType, typename Enable>
ClusterVector<ClusterType>
ClusterFile<ClusterType, Enable>::read_frame_without_cut() {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
if (m_num_left) {
throw std::runtime_error(
"There are still photons left in the last frame");
}
int32_t frame_number;
if (fread(&frame_number, sizeof(frame_number), 1, fp) != 1) {
throw std::runtime_error(LOCATION + "Could not read frame number");
}
int32_t n_clusters; // Saved as 32bit integer in the cluster file
if (fread(&n_clusters, sizeof(n_clusters), 1, fp) != 1) {
throw std::runtime_error(LOCATION +
"Could not read number of clusters");
}
LOG(logDEBUG1) << "Reading " << n_clusters << " clusters from frame "
<< frame_number;
ClusterVector<ClusterType> clusters(n_clusters);
clusters.set_frame_number(frame_number);
clusters.resize(n_clusters);
LOG(logDEBUG1) << "clusters.item_size(): " << clusters.item_size();
if (fread(clusters.data(), clusters.item_size(), n_clusters, fp) !=
static_cast<size_t>(n_clusters)) {
throw std::runtime_error(LOCATION + "Could not read clusters");
}
if (m_gain_map)
m_gain_map->apply_gain_map(clusters);
return clusters;
}
template <typename ClusterType, typename Enable>
ClusterVector<ClusterType>
ClusterFile<ClusterType, Enable>::read_frame_with_cut() {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
if (m_num_left) {
throw std::runtime_error(
"There are still photons left in the last frame");
}
int32_t frame_number;
if (fread(&frame_number, sizeof(frame_number), 1, fp) != 1) {
throw std::runtime_error("Could not read frame number");
}
if (fread(&m_num_left, sizeof(m_num_left), 1, fp) != 1) {
throw std::runtime_error("Could not read number of clusters");
}
ClusterVector<ClusterType> clusters;
clusters.reserve(m_num_left);
clusters.set_frame_number(frame_number);
while (m_num_left) {
ClusterType c = read_one_cluster();
if (is_selected(c)) {
clusters.push_back(c);
}
}
if (m_gain_map)
m_gain_map->apply_gain_map(clusters);
return clusters;
}
template <typename ClusterType, typename Enable>
bool ClusterFile<ClusterType, Enable>::is_selected(ClusterType &cl) {
// Should fail fast
if (m_roi) {
if (!(m_roi->contains(cl.x, cl.y))) {
return false;
}
}
size_t cluster_center_index =
(ClusterType::cluster_size_x / 2) +
(ClusterType::cluster_size_y / 2) * ClusterType::cluster_size_x;
if (m_noise_map) {
auto sum_1x1 = cl.data[cluster_center_index]; // central pixel
auto sum_2x2 = cl.max_sum_2x2().first; // highest sum of 2x2 subclusters
auto total_sum = cl.sum(); // sum of all pixels
auto noise =
(*m_noise_map)(cl.y, cl.x); // TODO! check if this is correct
if (sum_1x1 <= noise || sum_2x2 <= 2 * noise ||
total_sum <= 3 * noise) {
return false;
}
}
// we passed all checks
return true;
}
} // namespace aare } // namespace aare

View File

@ -3,54 +3,63 @@
#include <filesystem> #include <filesystem>
#include <thread> #include <thread>
#include "aare/ProducerConsumerQueue.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/ClusterFinderMT.hpp" #include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/ProducerConsumerQueue.hpp"
namespace aare{ namespace aare {
class ClusterFileSink{ template <typename ClusterType,
ProducerConsumerQueue<ClusterVector<int>>* m_source; typename = std::enable_if_t<is_cluster_v<ClusterType>>>
class ClusterFileSink {
ProducerConsumerQueue<ClusterVector<ClusterType>> *m_source;
std::atomic<bool> m_stop_requested{false}; std::atomic<bool> m_stop_requested{false};
std::atomic<bool> m_stopped{true}; std::atomic<bool> m_stopped{true};
std::chrono::milliseconds m_default_wait{1}; std::chrono::milliseconds m_default_wait{1};
std::thread m_thread; std::thread m_thread;
std::ofstream m_file; std::ofstream m_file;
void process() {
void process(){
m_stopped = false; m_stopped = false;
fmt::print("ClusterFileSink started\n"); LOG(logDEBUG) << "ClusterFileSink started";
while (!m_stop_requested || !m_source->isEmpty()) { while (!m_stop_requested || !m_source->isEmpty()) {
if (ClusterVector<int> *clusters = m_source->frontPtr(); if (ClusterVector<ClusterType> *clusters = m_source->frontPtr();
clusters != nullptr) { clusters != nullptr) {
// Write clusters to file // Write clusters to file
int32_t frame_number = clusters->frame_number(); //TODO! Should we store frame number already as int? int32_t frame_number =
clusters->frame_number(); // TODO! Should we store frame
// number already as int?
uint32_t num_clusters = clusters->size(); uint32_t num_clusters = clusters->size();
m_file.write(reinterpret_cast<const char*>(&frame_number), sizeof(frame_number)); m_file.write(reinterpret_cast<const char *>(&frame_number),
m_file.write(reinterpret_cast<const char*>(&num_clusters), sizeof(num_clusters)); sizeof(frame_number));
m_file.write(reinterpret_cast<const char*>(clusters->data()), clusters->size() * clusters->item_size()); m_file.write(reinterpret_cast<const char *>(&num_clusters),
sizeof(num_clusters));
m_file.write(reinterpret_cast<const char *>(clusters->data()),
clusters->size() * clusters->item_size());
m_source->popFront(); m_source->popFront();
}else{ } else {
std::this_thread::sleep_for(m_default_wait); std::this_thread::sleep_for(m_default_wait);
} }
} }
fmt::print("ClusterFileSink stopped\n"); LOG(logDEBUG) << "ClusterFileSink stopped";
m_stopped = true; m_stopped = true;
} }
public: public:
ClusterFileSink(ClusterFinderMT<uint16_t, double, int32_t>* source, const std::filesystem::path& fname){ ClusterFileSink(ClusterFinderMT<ClusterType, uint16_t, double> *source,
m_source = source->sink(); const std::filesystem::path &fname) {
m_thread = std::thread(&ClusterFileSink::process, this); LOG(logDEBUG) << "ClusterFileSink: "
m_file.open(fname, std::ios::binary); << "source: " << source->sink()
} << ", file: " << fname.string();
void stop(){ m_source = source->sink();
m_stop_requested = true; m_thread = std::thread(&ClusterFileSink::process, this);
m_thread.join(); m_file.open(fname, std::ios::binary);
m_file.close(); }
} void stop() {
m_stop_requested = true;
m_thread.join();
m_file.close();
}
}; };
} // namespace aare } // namespace aare

View File

@ -1,148 +0,0 @@
#pragma once
#include "aare/core/defs.hpp"
#include <filesystem>
#include <string>
#include <fmt/format.h>
namespace aare {
struct ClusterHeader {
int32_t frame_number;
int32_t n_clusters;
std::string to_string() const {
return "frame_number: " + std::to_string(frame_number) + ", n_clusters: " + std::to_string(n_clusters);
}
};
struct ClusterV2_ {
int16_t x;
int16_t y;
std::array<int32_t, 9> data;
std::string to_string(bool detailed = false) const {
if (detailed) {
std::string data_str = "[";
for (auto &d : data) {
data_str += std::to_string(d) + ", ";
}
data_str += "]";
return "x: " + std::to_string(x) + ", y: " + std::to_string(y) + ", data: " + data_str;
}
return "x: " + std::to_string(x) + ", y: " + std::to_string(y);
}
};
struct ClusterV2 {
ClusterV2_ cluster;
int32_t frame_number;
std::string to_string() const {
return "frame_number: " + std::to_string(frame_number) + ", " + cluster.to_string();
}
};
/**
* @brief
* important not: fp always points to the clusters header and does not point to individual clusters
*
*/
class ClusterFileV2 {
std::filesystem::path m_fpath;
std::string m_mode;
FILE *fp{nullptr};
void check_open(){
if (!fp)
throw std::runtime_error(fmt::format("File: {} not open", m_fpath.string()));
}
public:
ClusterFileV2(std::filesystem::path const &fpath, std::string const &mode): m_fpath(fpath), m_mode(mode) {
if (m_mode != "r" && m_mode != "w")
throw std::invalid_argument("mode must be 'r' or 'w'");
if (m_mode == "r" && !std::filesystem::exists(m_fpath))
throw std::invalid_argument("File does not exist");
if (mode == "r") {
fp = fopen(fpath.string().c_str(), "rb");
} else if (mode == "w") {
if (std::filesystem::exists(fpath)) {
fp = fopen(fpath.string().c_str(), "r+b");
} else {
fp = fopen(fpath.string().c_str(), "wb");
}
}
if (fp == nullptr) {
throw std::runtime_error("Failed to open file");
}
}
~ClusterFileV2() { close(); }
std::vector<ClusterV2> read() {
check_open();
ClusterHeader header;
fread(&header, sizeof(ClusterHeader), 1, fp);
std::vector<ClusterV2_> clusters_(header.n_clusters);
fread(clusters_.data(), sizeof(ClusterV2_), header.n_clusters, fp);
std::vector<ClusterV2> clusters;
for (auto &c : clusters_) {
ClusterV2 cluster;
cluster.cluster = std::move(c);
cluster.frame_number = header.frame_number;
clusters.push_back(cluster);
}
return clusters;
}
std::vector<std::vector<ClusterV2>> read(int n_frames) {
std::vector<std::vector<ClusterV2>> clusters;
for (int i = 0; i < n_frames; i++) {
clusters.push_back(read());
}
return clusters;
}
size_t write(std::vector<ClusterV2> const &clusters) {
check_open();
if (m_mode != "w")
throw std::runtime_error("File not opened in write mode");
if (clusters.empty())
return 0;
ClusterHeader header;
header.frame_number = clusters[0].frame_number;
header.n_clusters = clusters.size();
fwrite(&header, sizeof(ClusterHeader), 1, fp);
for (auto &c : clusters) {
fwrite(&c.cluster, sizeof(ClusterV2_), 1, fp);
}
return clusters.size();
}
size_t write(std::vector<std::vector<ClusterV2>> const &clusters) {
check_open();
if (m_mode != "w")
throw std::runtime_error("File not opened in write mode");
size_t n_clusters = 0;
for (auto &c : clusters) {
n_clusters += write(c);
}
return n_clusters;
}
int seek_to_begin() { return fseek(fp, 0, SEEK_SET); }
int seek_to_end() { return fseek(fp, 0, SEEK_END); }
int32_t frame_number() {
auto pos = ftell(fp);
ClusterHeader header;
fread(&header, sizeof(ClusterHeader), 1, fp);
fseek(fp, pos, SEEK_SET);
return header.frame_number;
}
void close() {
if (fp) {
fclose(fp);
fp = nullptr;
}
}
};
} // namespace aare

View File

@ -10,17 +10,19 @@
namespace aare { namespace aare {
template <typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double, template <typename ClusterType = Cluster<int32_t, 3, 3>,
typename CT = int32_t> typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double>
class ClusterFinder { class ClusterFinder {
Shape<2> m_image_size; Shape<2> m_image_size;
const int m_cluster_sizeX;
const int m_cluster_sizeY;
const PEDESTAL_TYPE m_nSigma; const PEDESTAL_TYPE m_nSigma;
const PEDESTAL_TYPE c2; const PEDESTAL_TYPE c2;
const PEDESTAL_TYPE c3; const PEDESTAL_TYPE c3;
Pedestal<PEDESTAL_TYPE> m_pedestal; Pedestal<PEDESTAL_TYPE> m_pedestal;
ClusterVector<CT> m_clusters; ClusterVector<ClusterType> m_clusters;
static const uint8_t ClusterSizeX = ClusterType::cluster_size_x;
static const uint8_t ClusterSizeY = ClusterType::cluster_size_y;
using CT = typename ClusterType::value_type;
public: public:
/** /**
@ -31,15 +33,16 @@ class ClusterFinder {
* @param capacity initial capacity of the cluster vector * @param capacity initial capacity of the cluster vector
* *
*/ */
ClusterFinder(Shape<2> image_size, Shape<2> cluster_size, ClusterFinder(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0,
PEDESTAL_TYPE nSigma = 5.0, size_t capacity = 1000000) size_t capacity = 1000000)
: m_image_size(image_size), m_cluster_sizeX(cluster_size[0]), : m_image_size(image_size), m_nSigma(nSigma),
m_cluster_sizeY(cluster_size[1]), c2(sqrt((ClusterSizeY + 1) / 2 * (ClusterSizeX + 1) / 2)),
m_nSigma(nSigma), c3(sqrt(ClusterSizeX * ClusterSizeY)),
c2(sqrt((m_cluster_sizeY + 1) / 2 * (m_cluster_sizeX + 1) / 2)), m_pedestal(image_size[0], image_size[1]), m_clusters(capacity) {
c3(sqrt(m_cluster_sizeX * m_cluster_sizeY)), LOG(logDEBUG) << "ClusterFinder: "
m_pedestal(image_size[0], image_size[1]), << "image_size: " << image_size[0] << "x" << image_size[1]
m_clusters(m_cluster_sizeX, m_cluster_sizeY, capacity) {}; << ", nSigma: " << nSigma << ", capacity: " << capacity;
}
void push_pedestal_frame(NDView<FRAME_TYPE, 2> frame) { void push_pedestal_frame(NDView<FRAME_TYPE, 2> frame) {
m_pedestal.push(frame); m_pedestal.push(frame);
@ -56,23 +59,28 @@ class ClusterFinder {
* same capacity as the old one * same capacity as the old one
* *
*/ */
ClusterVector<CT> steal_clusters(bool realloc_same_capacity = false) { ClusterVector<ClusterType>
ClusterVector<CT> tmp = std::move(m_clusters); steal_clusters(bool realloc_same_capacity = false) {
ClusterVector<ClusterType> tmp = std::move(m_clusters);
if (realloc_same_capacity) if (realloc_same_capacity)
m_clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY, m_clusters = ClusterVector<ClusterType>(tmp.capacity());
tmp.capacity());
else else
m_clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY); m_clusters = ClusterVector<ClusterType>{};
return tmp; return tmp;
} }
void find_clusters(NDView<FRAME_TYPE, 2> frame, uint64_t frame_number = 0) { void find_clusters(NDView<FRAME_TYPE, 2> frame, uint64_t frame_number = 0) {
// // TODO! deal with even size clusters // // TODO! deal with even size clusters
// // currently 3,3 -> +/- 1 // // currently 3,3 -> +/- 1
// // 4,4 -> +/- 2 // // 4,4 -> +/- 2
int dy = m_cluster_sizeY / 2; int dy = ClusterSizeY / 2;
int dx = m_cluster_sizeX / 2; int dx = ClusterSizeX / 2;
int has_center_pixel_x =
ClusterSizeX %
2; // for even sized clusters there is no proper cluster center and
// even amount of pixels around the center
int has_center_pixel_y = ClusterSizeY % 2;
m_clusters.set_frame_number(frame_number); m_clusters.set_frame_number(frame_number);
std::vector<CT> cluster_data(m_cluster_sizeX * m_cluster_sizeY);
for (int iy = 0; iy < frame.shape(0); iy++) { for (int iy = 0; iy < frame.shape(0); iy++) {
for (int ix = 0; ix < frame.shape(1); ix++) { for (int ix = 0; ix < frame.shape(1); ix++) {
@ -87,8 +95,8 @@ class ClusterFinder {
continue; // NEGATIVE_PEDESTAL go to next pixel continue; // NEGATIVE_PEDESTAL go to next pixel
// TODO! No pedestal update??? // TODO! No pedestal update???
for (int ir = -dy; ir < dy + 1; ir++) { for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) {
for (int ic = -dx; ic < dx + 1; ic++) { for (int ic = -dx; ic < dx + has_center_pixel_x; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) && if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) { iy + ir >= 0 && iy + ir < frame.shape(0)) {
PEDESTAL_TYPE val = PEDESTAL_TYPE val =
@ -109,27 +117,33 @@ class ClusterFinder {
// pass // pass
} else { } else {
// m_pedestal.push(iy, ix, frame(iy, ix)); // Safe option // m_pedestal.push(iy, ix, frame(iy, ix)); // Safe option
m_pedestal.push_fast(iy, ix, frame(iy, ix)); // Assume we have reached n_samples in the pedestal, slight performance improvement m_pedestal.push_fast(
continue; // It was a pedestal value nothing to store iy, ix,
frame(iy,
ix)); // Assume we have reached n_samples in the
// pedestal, slight performance improvement
continue; // It was a pedestal value nothing to store
} }
// Store cluster // Store cluster
if (value == max) { if (value == max) {
// Zero out the cluster data ClusterType cluster{};
std::fill(cluster_data.begin(), cluster_data.end(), 0); cluster.x = ix;
cluster.y = iy;
// Fill the cluster data since we have a photon to store // Fill the cluster data since we have a photon to store
// It's worth redoing the look since most of the time we // It's worth redoing the look since most of the time we
// don't have a photon // don't have a photon
int i = 0; int i = 0;
for (int ir = -dy; ir < dy + 1; ir++) { for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) {
for (int ic = -dx; ic < dx + 1; ic++) { for (int ic = -dx; ic < dx + has_center_pixel_y; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) && if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) { iy + ir >= 0 && iy + ir < frame.shape(0)) {
CT tmp = CT tmp =
static_cast<CT>(frame(iy + ir, ix + ic)) - static_cast<CT>(frame(iy + ir, ix + ic)) -
m_pedestal.mean(iy + ir, ix + ic); static_cast<CT>(
cluster_data[i] = m_pedestal.mean(iy + ir, ix + ic));
cluster.data[i] =
tmp; // Watch for out of bounds access tmp; // Watch for out of bounds access
i++; i++;
} }
@ -137,9 +151,7 @@ class ClusterFinder {
} }
// Add the cluster to the output ClusterVector // Add the cluster to the output ClusterVector
m_clusters.push_back( m_clusters.push_back(cluster);
ix, iy,
reinterpret_cast<std::byte *>(cluster_data.data()));
} }
} }
} }

View File

@ -8,6 +8,7 @@
#include "aare/ClusterFinder.hpp" #include "aare/ClusterFinder.hpp"
#include "aare/NDArray.hpp" #include "aare/NDArray.hpp"
#include "aare/ProducerConsumerQueue.hpp" #include "aare/ProducerConsumerQueue.hpp"
#include "aare/logger.hpp"
namespace aare { namespace aare {
@ -30,14 +31,17 @@ struct FrameWrapper {
* @tparam PEDESTAL_TYPE type of the pedestal data * @tparam PEDESTAL_TYPE type of the pedestal data
* @tparam CT type of the cluster data * @tparam CT type of the cluster data
*/ */
template <typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double, template <typename ClusterType = Cluster<int32_t, 3, 3>,
typename CT = int32_t> typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double>
class ClusterFinderMT { class ClusterFinderMT {
protected:
using CT = typename ClusterType::value_type;
size_t m_current_thread{0}; size_t m_current_thread{0};
size_t m_n_threads{0}; size_t m_n_threads{0};
using Finder = ClusterFinder<FRAME_TYPE, PEDESTAL_TYPE, CT>; using Finder = ClusterFinder<ClusterType, FRAME_TYPE, PEDESTAL_TYPE>;
using InputQueue = ProducerConsumerQueue<FrameWrapper>; using InputQueue = ProducerConsumerQueue<FrameWrapper>;
using OutputQueue = ProducerConsumerQueue<ClusterVector<int>>; using OutputQueue = ProducerConsumerQueue<ClusterVector<ClusterType>>;
std::vector<std::unique_ptr<InputQueue>> m_input_queues; std::vector<std::unique_ptr<InputQueue>> m_input_queues;
std::vector<std::unique_ptr<OutputQueue>> m_output_queues; std::vector<std::unique_ptr<OutputQueue>> m_output_queues;
@ -48,6 +52,7 @@ class ClusterFinderMT {
std::thread m_collect_thread; std::thread m_collect_thread;
std::chrono::milliseconds m_default_wait{1}; std::chrono::milliseconds m_default_wait{1};
private:
std::atomic<bool> m_stop_requested{false}; std::atomic<bool> m_stop_requested{false};
std::atomic<bool> m_processing_threads_stopped{true}; std::atomic<bool> m_processing_threads_stopped{true};
@ -66,7 +71,8 @@ class ClusterFinderMT {
switch (frame->type) { switch (frame->type) {
case FrameType::DATA: case FrameType::DATA:
cf->find_clusters(frame->data.view(), frame->frame_number); cf->find_clusters(frame->data.view(), frame->frame_number);
m_output_queues[thread_id]->write(cf->steal_clusters(realloc_same_capacity)); m_output_queues[thread_id]->write(
cf->steal_clusters(realloc_same_capacity));
break; break;
case FrameType::PEDESTAL: case FrameType::PEDESTAL:
@ -114,28 +120,38 @@ class ClusterFinderMT {
* expected number of clusters in a frame per frame. * expected number of clusters in a frame per frame.
* @param n_threads number of threads to use * @param n_threads number of threads to use
*/ */
ClusterFinderMT(Shape<2> image_size, Shape<2> cluster_size, ClusterFinderMT(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0,
PEDESTAL_TYPE nSigma = 5.0, size_t capacity = 2000, size_t capacity = 2000, size_t n_threads = 3)
size_t n_threads = 3)
: m_n_threads(n_threads) { : m_n_threads(n_threads) {
LOG(logDEBUG1) << "ClusterFinderMT: "
<< "image_size: " << image_size[0] << "x"
<< image_size[1] << ", nSigma: " << nSigma
<< ", capacity: " << capacity
<< ", n_threads: " << n_threads;
for (size_t i = 0; i < n_threads; i++) { for (size_t i = 0; i < n_threads; i++) {
m_cluster_finders.push_back( m_cluster_finders.push_back(
std::make_unique<ClusterFinder<FRAME_TYPE, PEDESTAL_TYPE, CT>>( std::make_unique<
image_size, cluster_size, nSigma, capacity)); ClusterFinder<ClusterType, FRAME_TYPE, PEDESTAL_TYPE>>(
image_size, nSigma, capacity));
} }
for (size_t i = 0; i < n_threads; i++) { for (size_t i = 0; i < n_threads; i++) {
m_input_queues.emplace_back(std::make_unique<InputQueue>(200)); m_input_queues.emplace_back(std::make_unique<InputQueue>(200));
m_output_queues.emplace_back(std::make_unique<OutputQueue>(200)); m_output_queues.emplace_back(std::make_unique<OutputQueue>(200));
} }
//TODO! Should we start automatically? // TODO! Should we start automatically?
start(); start();
} }
/** /**
* @brief Return the sink queue where all the clusters are collected * @brief Return the sink queue where all the clusters are collected
* @warning You need to empty this queue otherwise the cluster finder will wait forever * @warning You need to empty this queue otherwise the cluster finder will
* wait forever
*/ */
ProducerConsumerQueue<ClusterVector<int>> *sink() { return &m_sink; } ProducerConsumerQueue<ClusterVector<ClusterType>> *sink() {
return &m_sink;
}
/** /**
* @brief Start all processing threads * @brief Start all processing threads

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "aare/Cluster.hpp" //TODO maybe store in seperate file !!!
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cstddef> #include <cstddef>
@ -13,292 +14,157 @@
namespace aare { namespace aare {
template <typename ClusterType,
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
class ClusterVector; // Forward declaration
/** /**
* @brief ClusterVector is a container for clusters of various sizes. It uses a * @brief ClusterVector is a container for clusters of various sizes. It
* contiguous memory buffer to store the clusters. It is templated on the data * uses a contiguous memory buffer to store the clusters. It is templated on
* type and the coordinate type of the clusters. * the data type and the coordinate type of the clusters.
* @note push_back can invalidate pointers to elements in the container * @note push_back can invalidate pointers to elements in the container
* @warning ClusterVector is currently move only to catch unintended copies, but * @warning ClusterVector is currently move only to catch unintended copies,
* this might change since there are probably use cases where copying is needed. * but this might change since there are probably use cases where copying is
* needed.
* @tparam T data type of the pixels in the cluster * @tparam T data type of the pixels in the cluster
* @tparam CoordType data type of the x and y coordinates of the cluster * @tparam CoordType data type of the x and y coordinates of the cluster
* (normally int16_t) * (normally int16_t)
*/ */
template <typename T, typename CoordType = int16_t> class ClusterVector { template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
using value_type = T; typename CoordType>
size_t m_cluster_size_x; class ClusterVector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> {
size_t m_cluster_size_y;
std::byte *m_data{}; std::vector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> m_data{};
size_t m_size{0}; int32_t m_frame_number{0}; // TODO! Check frame number size and type
size_t m_capacity;
uint64_t m_frame_number{0}; // TODO! Check frame number size and type
/*
Format string used in the python bindings to create a numpy
array from the buffer
= - native byte order
h - short
d - double
i - int
*/
constexpr static char m_fmt_base[] = "=h:x:\nh:y:\n({},{}){}:data:";
public: public:
using value_type = T;
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
/** /**
* @brief Construct a new ClusterVector object * @brief Construct a new ClusterVector object
* @param cluster_size_x size of the cluster in x direction
* @param cluster_size_y size of the cluster in y direction
* @param capacity initial capacity of the buffer in number of clusters * @param capacity initial capacity of the buffer in number of clusters
* @param frame_number frame number of the clusters. Default is 0, which is * @param frame_number frame number of the clusters. Default is 0, which is
* also used to indicate that the clusters come from many frames * also used to indicate that the clusters come from many frames
*/ */
ClusterVector(size_t cluster_size_x = 3, size_t cluster_size_y = 3, ClusterVector(size_t capacity = 1024, uint64_t frame_number = 0)
size_t capacity = 1024, uint64_t frame_number = 0) : m_frame_number(frame_number) {
: m_cluster_size_x(cluster_size_x), m_cluster_size_y(cluster_size_y), m_data.reserve(capacity);
m_capacity(capacity), m_frame_number(frame_number) {
allocate_buffer(capacity);
} }
~ClusterVector() { delete[] m_data; }
// Move constructor // Move constructor
ClusterVector(ClusterVector &&other) noexcept ClusterVector(ClusterVector &&other) noexcept
: m_cluster_size_x(other.m_cluster_size_x), : m_data(other.m_data), m_frame_number(other.m_frame_number) {
m_cluster_size_y(other.m_cluster_size_y), m_data(other.m_data), other.m_data.clear();
m_size(other.m_size), m_capacity(other.m_capacity),
m_frame_number(other.m_frame_number) {
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
} }
// Move assignment operator // Move assignment operator
ClusterVector &operator=(ClusterVector &&other) noexcept { ClusterVector &operator=(ClusterVector &&other) noexcept {
if (this != &other) { if (this != &other) {
delete[] m_data;
m_cluster_size_x = other.m_cluster_size_x;
m_cluster_size_y = other.m_cluster_size_y;
m_data = other.m_data; m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
m_frame_number = other.m_frame_number; m_frame_number = other.m_frame_number;
other.m_data = nullptr; other.m_data.clear();
other.m_size = 0;
other.m_capacity = 0;
other.m_frame_number = 0; other.m_frame_number = 0;
} }
return *this; return *this;
} }
/**
* @brief Reserve space for at least capacity clusters
* @param capacity number of clusters to reserve space for
* @note If capacity is less than the current capacity, the function does
* nothing.
*/
void reserve(size_t capacity) {
if (capacity > m_capacity) {
allocate_buffer(capacity);
}
}
/**
* @brief Add a cluster to the vector
* @param x x-coordinate of the cluster
* @param y y-coordinate of the cluster
* @param data pointer to the data of the cluster
* @warning The data pointer must point to a buffer of size cluster_size_x *
* cluster_size_y * sizeof(T)
*/
void push_back(CoordType x, CoordType y, const std::byte *data) {
if (m_size == m_capacity) {
allocate_buffer(m_capacity * 2);
}
std::byte *ptr = element_ptr(m_size);
*reinterpret_cast<CoordType *>(ptr) = x;
ptr += sizeof(CoordType);
*reinterpret_cast<CoordType *>(ptr) = y;
ptr += sizeof(CoordType);
std::copy(data, data + m_cluster_size_x * m_cluster_size_y * sizeof(T),
ptr);
m_size++;
}
ClusterVector &operator+=(const ClusterVector &other) {
if (m_size + other.m_size > m_capacity) {
allocate_buffer(m_capacity + other.m_size);
}
std::copy(other.m_data, other.m_data + other.m_size * item_size(),
m_data + m_size * item_size());
m_size += other.m_size;
return *this;
}
/** /**
* @brief Sum the pixels in each cluster * @brief Sum the pixels in each cluster
* @return std::vector<T> vector of sums for each cluster * @return std::vector<T> vector of sums for each cluster
*/ */
std::vector<T> sum() { std::vector<T> sum() {
std::vector<T> sums(m_size); std::vector<T> sums(m_data.size());
const size_t stride = item_size();
const size_t n_pixels = m_cluster_size_x * m_cluster_size_y; std::transform(
std::byte *ptr = m_data + 2 * sizeof(CoordType); // skip x and y m_data.begin(), m_data.end(), sums.begin(),
[](const ClusterType &cluster) { return cluster.sum(); });
for (size_t i = 0; i < m_size; i++) {
sums[i] =
std::accumulate(reinterpret_cast<T *>(ptr),
reinterpret_cast<T *>(ptr) + n_pixels, T{});
ptr += stride;
}
return sums; return sums;
} }
/** /**
* @brief Return the maximum sum of the 2x2 subclusters in each cluster * @brief Sum the pixels in the 2x2 subcluster with the biggest pixel sum in
* each cluster
* @return std::vector<T> vector of sums for each cluster * @return std::vector<T> vector of sums for each cluster
* @throws std::runtime_error if the cluster size is not 3x3
* @warning Only 3x3 clusters are supported for the 2x2 sum.
*/ */
std::vector<T> sum_2x2() { std::vector<T> sum_2x2() {
std::vector<T> sums(m_size); std::vector<T> sums_2x2(m_data.size());
const size_t stride = item_size();
if (m_cluster_size_x != 3 || m_cluster_size_y != 3) { std::transform(m_data.begin(), m_data.end(), sums_2x2.begin(),
throw std::runtime_error( [](const ClusterType &cluster) {
"Only 3x3 clusters are supported for the 2x2 sum."); return cluster.max_sum_2x2().first;
} });
std::byte *ptr = m_data + 2 * sizeof(CoordType); // skip x and y
for (size_t i = 0; i < m_size; i++) { return sums_2x2;
std::array<T, 4> total; }
auto T_ptr = reinterpret_cast<T *>(ptr);
total[0] = T_ptr[0] + T_ptr[1] + T_ptr[3] + T_ptr[4];
total[1] = T_ptr[1] + T_ptr[2] + T_ptr[4] + T_ptr[5];
total[2] = T_ptr[3] + T_ptr[4] + T_ptr[6] + T_ptr[7];
total[3] = T_ptr[4] + T_ptr[5] + T_ptr[7] + T_ptr[8];
sums[i] = *std::max_element(total.begin(), total.end()); /**
ptr += stride; * @brief Reserve space for at least capacity clusters
} * @param capacity number of clusters to reserve space for
* @note If capacity is less than the current capacity, the function does
* nothing.
*/
void reserve(size_t capacity) { m_data.reserve(capacity); }
return sums; void resize(size_t size) { m_data.resize(size); }
void push_back(const ClusterType &cluster) { m_data.push_back(cluster); }
ClusterVector &operator+=(const ClusterVector &other) {
m_data.insert(m_data.end(), other.begin(), other.end());
return *this;
} }
/** /**
* @brief Return the number of clusters in the vector * @brief Return the number of clusters in the vector
*/ */
size_t size() const { return m_size; } size_t size() const { return m_data.size(); }
uint8_t cluster_size_x() const { return ClusterSizeX; }
uint8_t cluster_size_y() const { return ClusterSizeY; }
/** /**
* @brief Return the capacity of the buffer in number of clusters. This is * @brief Return the capacity of the buffer in number of clusters. This is
* the number of clusters that can be stored in the current buffer without * the number of clusters that can be stored in the current buffer without
* reallocation. * reallocation.
*/ */
size_t capacity() const { return m_capacity; } size_t capacity() const { return m_data.capacity(); }
auto begin() const { return m_data.begin(); }
auto end() const { return m_data.end(); }
/** /**
* @brief Return the size in bytes of a single cluster * @brief Return the size in bytes of a single cluster
*/ */
size_t item_size() const { size_t item_size() const {
return 2 * sizeof(CoordType) + return sizeof(ClusterType); // 2 * sizeof(CoordType) + ClusterSizeX *
m_cluster_size_x * m_cluster_size_y * sizeof(T); // ClusterSizeY * sizeof(T);
} }
/** ClusterType *data() { return m_data.data(); }
* @brief Return the offset in bytes for the i-th cluster ClusterType const *data() const { return m_data.data(); }
*/
size_t element_offset(size_t i) const { return item_size() * i; }
/**
* @brief Return a pointer to the i-th cluster
*/
std::byte *element_ptr(size_t i) { return m_data + element_offset(i); }
/**
* @brief Return a pointer to the i-th cluster
*/
const std::byte *element_ptr(size_t i) const {
return m_data + element_offset(i);
}
size_t cluster_size_x() const { return m_cluster_size_x; }
size_t cluster_size_y() const { return m_cluster_size_y; }
std::byte *data() { return m_data; }
std::byte const *data() const { return m_data; }
/** /**
* @brief Return a reference to the i-th cluster casted to type V * @brief Return a reference to the i-th cluster casted to type V
* @tparam V type of the cluster * @tparam V type of the cluster
*/ */
template <typename V> V &at(size_t i) { ClusterType &operator[](size_t i) { return m_data[i]; }
return *reinterpret_cast<V *>(element_ptr(i));
}
template <typename V> const V &at(size_t i) const { const ClusterType &operator[](size_t i) const { return m_data[i]; }
return *reinterpret_cast<const V *>(element_ptr(i));
}
const std::string_view fmt_base() const {
// TODO! how do we match on coord_t?
return m_fmt_base;
}
/** /**
* @brief Return the frame number of the clusters. 0 is used to indicate * @brief Return the frame number of the clusters. 0 is used to indicate
* that the clusters come from many frames * that the clusters come from many frames
*/ */
uint64_t frame_number() const { return m_frame_number; } int32_t frame_number() const { return m_frame_number; }
void set_frame_number(uint64_t frame_number) { void set_frame_number(int32_t frame_number) {
m_frame_number = frame_number; m_frame_number = frame_number;
} }
/**
* @brief Resize the vector to contain new_size clusters. If new_size is
* greater than the current capacity, a new buffer is allocated. If the size
* is smaller no memory is freed, size is just updated.
* @param new_size new size of the vector
* @warning The additional clusters are not initialized
*/
void resize(size_t new_size) {
// TODO! Should we initialize the new clusters?
if (new_size > m_capacity) {
allocate_buffer(new_size);
}
m_size = new_size;
}
void apply_gain_map(const NDView<double> gain_map){
//in principle we need to know the size of the image for this lookup
//TODO! check orientations
std::array<int64_t, 9> xcorr = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
std::array<int64_t, 9> ycorr = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
for (size_t i=0; i<m_size; i++){
auto& cl = at<Cluster3x3>(i);
if (cl.x > 0 && cl.y > 0 && cl.x < gain_map.shape(1)-1 && cl.y < gain_map.shape(0)-1){
for (size_t j=0; j<9; j++){
size_t x = cl.x + xcorr[j];
size_t y = cl.y + ycorr[j];
cl.data[j] = static_cast<T>(cl.data[j] * gain_map(y, x));
}
}else{
memset(cl.data, 0, 9*sizeof(T)); //clear edge clusters
}
}
}
private:
void allocate_buffer(size_t new_capacity) {
size_t num_bytes = item_size() * new_capacity;
std::byte *new_data = new std::byte[num_bytes]{};
std::copy(m_data, m_data + item_size() * m_size, new_data);
delete[] m_data;
m_data = new_data;
m_capacity = new_capacity;
}
}; };
} // namespace aare } // namespace aare

View File

@ -1,27 +1,27 @@
#pragma once #pragma once
#include "aare/FileInterface.hpp" #include "aare/FileInterface.hpp"
#include "aare/RawMasterFile.hpp"
#include "aare/Frame.hpp" #include "aare/Frame.hpp"
#include "aare/RawMasterFile.hpp"
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
namespace aare{ namespace aare {
class CtbRawFile {
class CtbRawFile{
RawMasterFile m_master; RawMasterFile m_master;
std::ifstream m_file; std::ifstream m_file;
size_t m_current_frame{0}; size_t m_current_frame{0};
size_t m_current_subfile{0}; size_t m_current_subfile{0};
size_t m_num_subfiles{0}; size_t m_num_subfiles{0};
public:
public:
CtbRawFile(const std::filesystem::path &fname); CtbRawFile(const std::filesystem::path &fname);
void read_into(std::byte *image_buf, DetectorHeader* header = nullptr); void read_into(std::byte *image_buf, DetectorHeader *header = nullptr);
void seek(size_t frame_index); //!< seek to the given frame index void seek(size_t frame_index); //!< seek to the given frame index
size_t tell() const; //!< get the frame index of the file pointer size_t tell() const; //!< get the frame index of the file pointer
// in the specific class we can expose more functionality // in the specific class we can expose more functionality
@ -29,13 +29,13 @@ public:
size_t frames_in_file() const; size_t frames_in_file() const;
RawMasterFile master() const; RawMasterFile master() const;
private:
private:
void find_subfiles(); void find_subfiles();
size_t sub_file_index(size_t frame_index) const { size_t sub_file_index(size_t frame_index) const {
return frame_index / m_master.max_frames_per_file(); return frame_index / m_master.max_frames_per_file();
} }
void open_data_file(size_t subfile_index); void open_data_file(size_t subfile_index);
}; };
} } // namespace aare

View File

@ -0,0 +1,81 @@
#pragma once
#include "aare/RawMasterFile.hpp" //ROI refactor away
#include "aare/defs.hpp"
namespace aare {
struct ModuleConfig {
int module_gap_row{};
int module_gap_col{};
bool operator==(const ModuleConfig &other) const {
if (module_gap_col != other.module_gap_col)
return false;
if (module_gap_row != other.module_gap_row)
return false;
return true;
}
};
/**
* @brief Class to hold the geometry of a module. Where pixel 0 is located and
* the size of the module
*/
struct ModuleGeometry {
int origin_x{};
int origin_y{};
int height{};
int width{};
int row_index{};
int col_index{};
};
/**
* @brief Class to hold the geometry of a detector. Number of modules, their
* size and where pixel 0 for each module is located
*/
class DetectorGeometry {
public:
DetectorGeometry(const xy &geometry, const ssize_t module_pixels_x,
const ssize_t module_pixels_y,
const xy udp_interfaces_per_module = xy{1, 1},
const bool quad = false);
~DetectorGeometry() = default;
/**
* @brief Update the detector geometry given a region of interest
*
* @param roi
* @return DetectorGeometry
*/
void update_geometry_with_roi(ROI roi);
size_t n_modules() const;
size_t n_modules_in_roi() const;
size_t pixels_x() const;
size_t pixels_y() const;
size_t modules_x() const;
size_t modules_y() const;
const std::vector<ssize_t> &get_modules_in_roi() const;
ssize_t get_modules_in_roi(const size_t index) const;
const std::vector<ModuleGeometry> &get_module_geometries() const;
const ModuleGeometry &get_module_geometries(const size_t index) const;
private:
size_t m_modules_x{};
size_t m_modules_y{};
size_t m_pixels_x{};
size_t m_pixels_y{};
static constexpr ModuleConfig cfg{0, 0};
std::vector<ModuleGeometry> module_geometries{};
std::vector<ssize_t> modules_in_roi{};
};
} // namespace aare

View File

@ -6,31 +6,37 @@
namespace aare { namespace aare {
// The format descriptor is a single character that specifies the type of the data // The format descriptor is a single character that specifies the type of the
// data
// - python documentation: https://docs.python.org/3/c-api/arg.html#numbers // - python documentation: https://docs.python.org/3/c-api/arg.html#numbers
// - py::format_descriptor<T>::format() (in pybind11) does not return the same format as // - py::format_descriptor<T>::format() (in pybind11) does not return the same
// format as
// written in python.org documentation. // written in python.org documentation.
// - numpy also doesn't use the same format. and also numpy associates the format // - numpy also doesn't use the same format. and also numpy associates the
// with variable bitdepth types. (e.g. long is int64 on linux64 and int32 on win64) // format
// https://numpy.org/doc/stable/reference/arrays.scalars.html // with variable bitdepth types. (e.g. long is int64 on linux64 and int32 on
// win64) https://numpy.org/doc/stable/reference/arrays.scalars.html
// //
// github issue discussing this: // github issue discussing this:
// https://github.com/pybind/pybind11/issues/1908#issuecomment-658358767 // https://github.com/pybind/pybind11/issues/1908#issuecomment-658358767
// //
// [IN LINUX] the difference is for int64 (long) and uint64 (unsigned long). The format // [IN LINUX] the difference is for int64 (long) and uint64 (unsigned long). The
// descriptor is 'q' and 'Q' respectively and in the documentation it is 'l' and 'k'. // format descriptor is 'q' and 'Q' respectively and in the documentation it is
// 'l' and 'k'.
// in practice numpy doesn't seem to care when reading buffer info: the library // in practice numpy doesn't seem to care when reading buffer info: the library
// interprets 'q' or 'l' as int64 and 'Q' or 'L' as uint64. // interprets 'q' or 'l' as int64 and 'Q' or 'L' as uint64.
// for this reason we decided to use the same format descriptor as pybind to avoid // for this reason we decided to use the same format descriptor as pybind to
// any further discrepancies. // avoid any further discrepancies.
// in the following order: // in the following order:
// int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double // int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double
const char DTYPE_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i', 'I', 'q', 'Q', 'f', 'd'}; const char DTYPE_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i',
'I', 'q', 'Q', 'f', 'd'};
// on linux64 & apple // on linux64 & apple
const char NUMPY_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'f', 'd'}; const char NUMPY_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i',
'I', 'l', 'L', 'f', 'd'};
/** /**
* @brief enum class to define the endianess of the system * @brief enum class to define the endianess of the system
*/ */
@ -52,12 +58,29 @@ enum class endian {
*/ */
class Dtype { class Dtype {
public: public:
enum TypeIndex { INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT, DOUBLE, ERROR, NONE }; enum TypeIndex {
INT8,
UINT8,
INT16,
UINT16,
INT32,
UINT32,
INT64,
UINT64,
FLOAT,
DOUBLE,
ERROR,
NONE
};
uint8_t bitdepth() const; uint8_t bitdepth() const;
size_t bytes() const; size_t bytes() const;
std::string format_descr() const { return std::string(1, DTYPE_FORMAT_DSC[static_cast<int>(m_type)]); } std::string format_descr() const {
std::string numpy_descr() const { return std::string(1, NUMPY_FORMAT_DSC[static_cast<int>(m_type)]); } return std::string(1, DTYPE_FORMAT_DSC[static_cast<int>(m_type)]);
}
std::string numpy_descr() const {
return std::string(1, NUMPY_FORMAT_DSC[static_cast<int>(m_type)]);
}
explicit Dtype(const std::type_info &t); explicit Dtype(const std::type_info &t);
explicit Dtype(std::string_view sv); explicit Dtype(std::string_view sv);

View File

@ -6,11 +6,11 @@ namespace aare {
/** /**
* @brief RAII File class for reading, and in the future potentially writing * @brief RAII File class for reading, and in the future potentially writing
* image files in various formats. Minimal generic interface. For specail fuctions * image files in various formats. Minimal generic interface. For specail
* plase use the RawFile or NumpyFile classes directly. * fuctions plase use the RawFile or NumpyFile classes directly. Wraps
* Wraps FileInterface to abstract the underlying file format * FileInterface to abstract the underlying file format
* @note **frame_number** refers the the frame number sent by the detector while **frame_index** * @note **frame_number** refers the the frame number sent by the detector while
* is the position of the frame in the file * **frame_index** is the position of the frame in the file
*/ */
class File { class File {
std::unique_ptr<FileInterface> file_impl; std::unique_ptr<FileInterface> file_impl;
@ -25,42 +25,46 @@ class File {
* @throws std::invalid_argument if the file mode is not supported * @throws std::invalid_argument if the file mode is not supported
* *
*/ */
File(const std::filesystem::path &fname, const std::string &mode="r", const FileConfig &cfg = {}); File(const std::filesystem::path &fname, const std::string &mode = "r",
const FileConfig &cfg = {});
/**Since the object is responsible for managing the file we disable copy construction */ /**Since the object is responsible for managing the file we disable copy
* construction */
File(File const &other) = delete; File(File const &other) = delete;
/**The same goes for copy assignment */ /**The same goes for copy assignment */
File& operator=(File const &other) = delete; File &operator=(File const &other) = delete;
File(File &&other) noexcept; File(File &&other) noexcept;
File& operator=(File &&other) noexcept; File &operator=(File &&other) noexcept;
~File() = default; ~File() = default;
// void close(); //!< close the file // void close(); //!< close the file
Frame read_frame(); //!< read one frame from the file at the current position Frame
Frame read_frame(size_t frame_index); //!< read one frame at the position given by frame number read_frame(); //!< read one frame from the file at the current position
std::vector<Frame> read_n(size_t n_frames); //!< read n_frames from the file at the current position Frame read_frame(size_t frame_index); //!< read one frame at the position
//!< given by frame number
std::vector<Frame> read_n(size_t n_frames); //!< read n_frames from the file
//!< at the current position
void read_into(std::byte *image_buf); void read_into(std::byte *image_buf);
void read_into(std::byte *image_buf, size_t n_frames); void read_into(std::byte *image_buf, size_t n_frames);
size_t frame_number(); //!< get the frame number at the current position size_t frame_number(); //!< get the frame number at the current position
size_t frame_number(size_t frame_index); //!< get the frame number at the given frame index size_t frame_number(
size_t frame_index); //!< get the frame number at the given frame index
size_t bytes_per_frame() const; size_t bytes_per_frame() const;
size_t pixels_per_frame() const; size_t pixels_per_frame() const;
size_t bytes_per_pixel() const; size_t bytes_per_pixel() const;
size_t bitdepth() const; size_t bitdepth() const;
void seek(size_t frame_index); //!< seek to the given frame index void seek(size_t frame_index); //!< seek to the given frame index
size_t tell() const; //!< get the frame index of the file pointer size_t tell() const; //!< get the frame index of the file pointer
size_t total_frames() const; size_t total_frames() const;
size_t rows() const; size_t rows() const;
size_t cols() const; size_t cols() const;
DetectorType detector_type() const; DetectorType detector_type() const;
}; };
} // namespace aare } // namespace aare

View File

@ -20,8 +20,10 @@ struct FileConfig {
uint64_t rows{}; uint64_t rows{};
uint64_t cols{}; uint64_t cols{};
bool operator==(const FileConfig &other) const { bool operator==(const FileConfig &other) const {
return dtype == other.dtype && rows == other.rows && cols == other.cols && geometry == other.geometry && return dtype == other.dtype && rows == other.rows &&
detector_type == other.detector_type && max_frames_per_file == other.max_frames_per_file; cols == other.cols && geometry == other.geometry &&
detector_type == other.detector_type &&
max_frames_per_file == other.max_frames_per_file;
} }
bool operator!=(const FileConfig &other) const { return !(*this == other); } bool operator!=(const FileConfig &other) const { return !(*this == other); }
@ -32,8 +34,11 @@ struct FileConfig {
int max_frames_per_file{}; int max_frames_per_file{};
size_t total_frames{}; size_t total_frames{};
std::string to_string() const { std::string to_string() const {
return "{ dtype: " + dtype.to_string() + ", rows: " + std::to_string(rows) + ", cols: " + std::to_string(cols) + return "{ dtype: " + dtype.to_string() +
", geometry: " + geometry.to_string() + ", detector_type: " + ToString(detector_type) + ", rows: " + std::to_string(rows) +
", cols: " + std::to_string(cols) +
", geometry: " + geometry.to_string() +
", detector_type: " + ToString(detector_type) +
", max_frames_per_file: " + std::to_string(max_frames_per_file) + ", max_frames_per_file: " + std::to_string(max_frames_per_file) +
", total_frames: " + std::to_string(total_frames) + " }"; ", total_frames: " + std::to_string(total_frames) + " }";
} }
@ -42,7 +47,8 @@ struct FileConfig {
/** /**
* @brief FileInterface class to define the interface for file operations * @brief FileInterface class to define the interface for file operations
* @note parent class for NumpyFile and RawFile * @note parent class for NumpyFile and RawFile
* @note all functions are pure virtual and must be implemented by the derived classes * @note all functions are pure virtual and must be implemented by the derived
* classes
*/ */
class FileInterface { class FileInterface {
public: public:
@ -64,17 +70,20 @@ class FileInterface {
* @param n_frames number of frames to read * @param n_frames number of frames to read
* @return vector of frames * @return vector of frames
*/ */
virtual std::vector<Frame> read_n(size_t n_frames) = 0; // Is this the right interface? virtual std::vector<Frame>
read_n(size_t n_frames) = 0; // Is this the right interface?
/** /**
* @brief read one frame from the file at the current position and store it in the provided buffer * @brief read one frame from the file at the current position and store it
* in the provided buffer
* @param image_buf buffer to store the frame * @param image_buf buffer to store the frame
* @return void * @return void
*/ */
virtual void read_into(std::byte *image_buf) = 0; virtual void read_into(std::byte *image_buf) = 0;
/** /**
* @brief read n_frames from the file at the current position and store them in the provided buffer * @brief read n_frames from the file at the current position and store them
* in the provided buffer
* @param image_buf buffer to store the frames * @param image_buf buffer to store the frames
* @param n_frames number of frames to read * @param n_frames number of frames to read
* @return void * @return void
@ -134,7 +143,6 @@ class FileInterface {
*/ */
virtual size_t bitdepth() const = 0; virtual size_t bitdepth() const = 0;
virtual DetectorType detector_type() const = 0; virtual DetectorType detector_type() const = 0;
// function to query the data type of the file // function to query the data type of the file

View File

@ -12,14 +12,14 @@ class FilePtr {
public: public:
FilePtr() = default; FilePtr() = default;
FilePtr(const std::filesystem::path& fname, const std::string& mode); FilePtr(const std::filesystem::path &fname, const std::string &mode);
FilePtr(const FilePtr &) = delete; // we don't want a copy FilePtr(const FilePtr &) = delete; // we don't want a copy
FilePtr &operator=(const FilePtr &) = delete; // since we handle a resource FilePtr &operator=(const FilePtr &) = delete; // since we handle a resource
FilePtr(FilePtr &&other); FilePtr(FilePtr &&other);
FilePtr &operator=(FilePtr &&other); FilePtr &operator=(FilePtr &&other);
FILE *get(); FILE *get();
int64_t tell(); ssize_t tell();
void seek(int64_t offset, int whence = SEEK_SET) { void seek(ssize_t offset, int whence = SEEK_SET) {
if (fseek(fp_, offset, whence) != 0) if (fseek(fp_, offset, whence) != 0)
throw std::runtime_error("Error seeking in file"); throw std::runtime_error("Error seeking in file");
} }

View File

@ -15,15 +15,27 @@ NDArray<double, 1> gaus(NDView<double, 1> x, NDView<double, 1> par);
double pol1(const double x, const double *par); double pol1(const double x, const double *par);
NDArray<double, 1> pol1(NDView<double, 1> x, NDView<double, 1> par); NDArray<double, 1> pol1(NDView<double, 1> x, NDView<double, 1> par);
} // namespace func double scurve(const double x, const double *par);
NDArray<double, 1> scurve(NDView<double, 1> x, NDView<double, 1> par);
double scurve2(const double x, const double *par);
NDArray<double, 1> scurve2(NDView<double, 1> x, NDView<double, 1> par);
} // namespace func
/** /**
* @brief Estimate the initial parameters for a Gaussian fit * @brief Estimate the initial parameters for a Gaussian fit
*/ */
std::array<double, 3> gaus_init_par(const NDView<double, 1> x, const NDView<double, 1> y); std::array<double, 3> gaus_init_par(const NDView<double, 1> x,
const NDView<double, 1> y);
std::array<double, 2> pol1_init_par(const NDView<double, 1> x, const NDView<double, 1> y); std::array<double, 2> pol1_init_par(const NDView<double, 1> x,
const NDView<double, 1> y);
std::array<double, 6> scurve_init_par(const NDView<double, 1> x,
const NDView<double, 1> y);
std::array<double, 6> scurve2_init_par(const NDView<double, 1> x,
const NDView<double, 1> y);
static constexpr int DEFAULT_NUM_THREADS = 4; static constexpr int DEFAULT_NUM_THREADS = 4;
@ -34,46 +46,41 @@ static constexpr int DEFAULT_NUM_THREADS = 4;
*/ */
NDArray<double, 1> fit_gaus(NDView<double, 1> x, NDView<double, 1> y); NDArray<double, 1> fit_gaus(NDView<double, 1> x, NDView<double, 1> y);
/** /**
* @brief Fit a 1D Gaussian to each pixel. Data layout [row, col, values] * @brief Fit a 1D Gaussian to each pixel. Data layout [row, col, values]
* @param x x values * @param x x values
* @param y y vales, layout [row, col, values] * @param y y values, layout [row, col, values]
* @param n_threads number of threads to use * @param n_threads number of threads to use
*/ */
NDArray<double, 3> fit_gaus(NDView<double, 1> x, NDView<double, 3> y, NDArray<double, 3> fit_gaus(NDView<double, 1> x, NDView<double, 3> y,
int n_threads = DEFAULT_NUM_THREADS); int n_threads = DEFAULT_NUM_THREADS);
/** /**
* @brief Fit a 1D Gaussian with error estimates * @brief Fit a 1D Gaussian with error estimates
* @param x x values * @param x x values
* @param y y vales, layout [row, col, values] * @param y y values, layout [row, col, values]
* @param y_err error in y, layout [row, col, values] * @param y_err error in y, layout [row, col, values]
* @param par_out output parameters * @param par_out output parameters
* @param par_err_out output error parameters * @param par_err_out output error parameters
*/ */
void fit_gaus(NDView<double, 1> x, NDView<double, 1> y, NDView<double, 1> y_err, void fit_gaus(NDView<double, 1> x, NDView<double, 1> y, NDView<double, 1> y_err,
NDView<double, 1> par_out, NDView<double, 1> par_err_out, NDView<double, 1> par_out, NDView<double, 1> par_err_out,
double& chi2); double &chi2);
/** /**
* @brief Fit a 1D Gaussian to each pixel with error estimates. Data layout * @brief Fit a 1D Gaussian to each pixel with error estimates. Data layout
* [row, col, values] * [row, col, values]
* @param x x values * @param x x values
* @param y y vales, layout [row, col, values] * @param y y values, layout [row, col, values]
* @param y_err error in y, layout [row, col, values] * @param y_err error in y, layout [row, col, values]
* @param par_out output parameters, layout [row, col, values] * @param par_out output parameters, layout [row, col, values]
* @param par_err_out output parameter errors, layout [row, col, values] * @param par_err_out output parameter errors, layout [row, col, values]
* @param n_threads number of threads to use * @param n_threads number of threads to use
*/ */
void fit_gaus(NDView<double, 1> x, NDView<double, 3> y, NDView<double, 3> y_err, void fit_gaus(NDView<double, 1> x, NDView<double, 3> y, NDView<double, 3> y_err,
NDView<double, 3> par_out, NDView<double, 3> par_err_out, NDView<double, 2> chi2_out, NDView<double, 3> par_out, NDView<double, 3> par_err_out,
int n_threads = DEFAULT_NUM_THREADS NDView<double, 2> chi2_out, int n_threads = DEFAULT_NUM_THREADS);
);
NDArray<double, 1> fit_pol1(NDView<double, 1> x, NDView<double, 1> y); NDArray<double, 1> fit_pol1(NDView<double, 1> x, NDView<double, 1> y);
@ -81,12 +88,33 @@ NDArray<double, 3> fit_pol1(NDView<double, 1> x, NDView<double, 3> y,
int n_threads = DEFAULT_NUM_THREADS); int n_threads = DEFAULT_NUM_THREADS);
void fit_pol1(NDView<double, 1> x, NDView<double, 1> y, NDView<double, 1> y_err, void fit_pol1(NDView<double, 1> x, NDView<double, 1> y, NDView<double, 1> y_err,
NDView<double, 1> par_out, NDView<double, 1> par_err_out, double& chi2); NDView<double, 1> par_out, NDView<double, 1> par_err_out,
double &chi2);
// TODO! not sure we need to offer the different version in C++ // TODO! not sure we need to offer the different version in C++
void fit_pol1(NDView<double, 1> x, NDView<double, 3> y, NDView<double, 3> y_err, void fit_pol1(NDView<double, 1> x, NDView<double, 3> y, NDView<double, 3> y_err,
NDView<double, 3> par_out, NDView<double, 3> par_err_out,NDView<double, 2> chi2_out, NDView<double, 3> par_out, NDView<double, 3> par_err_out,
int n_threads = DEFAULT_NUM_THREADS); NDView<double, 2> chi2_out, int n_threads = DEFAULT_NUM_THREADS);
NDArray<double, 1> fit_scurve(NDView<double, 1> x, NDView<double, 1> y);
NDArray<double, 3> fit_scurve(NDView<double, 1> x, NDView<double, 3> y,
int n_threads);
void fit_scurve(NDView<double, 1> x, NDView<double, 1> y,
NDView<double, 1> y_err, NDView<double, 1> par_out,
NDView<double, 1> par_err_out, double &chi2);
void fit_scurve(NDView<double, 1> x, NDView<double, 3> y,
NDView<double, 3> y_err, NDView<double, 3> par_out,
NDView<double, 3> par_err_out, NDView<double, 2> chi2_out,
int n_threads);
NDArray<double, 1> fit_scurve2(NDView<double, 1> x, NDView<double, 1> y);
NDArray<double, 3> fit_scurve2(NDView<double, 1> x, NDView<double, 3> y,
int n_threads);
void fit_scurve2(NDView<double, 1> x, NDView<double, 1> y,
NDView<double, 1> y_err, NDView<double, 1> par_out,
NDView<double, 1> par_err_out, double &chi2);
void fit_scurve2(NDView<double, 1> x, NDView<double, 3> y,
NDView<double, 3> y_err, NDView<double, 3> par_out,
NDView<double, 3> par_err_out, NDView<double, 2> chi2_out,
int n_threads);
} // namespace aare } // namespace aare

View File

@ -19,7 +19,7 @@ class Frame {
uint32_t m_cols; uint32_t m_cols;
Dtype m_dtype; Dtype m_dtype;
std::byte *m_data; std::byte *m_data;
//TODO! Add frame number? // TODO! Add frame number?
public: public:
/** /**
@ -39,7 +39,7 @@ class Frame {
* @param dtype data type of the pixels * @param dtype data type of the pixels
*/ */
Frame(const std::byte *bytes, uint32_t rows, uint32_t cols, Dtype dtype); Frame(const std::byte *bytes, uint32_t rows, uint32_t cols, Dtype dtype);
~Frame(){ delete[] m_data; }; ~Frame() { delete[] m_data; };
/** @warning Copy is disabled to ensure performance when passing /** @warning Copy is disabled to ensure performance when passing
* frames around. Can discuss enabling it. * frames around. Can discuss enabling it.
@ -52,7 +52,6 @@ class Frame {
Frame &operator=(Frame &&other) noexcept; Frame &operator=(Frame &&other) noexcept;
Frame(Frame &&other) noexcept; Frame(Frame &&other) noexcept;
Frame clone() const; //<- Explicit copy Frame clone() const; //<- Explicit copy
uint32_t rows() const; uint32_t rows() const;
@ -93,7 +92,7 @@ class Frame {
if (row >= m_rows || col >= m_cols) { if (row >= m_rows || col >= m_cols) {
throw std::out_of_range("Invalid row or column index"); throw std::out_of_range("Invalid row or column index");
} }
//TODO! add tests then reimplement using pixel_ptr // TODO! add tests then reimplement using pixel_ptr
T data; T data;
std::memcpy(&data, m_data + (row * m_cols + col) * m_dtype.bytes(), std::memcpy(&data, m_data + (row * m_cols + col) * m_dtype.bytes(),
m_dtype.bytes()); m_dtype.bytes());
@ -107,8 +106,8 @@ class Frame {
* @return NDView<T, 2> * @return NDView<T, 2>
*/ */
template <typename T> NDView<T, 2> view() { template <typename T> NDView<T, 2> view() {
std::array<int64_t, 2> shape = {static_cast<int64_t>(m_rows), std::array<ssize_t, 2> shape = {static_cast<ssize_t>(m_rows),
static_cast<int64_t>(m_cols)}; static_cast<ssize_t>(m_cols)};
T *data = reinterpret_cast<T *>(m_data); T *data = reinterpret_cast<T *>(m_data);
return NDView<T, 2>(data, shape); return NDView<T, 2>(data, shape);
} }

68
include/aare/GainMap.hpp Normal file
View File

@ -0,0 +1,68 @@
/************************************************
* @file GainMap.hpp
* @short function to apply gain map of image size to a vector of clusters -
*note stored gainmap is inverted for efficient aaplication to images
***********************************************/
#pragma once
#include "aare/Cluster.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDArray.hpp"
#include "aare/NDView.hpp"
#include <memory>
namespace aare {
class InvertedGainMap {
public:
explicit InvertedGainMap(const NDArray<double, 2> &gain_map)
: m_gain_map(gain_map) {
for (auto &item : m_gain_map) {
item = 1.0 / item;
}
};
explicit InvertedGainMap(const NDView<double, 2> gain_map) {
m_gain_map = NDArray<double, 2>(gain_map);
for (auto &item : m_gain_map) {
item = 1.0 / item;
}
}
template <typename ClusterType,
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
void apply_gain_map(ClusterVector<ClusterType> &clustervec) {
// in principle we need to know the size of the image for this lookup
size_t ClusterSizeX = clustervec.cluster_size_x();
size_t ClusterSizeY = clustervec.cluster_size_y();
using T = typename ClusterVector<ClusterType>::value_type;
int64_t index_cluster_center_x = ClusterSizeX / 2;
int64_t index_cluster_center_y = ClusterSizeY / 2;
for (size_t i = 0; i < clustervec.size(); i++) {
auto &cl = clustervec[i];
if (cl.x > 0 && cl.y > 0 && cl.x < m_gain_map.shape(1) - 1 &&
cl.y < m_gain_map.shape(0) - 1) {
for (size_t j = 0; j < ClusterSizeX * ClusterSizeY; j++) {
size_t x = cl.x + j % ClusterSizeX - index_cluster_center_x;
size_t y = cl.y + j / ClusterSizeX - index_cluster_center_y;
cl.data[j] = static_cast<T>(
static_cast<double>(cl.data[j]) *
m_gain_map(
y, x)); // cast after conversion to keep precision
}
} else {
// clear edge clusters
cl.data.fill(0);
}
}
}
private:
NDArray<double, 2> m_gain_map{};
};
} // end of namespace aare

View File

@ -1,29 +1,130 @@
#pragma once #pragma once
#include "aare/CalculateEta.hpp"
#include "aare/Cluster.hpp"
#include "aare/ClusterFile.hpp" //Cluster_3x3
#include "aare/ClusterVector.hpp"
#include "aare/NDArray.hpp" #include "aare/NDArray.hpp"
#include "aare/NDView.hpp" #include "aare/NDView.hpp"
#include "aare/ClusterVector.hpp" #include "aare/algorithm.hpp"
#include "aare/ClusterFile.hpp" //Cluster_3x3
namespace aare{
struct Photon{ namespace aare {
struct Photon {
double x; double x;
double y; double y;
double energy; double energy;
}; };
class Interpolator{ class Interpolator {
NDArray<double, 3> m_ietax; NDArray<double, 3> m_ietax;
NDArray<double, 3> m_ietay; NDArray<double, 3> m_ietay;
NDArray<double, 1> m_etabinsx; NDArray<double, 1> m_etabinsx;
NDArray<double, 1> m_etabinsy; NDArray<double, 1> m_etabinsy;
NDArray<double, 1> m_energy_bins; NDArray<double, 1> m_energy_bins;
public:
Interpolator(NDView<double, 3> etacube, NDView<double, 1> xbins, NDView<double, 1> ybins, NDView<double, 1> ebins);
NDArray<double, 3> get_ietax(){return m_ietax;}
NDArray<double, 3> get_ietay(){return m_ietay;}
std::vector<Photon> interpolate(const ClusterVector<int32_t>& clusters); public:
Interpolator(NDView<double, 3> etacube, NDView<double, 1> xbins,
NDView<double, 1> ybins, NDView<double, 1> ebins);
NDArray<double, 3> get_ietax() { return m_ietax; }
NDArray<double, 3> get_ietay() { return m_ietay; }
template <typename ClusterType,
typename Eanble = std::enable_if_t<is_cluster_v<ClusterType>>>
std::vector<Photon> interpolate(const ClusterVector<ClusterType> &clusters);
}; };
// TODO: generalize to support any clustertype!!! otherwise add std::enable_if_t
// to only take Cluster2x2 and Cluster3x3
template <typename ClusterType, typename Enable>
std::vector<Photon>
Interpolator::interpolate(const ClusterVector<ClusterType> &clusters) {
std::vector<Photon> photons;
photons.reserve(clusters.size());
if (clusters.cluster_size_x() == 3 || clusters.cluster_size_y() == 3) {
for (const ClusterType &cluster : clusters) {
auto eta = calculate_eta2(cluster);
Photon photon;
photon.x = cluster.x;
photon.y = cluster.y;
photon.energy = static_cast<decltype(photon.energy)>(eta.sum);
// auto ie = nearest_index(m_energy_bins, photon.energy)-1;
// auto ix = nearest_index(m_etabinsx, eta.x)-1;
// auto iy = nearest_index(m_etabinsy, eta.y)-1;
// Finding the index of the last element that is smaller
// should work fine as long as we have many bins
auto ie = last_smaller(m_energy_bins, photon.energy);
auto ix = last_smaller(m_etabinsx, eta.x);
auto iy = last_smaller(m_etabinsy, eta.y);
// fmt::print("ex: {}, ix: {}, iy: {}\n", ie, ix, iy);
double dX, dY;
// cBottomLeft = 0,
// cBottomRight = 1,
// cTopLeft = 2,
// cTopRight = 3
switch (static_cast<corner>(eta.c)) {
case corner::cTopLeft:
dX = -1.;
dY = 0;
break;
case corner::cTopRight:;
dX = 0;
dY = 0;
break;
case corner::cBottomLeft:
dX = -1.;
dY = -1.;
break;
case corner::cBottomRight:
dX = 0.;
dY = -1.;
break;
}
photon.x += m_ietax(ix, iy, ie) * 2 + dX;
photon.y += m_ietay(ix, iy, ie) * 2 + dY;
photons.push_back(photon);
}
} else if (clusters.cluster_size_x() == 2 ||
clusters.cluster_size_y() == 2) {
for (const ClusterType &cluster : clusters) {
auto eta = calculate_eta2(cluster);
Photon photon;
photon.x = cluster.x;
photon.y = cluster.y;
photon.energy = static_cast<decltype(photon.energy)>(eta.sum);
// Now do some actual interpolation.
// Find which energy bin the cluster is in
// auto ie = nearest_index(m_energy_bins, photon.energy)-1;
// auto ix = nearest_index(m_etabinsx, eta.x)-1;
// auto iy = nearest_index(m_etabinsy, eta.y)-1;
// Finding the index of the last element that is smaller
// should work fine as long as we have many bins
auto ie = last_smaller(m_energy_bins, photon.energy);
auto ix = last_smaller(m_etabinsx, eta.x);
auto iy = last_smaller(m_etabinsy, eta.y);
photon.x += m_ietax(ix, iy, ie) *
2; // eta goes between 0 and 1 but we could move the hit
// anywhere in the 2x2
photon.y += m_ietay(ix, iy, ie) * 2;
photons.push_back(photon);
}
} else {
throw std::runtime_error(
"Only 3x3 and 2x2 clusters are supported for interpolation");
}
return photons;
}
} // namespace aare } // namespace aare

View File

@ -3,59 +3,64 @@
#include <filesystem> #include <filesystem>
#include <vector> #include <vector>
#include "aare/FilePtr.hpp"
#include "aare/defs.hpp"
#include "aare/NDArray.hpp"
#include "aare/FileInterface.hpp" #include "aare/FileInterface.hpp"
#include "aare/FilePtr.hpp"
#include "aare/NDArray.hpp"
#include "aare/defs.hpp"
namespace aare { namespace aare {
struct JungfrauDataHeader {
struct JungfrauDataHeader{
uint64_t framenum; uint64_t framenum;
uint64_t bunchid; uint64_t bunchid;
}; };
class JungfrauDataFile : public FileInterface { class JungfrauDataFile : public FileInterface {
size_t m_rows{}; //!< number of rows in the image, from find_frame_size(); size_t m_rows{}; //!< number of rows in the image, from find_frame_size();
size_t m_cols{}; //!< number of columns in the image, from find_frame_size(); size_t
m_cols{}; //!< number of columns in the image, from find_frame_size();
size_t m_bytes_per_frame{}; //!< number of bytes per frame excluding header size_t m_bytes_per_frame{}; //!< number of bytes per frame excluding header
size_t m_total_frames{}; //!< total number of frames in the series of files size_t m_total_frames{}; //!< total number of frames in the series of files
size_t m_offset{}; //!< file index of the first file, allow starting at non zero file size_t m_offset{}; //!< file index of the first file, allow starting at non
size_t m_current_file_index{}; //!< The index of the open file //!< zero file
size_t m_current_frame_index{}; //!< The index of the current frame (with reference to all files) size_t m_current_file_index{}; //!< The index of the open file
size_t m_current_frame_index{}; //!< The index of the current frame (with
//!< reference to all files)
std::vector<size_t> m_last_frame_in_file{}; //!< Used for seeking to the correct file std::vector<size_t>
m_last_frame_in_file{}; //!< Used for seeking to the correct file
std::filesystem::path m_path; //!< path to the files std::filesystem::path m_path; //!< path to the files
std::string m_base_name; //!< base name used for formatting file names std::string m_base_name; //!< base name used for formatting file names
FilePtr m_fp; //!< RAII wrapper for a FILE* FilePtr m_fp; //!< RAII wrapper for a FILE*
using pixel_type = uint16_t; using pixel_type = uint16_t;
static constexpr size_t header_size = sizeof(JungfrauDataHeader); static constexpr size_t header_size = sizeof(JungfrauDataHeader);
static constexpr size_t n_digits_in_file_index = 6; //!< to format file names static constexpr size_t n_digits_in_file_index =
6; //!< to format file names
public: public:
JungfrauDataFile(const std::filesystem::path &fname); JungfrauDataFile(const std::filesystem::path &fname);
std::string base_name() const; //!< get the base name of the file (without path and extension) std::string base_name()
const; //!< get the base name of the file (without path and extension)
size_t bytes_per_frame() override; size_t bytes_per_frame() override;
size_t pixels_per_frame() override; size_t pixels_per_frame() override;
size_t bytes_per_pixel() const; size_t bytes_per_pixel() const;
size_t bitdepth() const override; size_t bitdepth() const override;
void seek(size_t frame_index) override; //!< seek to the given frame index (note not byte offset) void seek(size_t frame_index)
size_t tell() override; //!< get the frame index of the file pointer override; //!< seek to the given frame index (note not byte offset)
size_t tell() override; //!< get the frame index of the file pointer
size_t total_frames() const override; size_t total_frames() const override;
size_t rows() const override; size_t rows() const override;
size_t cols() const override; size_t cols() const override;
std::array<ssize_t,2> shape() const; std::array<ssize_t, 2> shape() const;
size_t n_files() const; //!< get the number of files in the series. size_t n_files() const; //!< get the number of files in the series.
// Extra functions needed for FileInterface // Extra functions needed for FileInterface
Frame read_frame() override; Frame read_frame() override;
Frame read_frame(size_t frame_number) override; Frame read_frame(size_t frame_number) override;
std::vector<Frame> read_n(size_t n_frames=0) override; std::vector<Frame> read_n(size_t n_frames = 0) override;
void read_into(std::byte *image_buf) override; void read_into(std::byte *image_buf) override;
void read_into(std::byte *image_buf, size_t n_frames) override; void read_into(std::byte *image_buf, size_t n_frames) override;
size_t frame_number(size_t frame_index) override; size_t frame_number(size_t frame_index) override;
@ -63,44 +68,48 @@ class JungfrauDataFile : public FileInterface {
/** /**
* @brief Read a single frame from the file into the given buffer. * @brief Read a single frame from the file into the given buffer.
* @param image_buf buffer to read the frame into. (Note the caller is responsible for allocating the buffer) * @param image_buf buffer to read the frame into. (Note the caller is
* responsible for allocating the buffer)
* @param header pointer to a JungfrauDataHeader or nullptr to skip header) * @param header pointer to a JungfrauDataHeader or nullptr to skip header)
*/ */
void read_into(std::byte *image_buf, JungfrauDataHeader *header = nullptr); void read_into(std::byte *image_buf, JungfrauDataHeader *header = nullptr);
/** /**
* @brief Read a multiple frames from the file into the given buffer. * @brief Read a multiple frames from the file into the given buffer.
* @param image_buf buffer to read the frame into. (Note the caller is responsible for allocating the buffer) * @param image_buf buffer to read the frame into. (Note the caller is
* responsible for allocating the buffer)
* @param n_frames number of frames to read * @param n_frames number of frames to read
* @param header pointer to a JungfrauDataHeader or nullptr to skip header) * @param header pointer to a JungfrauDataHeader or nullptr to skip header)
*/ */
void read_into(std::byte *image_buf, size_t n_frames, JungfrauDataHeader *header = nullptr); void read_into(std::byte *image_buf, size_t n_frames,
JungfrauDataHeader *header = nullptr);
/** /**
* @brief Read a single frame from the file into the given NDArray * @brief Read a single frame from the file into the given NDArray
* @param image NDArray to read the frame into. * @param image NDArray to read the frame into.
*/ */
void read_into(NDArray<uint16_t>* image, JungfrauDataHeader* header = nullptr); void read_into(NDArray<uint16_t> *image,
JungfrauDataHeader *header = nullptr);
JungfrauDataHeader read_header(); JungfrauDataHeader read_header();
std::filesystem::path current_file() const { return fpath(m_current_file_index+m_offset); } std::filesystem::path current_file() const {
return fpath(m_current_file_index + m_offset);
}
private:
private:
/** /**
* @brief Find the size of the frame in the file. (256x256, 256x1024, 512x1024) * @brief Find the size of the frame in the file. (256x256, 256x1024,
* 512x1024)
* @param fname path to the file * @param fname path to the file
* @throws std::runtime_error if the file is empty or the size cannot be determined * @throws std::runtime_error if the file is empty or the size cannot be
* determined
*/ */
void find_frame_size(const std::filesystem::path &fname); void find_frame_size(const std::filesystem::path &fname);
void parse_fname(const std::filesystem::path &fname);
void parse_fname(const std::filesystem::path &fname); void scan_files();
void scan_files(); void open_file(size_t file_index);
void open_file(size_t file_index); std::filesystem::path fpath(size_t frame_index) const;
std::filesystem::path fpath(size_t frame_index) const; };
};
} // namespace aare } // namespace aare

View File

@ -21,11 +21,10 @@ TODO! Add expression templates for operators
namespace aare { namespace aare {
template <typename T, ssize_t Ndim = 2>
template <typename T, int64_t Ndim = 2>
class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> { class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
std::array<int64_t, Ndim> shape_; std::array<ssize_t, Ndim> shape_;
std::array<int64_t, Ndim> strides_; std::array<ssize_t, Ndim> strides_;
size_t size_{}; size_t size_{};
T *data_; T *data_;
@ -34,7 +33,7 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
* @brief Default constructor. Will construct an empty NDArray. * @brief Default constructor. Will construct an empty NDArray.
* *
*/ */
NDArray() : shape_(), strides_(c_strides<Ndim>(shape_)), data_(nullptr) {}; NDArray() : shape_(), strides_(c_strides<Ndim>(shape_)), data_(nullptr){};
/** /**
* @brief Construct a new NDArray object with a given shape. * @brief Construct a new NDArray object with a given shape.
@ -42,20 +41,19 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
* *
* @param shape shape of the new NDArray * @param shape shape of the new NDArray
*/ */
explicit NDArray(std::array<int64_t, Ndim> shape) explicit NDArray(std::array<ssize_t, Ndim> shape)
: shape_(shape), strides_(c_strides<Ndim>(shape_)), : shape_(shape), strides_(c_strides<Ndim>(shape_)),
size_(std::accumulate(shape_.begin(), shape_.end(), 1, size_(std::accumulate(shape_.begin(), shape_.end(), 1,
std::multiplies<>())), std::multiplies<>())),
data_(new T[size_]) {} data_(new T[size_]) {}
/** /**
* @brief Construct a new NDArray object with a shape and value. * @brief Construct a new NDArray object with a shape and value.
* *
* @param shape shape of the new array * @param shape shape of the new array
* @param value value to initialize the array with * @param value value to initialize the array with
*/ */
NDArray(std::array<int64_t, Ndim> shape, T value) : NDArray(shape) { NDArray(std::array<ssize_t, Ndim> shape, T value) : NDArray(shape) {
this->operator=(value); this->operator=(value);
} }
@ -69,8 +67,8 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
std::copy(v.begin(), v.end(), begin()); std::copy(v.begin(), v.end(), begin());
} }
template<size_t Size> template <size_t Size>
NDArray(const std::array<T, Size>& arr) : NDArray<T,1>({Size}) { NDArray(const std::array<T, Size> &arr) : NDArray<T, 1>({Size}) {
std::copy(arr.begin(), arr.end(), begin()); std::copy(arr.begin(), arr.end(), begin());
} }
@ -79,7 +77,6 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)), : shape_(other.shape_), strides_(c_strides<Ndim>(shape_)),
size_(other.size_), data_(other.data_) { size_(other.size_), data_(other.data_) {
other.reset(); // TODO! is this necessary? other.reset(); // TODO! is this necessary?
} }
// Copy constructor // Copy constructor
@ -113,10 +110,10 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
NDArray &operator-=(const NDArray &other); NDArray &operator-=(const NDArray &other);
NDArray &operator*=(const NDArray &other); NDArray &operator*=(const NDArray &other);
//Write directly to the data array, or create a new one // Write directly to the data array, or create a new one
template<size_t Size> template <size_t Size>
NDArray<T,1>& operator=(const std::array<T,Size> &other){ NDArray<T, 1> &operator=(const std::array<T, Size> &other) {
if(Size != size_){ if (Size != size_) {
delete[] data_; delete[] data_;
size_ = Size; size_ = Size;
data_ = new T[size_]; data_ = new T[size_];
@ -157,11 +154,6 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
NDArray &operator&=(const T & /*mask*/); NDArray &operator&=(const T & /*mask*/);
void sqrt() { void sqrt() {
for (int i = 0; i < size_; ++i) { for (int i = 0; i < size_; ++i) {
data_[i] = std::sqrt(data_[i]); data_[i] = std::sqrt(data_[i]);
@ -186,22 +178,22 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
} }
// TODO! is int the right type for index? // TODO! is int the right type for index?
T &operator()(int64_t i) { return data_[i]; } T &operator()(ssize_t i) { return data_[i]; }
const T &operator()(int64_t i) const { return data_[i]; } const T &operator()(ssize_t i) const { return data_[i]; }
T &operator[](int64_t i) { return data_[i]; } T &operator[](ssize_t i) { return data_[i]; }
const T &operator[](int64_t i) const { return data_[i]; } const T &operator[](ssize_t i) const { return data_[i]; }
T *data() { return data_; } T *data() { return data_; }
std::byte *buffer() { return reinterpret_cast<std::byte *>(data_); } std::byte *buffer() { return reinterpret_cast<std::byte *>(data_); }
ssize_t size() const { return static_cast<ssize_t>(size_); } ssize_t size() const { return static_cast<ssize_t>(size_); }
size_t total_bytes() const { return size_ * sizeof(T); } size_t total_bytes() const { return size_ * sizeof(T); }
std::array<int64_t, Ndim> shape() const noexcept { return shape_; } std::array<ssize_t, Ndim> shape() const noexcept { return shape_; }
int64_t shape(int64_t i) const noexcept { return shape_[i]; } ssize_t shape(ssize_t i) const noexcept { return shape_[i]; }
std::array<int64_t, Ndim> strides() const noexcept { return strides_; } std::array<ssize_t, Ndim> strides() const noexcept { return strides_; }
size_t bitdepth() const noexcept { return sizeof(T) * 8; } size_t bitdepth() const noexcept { return sizeof(T) * 8; }
std::array<int64_t, Ndim> byte_strides() const noexcept { std::array<ssize_t, Ndim> byte_strides() const noexcept {
auto byte_strides = strides_; auto byte_strides = strides_;
for (auto &val : byte_strides) for (auto &val : byte_strides)
val *= sizeof(T); val *= sizeof(T);
@ -228,7 +220,7 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
}; };
// Move assign // Move assign
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> & NDArray<T, Ndim> &
NDArray<T, Ndim>::operator=(NDArray<T, Ndim> &&other) noexcept { NDArray<T, Ndim>::operator=(NDArray<T, Ndim> &&other) noexcept {
if (this != &other) { if (this != &other) {
@ -242,7 +234,7 @@ NDArray<T, Ndim>::operator=(NDArray<T, Ndim> &&other) noexcept {
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) {
// check shape // check shape
if (shape_ == other.shape_) { if (shape_ == other.shape_) {
@ -254,7 +246,7 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) {
throw(std::runtime_error("Shape of ImageDatas must match")); throw(std::runtime_error("Shape of ImageDatas must match"));
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) {
// check shape // check shape
if (shape_ == other.shape_) { if (shape_ == other.shape_) {
@ -266,7 +258,7 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) {
throw(std::runtime_error("Shape of ImageDatas must match")); throw(std::runtime_error("Shape of ImageDatas must match"));
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) {
// check shape // check shape
if (shape_ == other.shape_) { if (shape_ == other.shape_) {
@ -278,14 +270,14 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) {
throw(std::runtime_error("Shape of ImageDatas must match")); throw(std::runtime_error("Shape of ImageDatas must match"));
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator&=(const T &mask) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator&=(const T &mask) {
for (auto it = begin(); it != end(); ++it) for (auto it = begin(); it != end(); ++it)
*it &= mask; *it &= mask;
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) { NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) {
if (shape_ == other.shape_) { if (shape_ == other.shape_) {
NDArray<bool, Ndim> result{shape_}; NDArray<bool, Ndim> result{shape_};
@ -297,7 +289,7 @@ NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) {
throw(std::runtime_error("Shape of ImageDatas must match")); throw(std::runtime_error("Shape of ImageDatas must match"));
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const NDArray<T, Ndim> &other) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const NDArray<T, Ndim> &other) {
if (this != &other) { if (this != &other) {
delete[] data_; delete[] data_;
@ -310,7 +302,7 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const NDArray<T, Ndim> &other) {
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
bool NDArray<T, Ndim>::operator==(const NDArray<T, Ndim> &other) const { bool NDArray<T, Ndim>::operator==(const NDArray<T, Ndim> &other) const {
if (shape_ != other.shape_) if (shape_ != other.shape_)
return false; return false;
@ -322,83 +314,80 @@ bool NDArray<T, Ndim>::operator==(const NDArray<T, Ndim> &other) const {
return true; return true;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
bool NDArray<T, Ndim>::operator!=(const NDArray<T, Ndim> &other) const { bool NDArray<T, Ndim>::operator!=(const NDArray<T, Ndim> &other) const {
return !((*this) == other); return !((*this) == other);
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator++() { NDArray<T, Ndim> &NDArray<T, Ndim>::operator++() {
for (uint32_t i = 0; i < size_; ++i) for (uint32_t i = 0; i < size_; ++i)
data_[i] += 1; data_[i] += 1;
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const T &value) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const T &value) {
std::fill_n(data_, size_, value); std::fill_n(data_, size_, value);
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const T &value) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const T &value) {
for (uint32_t i = 0; i < size_; ++i) for (uint32_t i = 0; i < size_; ++i)
data_[i] += value; data_[i] += value;
return *this; return *this;
} }
template <typename T, ssize_t Ndim>
template <typename T, int64_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator+(const T &value) { NDArray<T, Ndim> NDArray<T, Ndim>::operator+(const T &value) {
NDArray result = *this; NDArray result = *this;
result += value; result += value;
return result; return result;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const T &value) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const T &value) {
for (uint32_t i = 0; i < size_; ++i) for (uint32_t i = 0; i < size_; ++i)
data_[i] -= value; data_[i] -= value;
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator-(const T &value) { NDArray<T, Ndim> NDArray<T, Ndim>::operator-(const T &value) {
NDArray result = *this; NDArray result = *this;
result -= value; result -= value;
return result; return result;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator/=(const T &value) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator/=(const T &value) {
for (uint32_t i = 0; i < size_; ++i) for (uint32_t i = 0; i < size_; ++i)
data_[i] /= value; data_[i] /= value;
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator/(const T &value) { NDArray<T, Ndim> NDArray<T, Ndim>::operator/(const T &value) {
NDArray result = *this; NDArray result = *this;
result /= value; result /= value;
return result; return result;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const T &value) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const T &value) {
for (uint32_t i = 0; i < size_; ++i) for (uint32_t i = 0; i < size_; ++i)
data_[i] *= value; data_[i] *= value;
return *this; return *this;
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator*(const T &value) { NDArray<T, Ndim> NDArray<T, Ndim>::operator*(const T &value) {
NDArray result = *this; NDArray result = *this;
result *= value; result *= value;
return result; return result;
} }
// template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print() { // template <typename T, ssize_t Ndim> void NDArray<T, Ndim>::Print() {
// if (shape_[0] < 20 && shape_[1] < 20) // if (shape_[0] < 20 && shape_[1] < 20)
// Print_all(); // Print_all();
// else // else
// Print_some(); // Print_some();
// } // }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
std::ostream &operator<<(std::ostream &os, const NDArray<T, Ndim> &arr) { std::ostream &operator<<(std::ostream &os, const NDArray<T, Ndim> &arr) {
for (auto row = 0; row < arr.shape(0); ++row) { for (auto row = 0; row < arr.shape(0); ++row) {
for (auto col = 0; col < arr.shape(1); ++col) { for (auto col = 0; col < arr.shape(1); ++col) {
@ -410,7 +399,7 @@ std::ostream &operator<<(std::ostream &os, const NDArray<T, Ndim> &arr) {
return os; return os;
} }
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_all() { template <typename T, ssize_t Ndim> void NDArray<T, Ndim>::Print_all() {
for (auto row = 0; row < shape_[0]; ++row) { for (auto row = 0; row < shape_[0]; ++row) {
for (auto col = 0; col < shape_[1]; ++col) { for (auto col = 0; col < shape_[1]; ++col) {
std::cout << std::setw(3); std::cout << std::setw(3);
@ -419,7 +408,7 @@ template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_all() {
std::cout << "\n"; std::cout << "\n";
} }
} }
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_some() { template <typename T, ssize_t Ndim> void NDArray<T, Ndim>::Print_some() {
for (auto row = 0; row < 5; ++row) { for (auto row = 0; row < 5; ++row) {
for (auto col = 0; col < 5; ++col) { for (auto col = 0; col < 5; ++col) {
std::cout << std::setw(7); std::cout << std::setw(7);
@ -429,7 +418,7 @@ template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_some() {
} }
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
void save(NDArray<T, Ndim> &img, std::string &pathname) { void save(NDArray<T, Ndim> &img, std::string &pathname) {
std::ofstream f; std::ofstream f;
f.open(pathname, std::ios::binary); f.open(pathname, std::ios::binary);
@ -437,9 +426,9 @@ void save(NDArray<T, Ndim> &img, std::string &pathname) {
f.close(); f.close();
} }
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
NDArray<T, Ndim> load(const std::string &pathname, NDArray<T, Ndim> load(const std::string &pathname,
std::array<int64_t, Ndim> shape) { std::array<ssize_t, Ndim> shape) {
NDArray<T, Ndim> img{shape}; NDArray<T, Ndim> img{shape};
std::ifstream f; std::ifstream f;
f.open(pathname, std::ios::binary); f.open(pathname, std::ios::binary);
@ -448,6 +437,4 @@ NDArray<T, Ndim> load(const std::string &pathname,
return img; return img;
} }
} // namespace aare } // namespace aare

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "aare/defs.hpp"
#include "aare/ArrayExpr.hpp" #include "aare/ArrayExpr.hpp"
#include "aare/defs.hpp"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@ -14,10 +14,11 @@
#include <vector> #include <vector>
namespace aare { namespace aare {
template <int64_t Ndim> using Shape = std::array<int64_t, Ndim>; template <ssize_t Ndim> using Shape = std::array<ssize_t, Ndim>;
// TODO! fix mismatch between signed and unsigned // TODO! fix mismatch between signed and unsigned
template <int64_t Ndim> Shape<Ndim> make_shape(const std::vector<size_t> &shape) { template <ssize_t Ndim>
Shape<Ndim> make_shape(const std::vector<size_t> &shape) {
if (shape.size() != Ndim) if (shape.size() != Ndim)
throw std::runtime_error("Shape size mismatch"); throw std::runtime_error("Shape size mismatch");
Shape<Ndim> arr; Shape<Ndim> arr;
@ -25,62 +26,74 @@ template <int64_t Ndim> Shape<Ndim> make_shape(const std::vector<size_t> &shape)
return arr; return arr;
} }
template <int64_t Dim = 0, typename Strides> int64_t element_offset(const Strides & /*unused*/) { return 0; } template <ssize_t Dim = 0, typename Strides>
ssize_t element_offset(const Strides & /*unused*/) {
return 0;
}
template <int64_t Dim = 0, typename Strides, typename... Ix> template <ssize_t Dim = 0, typename Strides, typename... Ix>
int64_t element_offset(const Strides &strides, int64_t i, Ix... index) { ssize_t element_offset(const Strides &strides, ssize_t i, Ix... index) {
return i * strides[Dim] + element_offset<Dim + 1>(strides, index...); return i * strides[Dim] + element_offset<Dim + 1>(strides, index...);
} }
template <int64_t Ndim> std::array<int64_t, Ndim> c_strides(const std::array<int64_t, Ndim> &shape) { template <ssize_t Ndim>
std::array<int64_t, Ndim> strides{}; std::array<ssize_t, Ndim> c_strides(const std::array<ssize_t, Ndim> &shape) {
std::array<ssize_t, Ndim> strides{};
std::fill(strides.begin(), strides.end(), 1); std::fill(strides.begin(), strides.end(), 1);
for (int64_t i = Ndim - 1; i > 0; --i) { for (ssize_t i = Ndim - 1; i > 0; --i) {
strides[i - 1] = strides[i] * shape[i]; strides[i - 1] = strides[i] * shape[i];
} }
return strides; return strides;
} }
template <int64_t Ndim> std::array<int64_t, Ndim> make_array(const std::vector<int64_t> &vec) { template <ssize_t Ndim>
std::array<ssize_t, Ndim> make_array(const std::vector<ssize_t> &vec) {
assert(vec.size() == Ndim); assert(vec.size() == Ndim);
std::array<int64_t, Ndim> arr{}; std::array<ssize_t, Ndim> arr{};
std::copy_n(vec.begin(), Ndim, arr.begin()); std::copy_n(vec.begin(), Ndim, arr.begin());
return arr; return arr;
} }
template <typename T, int64_t Ndim = 2> class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> { template <typename T, ssize_t Ndim = 2>
class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
public: public:
NDView() = default; NDView() = default;
~NDView() = default; ~NDView() = default;
NDView(const NDView &) = default; NDView(const NDView &) = default;
NDView(NDView &&) = default; NDView(NDView &&) = default;
NDView(T *buffer, std::array<int64_t, Ndim> shape) NDView(T *buffer, std::array<ssize_t, Ndim> shape)
: buffer_(buffer), strides_(c_strides<Ndim>(shape)), shape_(shape), : buffer_(buffer), strides_(c_strides<Ndim>(shape)), shape_(shape),
size_(std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>())) {} size_(std::accumulate(std::begin(shape), std::end(shape), 1,
std::multiplies<>())) {}
// NDView(T *buffer, const std::vector<int64_t> &shape) // NDView(T *buffer, const std::vector<ssize_t> &shape)
// : buffer_(buffer), strides_(c_strides<Ndim>(make_array<Ndim>(shape))), shape_(make_array<Ndim>(shape)), // : buffer_(buffer),
// size_(std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>())) {} // strides_(c_strides<Ndim>(make_array<Ndim>(shape))),
// shape_(make_array<Ndim>(shape)),
// size_(std::accumulate(std::begin(shape), std::end(shape), 1,
// std::multiplies<>())) {}
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) { template <typename... Ix>
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
return buffer_[element_offset(strides_, index...)]; return buffer_[element_offset(strides_, index...)];
} }
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const { template <typename... Ix>
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
return buffer_[element_offset(strides_, index...)]; return buffer_[element_offset(strides_, index...)];
} }
ssize_t size() const { return static_cast<ssize_t>(size_); } ssize_t size() const { return static_cast<ssize_t>(size_); }
size_t total_bytes() const { return size_ * sizeof(T); } size_t total_bytes() const { return size_ * sizeof(T); }
std::array<int64_t, Ndim> strides() const noexcept { return strides_; } std::array<ssize_t, Ndim> strides() const noexcept { return strides_; }
T *begin() { return buffer_; } T *begin() { return buffer_; }
T *end() { return buffer_ + size_; } T *end() { return buffer_ + size_; }
T const *begin() const { return buffer_; } T const *begin() const { return buffer_; }
T const *end() const { return buffer_ + size_; } T const *end() const { return buffer_ + size_; }
T &operator()(int64_t i) const { return buffer_[i]; } T &operator()(ssize_t i) const { return buffer_[i]; }
T &operator[](int64_t i) const { return buffer_[i]; } T &operator[](ssize_t i) const { return buffer_[i]; }
bool operator==(const NDView &other) const { bool operator==(const NDView &other) const {
if (size_ != other.size_) if (size_ != other.size_)
@ -94,16 +107,21 @@ template <typename T, int64_t Ndim = 2> class NDView : public ArrayExpr<NDView<T
NDView &operator+=(const T val) { return elemenwise(val, std::plus<T>()); } NDView &operator+=(const T val) { return elemenwise(val, std::plus<T>()); }
NDView &operator-=(const T val) { return elemenwise(val, std::minus<T>()); } NDView &operator-=(const T val) { return elemenwise(val, std::minus<T>()); }
NDView &operator*=(const T val) { return elemenwise(val, std::multiplies<T>()); } NDView &operator*=(const T val) {
NDView &operator/=(const T val) { return elemenwise(val, std::divides<T>()); } return elemenwise(val, std::multiplies<T>());
}
NDView &operator/=(const T val) {
return elemenwise(val, std::divides<T>());
}
NDView &operator/=(const NDView &other) { return elemenwise(other, std::divides<T>()); } NDView &operator/=(const NDView &other) {
return elemenwise(other, std::divides<T>());
}
template <size_t Size> NDView &operator=(const std::array<T, Size> &arr) {
template<size_t Size> if (size() != static_cast<ssize_t>(arr.size()))
NDView& operator=(const std::array<T, Size> &arr) { throw std::runtime_error(LOCATION +
if(size() != static_cast<ssize_t>(arr.size())) "Array and NDView size mismatch");
throw std::runtime_error(LOCATION + "Array and NDView size mismatch");
std::copy(arr.begin(), arr.end(), begin()); std::copy(arr.begin(), arr.end(), begin());
return *this; return *this;
} }
@ -136,31 +154,33 @@ template <typename T, int64_t Ndim = 2> class NDView : public ArrayExpr<NDView<T
} }
auto &shape() const { return shape_; } auto &shape() const { return shape_; }
auto shape(int64_t i) const { return shape_[i]; } auto shape(ssize_t i) const { return shape_[i]; }
T *data() { return buffer_; } T *data() { return buffer_; }
void print_all() const; void print_all() const;
private: private:
T *buffer_{nullptr}; T *buffer_{nullptr};
std::array<int64_t, Ndim> strides_{}; std::array<ssize_t, Ndim> strides_{};
std::array<int64_t, Ndim> shape_{}; std::array<ssize_t, Ndim> shape_{};
uint64_t size_{}; uint64_t size_{};
template <class BinaryOperation> NDView &elemenwise(T val, BinaryOperation op) { template <class BinaryOperation>
NDView &elemenwise(T val, BinaryOperation op) {
for (uint64_t i = 0; i != size_; ++i) { for (uint64_t i = 0; i != size_; ++i) {
buffer_[i] = op(buffer_[i], val); buffer_[i] = op(buffer_[i], val);
} }
return *this; return *this;
} }
template <class BinaryOperation> NDView &elemenwise(const NDView &other, BinaryOperation op) { template <class BinaryOperation>
NDView &elemenwise(const NDView &other, BinaryOperation op) {
for (uint64_t i = 0; i != size_; ++i) { for (uint64_t i = 0; i != size_; ++i) {
buffer_[i] = op(buffer_[i], other.buffer_[i]); buffer_[i] = op(buffer_[i], other.buffer_[i]);
} }
return *this; return *this;
} }
}; };
template <typename T, int64_t Ndim> void NDView<T, Ndim>::print_all() const { template <typename T, ssize_t Ndim> void NDView<T, Ndim>::print_all() const {
for (auto row = 0; row < shape_[0]; ++row) { for (auto row = 0; row < shape_[0]; ++row) {
for (auto col = 0; col < shape_[1]; ++col) { for (auto col = 0; col < shape_[1]; ++col) {
std::cout << std::setw(3); std::cout << std::setw(3);
@ -170,9 +190,8 @@ template <typename T, int64_t Ndim> void NDView<T, Ndim>::print_all() const {
} }
} }
template <typename T, ssize_t Ndim>
template <typename T, int64_t Ndim> std::ostream &operator<<(std::ostream &os, const NDView<T, Ndim> &arr) {
std::ostream& operator <<(std::ostream& os, const NDView<T, Ndim>& arr){
for (auto row = 0; row < arr.shape(0); ++row) { for (auto row = 0; row < arr.shape(0); ++row) {
for (auto col = 0; col < arr.shape(1); ++col) { for (auto col = 0; col < arr.shape(1); ++col) {
os << std::setw(3); os << std::setw(3);
@ -183,10 +202,8 @@ std::ostream& operator <<(std::ostream& os, const NDView<T, Ndim>& arr){
return os; return os;
} }
template <typename T> NDView<T, 1> make_view(std::vector<T> &vec) {
template <typename T> return NDView<T, 1>(vec.data(), {static_cast<ssize_t>(vec.size())});
NDView<T,1> make_view(std::vector<T>& vec){
return NDView<T,1>(vec.data(), {static_cast<int64_t>(vec.size())});
} }
} // namespace aare } // namespace aare

View File

@ -1,9 +1,8 @@
#pragma once #pragma once
#include "aare/Dtype.hpp" #include "aare/Dtype.hpp"
#include "aare/defs.hpp"
#include "aare/FileInterface.hpp" #include "aare/FileInterface.hpp"
#include "aare/NumpyHelpers.hpp" #include "aare/NumpyHelpers.hpp"
#include "aare/defs.hpp"
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
@ -11,13 +10,12 @@
namespace aare { namespace aare {
/** /**
* @brief NumpyFile class to read and write numpy files * @brief NumpyFile class to read and write numpy files
* @note derived from FileInterface * @note derived from FileInterface
* @note implements all the pure virtual functions from FileInterface * @note implements all the pure virtual functions from FileInterface
* @note documentation for the functions can also be found in the FileInterface class * @note documentation for the functions can also be found in the FileInterface
* class
*/ */
class NumpyFile : public FileInterface { class NumpyFile : public FileInterface {
@ -28,26 +26,35 @@ class NumpyFile : public FileInterface {
* @param mode file mode (r, w) * @param mode file mode (r, w)
* @param cfg file configuration * @param cfg file configuration
*/ */
explicit NumpyFile(const std::filesystem::path &fname, const std::string &mode = "r", FileConfig cfg = {}); explicit NumpyFile(const std::filesystem::path &fname,
const std::string &mode = "r", FileConfig cfg = {});
void write(Frame &frame); void write(Frame &frame);
Frame read_frame() override { return get_frame(this->current_frame++); } Frame read_frame() override { return get_frame(this->current_frame++); }
Frame read_frame(size_t frame_number) override { return get_frame(frame_number); } Frame read_frame(size_t frame_number) override {
return get_frame(frame_number);
}
std::vector<Frame> read_n(size_t n_frames) override; std::vector<Frame> read_n(size_t n_frames) override;
void read_into(std::byte *image_buf) override { return get_frame_into(this->current_frame++, image_buf); } void read_into(std::byte *image_buf) override {
return get_frame_into(this->current_frame++, image_buf);
}
void read_into(std::byte *image_buf, size_t n_frames) override; void read_into(std::byte *image_buf, size_t n_frames) override;
size_t frame_number(size_t frame_index) override { return frame_index; }; size_t frame_number(size_t frame_index) override { return frame_index; };
size_t bytes_per_frame() override; size_t bytes_per_frame() override;
size_t pixels_per_frame() override; size_t pixels_per_frame() override;
void seek(size_t frame_number) override { this->current_frame = frame_number; } void seek(size_t frame_number) override {
this->current_frame = frame_number;
}
size_t tell() override { return this->current_frame; } size_t tell() override { return this->current_frame; }
size_t total_frames() const override { return m_header.shape[0]; } size_t total_frames() const override { return m_header.shape[0]; }
size_t rows() const override { return m_header.shape[1]; } size_t rows() const override { return m_header.shape[1]; }
size_t cols() const override { return m_header.shape[2]; } size_t cols() const override { return m_header.shape[2]; }
size_t bitdepth() const override { return m_header.dtype.bitdepth(); } size_t bitdepth() const override { return m_header.dtype.bitdepth(); }
DetectorType detector_type() const override { return DetectorType::Unknown; } DetectorType detector_type() const override {
return DetectorType::Unknown;
}
/** /**
* @brief get the data type of the numpy file * @brief get the data type of the numpy file
@ -69,8 +76,9 @@ class NumpyFile : public FileInterface {
*/ */
template <typename T, size_t NDim> NDArray<T, NDim> load() { template <typename T, size_t NDim> NDArray<T, NDim> load() {
NDArray<T, NDim> arr(make_shape<NDim>(m_header.shape)); NDArray<T, NDim> arr(make_shape<NDim>(m_header.shape));
if (fseek(fp, static_cast<int64_t>(header_size), SEEK_SET)) { if (fseek(fp, static_cast<long>(header_size), SEEK_SET)) {
throw std::runtime_error(LOCATION + "Error seeking to the start of the data"); throw std::runtime_error(LOCATION +
"Error seeking to the start of the data");
} }
size_t rc = fread(arr.data(), sizeof(T), arr.size(), fp); size_t rc = fread(arr.data(), sizeof(T), arr.size(), fp);
if (rc != static_cast<size_t>(arr.size())) { if (rc != static_cast<size_t>(arr.size())) {
@ -78,16 +86,20 @@ class NumpyFile : public FileInterface {
} }
return arr; return arr;
} }
template <typename A, typename TYPENAME, A Ndim> void write(NDView<TYPENAME, Ndim> &frame) { template <typename A, typename TYPENAME, A Ndim>
void write(NDView<TYPENAME, Ndim> &frame) {
write_impl(frame.data(), frame.total_bytes()); write_impl(frame.data(), frame.total_bytes());
} }
template <typename A, typename TYPENAME, A Ndim> void write(NDArray<TYPENAME, Ndim> &frame) { template <typename A, typename TYPENAME, A Ndim>
void write(NDArray<TYPENAME, Ndim> &frame) {
write_impl(frame.data(), frame.total_bytes()); write_impl(frame.data(), frame.total_bytes());
} }
template <typename A, typename TYPENAME, A Ndim> void write(NDView<TYPENAME, Ndim> &&frame) { template <typename A, typename TYPENAME, A Ndim>
void write(NDView<TYPENAME, Ndim> &&frame) {
write_impl(frame.data(), frame.total_bytes()); write_impl(frame.data(), frame.total_bytes());
} }
template <typename A, typename TYPENAME, A Ndim> void write(NDArray<TYPENAME, Ndim> &&frame) { template <typename A, typename TYPENAME, A Ndim>
void write(NDArray<TYPENAME, Ndim> &&frame) {
write_impl(frame.data(), frame.total_bytes()); write_impl(frame.data(), frame.total_bytes());
} }

View File

@ -40,15 +40,18 @@ bool parse_bool(const std::string &in);
std::string get_value_from_map(const std::string &mapstr); std::string get_value_from_map(const std::string &mapstr);
std::unordered_map<std::string, std::string> parse_dict(std::string in, const std::vector<std::string> &keys); std::unordered_map<std::string, std::string>
parse_dict(std::string in, const std::vector<std::string> &keys);
template <typename T, size_t N> bool in_array(T val, const std::array<T, N> &arr) { template <typename T, size_t N>
bool in_array(T val, const std::array<T, N> &arr) {
return std::find(std::begin(arr), std::end(arr), val) != std::end(arr); return std::find(std::begin(arr), std::end(arr), val) != std::end(arr);
} }
bool is_digits(const std::string &str); bool is_digits(const std::string &str);
aare::Dtype parse_descr(std::string typestring); aare::Dtype parse_descr(std::string typestring);
size_t write_header(const std::filesystem::path &fname, const NumpyHeader &header); size_t write_header(const std::filesystem::path &fname,
const NumpyHeader &header);
size_t write_header(std::ostream &out, const NumpyHeader &header); size_t write_header(std::ostream &out, const NumpyHeader &header);
} // namespace NumpyHelpers } // namespace NumpyHelpers

View File

@ -19,13 +19,13 @@ template <typename SUM_TYPE = double> class Pedestal {
uint32_t m_samples; uint32_t m_samples;
NDArray<uint32_t, 2> m_cur_samples; NDArray<uint32_t, 2> m_cur_samples;
//TODO! in case of int needs to be changed to uint64_t // TODO! in case of int needs to be changed to uint64_t
NDArray<SUM_TYPE, 2> m_sum; NDArray<SUM_TYPE, 2> m_sum;
NDArray<SUM_TYPE, 2> m_sum2; NDArray<SUM_TYPE, 2> m_sum2;
//Cache mean since it is used over and over in the ClusterFinder // Cache mean since it is used over and over in the ClusterFinder
//This optimization is related to the access pattern of the ClusterFinder // This optimization is related to the access pattern of the ClusterFinder
//Relies on having more reads than pushes to the pedestal // Relies on having more reads than pushes to the pedestal
NDArray<SUM_TYPE, 2> m_mean; NDArray<SUM_TYPE, 2> m_mean;
public: public:
@ -42,9 +42,7 @@ template <typename SUM_TYPE = double> class Pedestal {
} }
~Pedestal() = default; ~Pedestal() = default;
NDArray<SUM_TYPE, 2> mean() { NDArray<SUM_TYPE, 2> mean() { return m_mean; }
return m_mean;
}
SUM_TYPE mean(const uint32_t row, const uint32_t col) const { SUM_TYPE mean(const uint32_t row, const uint32_t col) const {
return m_mean(row, col); return m_mean(row, col);
@ -71,8 +69,6 @@ template <typename SUM_TYPE = double> class Pedestal {
return variance_array; return variance_array;
} }
NDArray<SUM_TYPE, 2> std() { NDArray<SUM_TYPE, 2> std() {
NDArray<SUM_TYPE, 2> standard_deviation_array({m_rows, m_cols}); NDArray<SUM_TYPE, 2> standard_deviation_array({m_rows, m_cols});
for (uint32_t i = 0; i < m_rows * m_cols; i++) { for (uint32_t i = 0; i < m_rows * m_cols; i++) {
@ -83,8 +79,6 @@ template <typename SUM_TYPE = double> class Pedestal {
return standard_deviation_array; return standard_deviation_array;
} }
void clear() { void clear() {
m_sum = 0; m_sum = 0;
m_sum2 = 0; m_sum2 = 0;
@ -92,8 +86,6 @@ template <typename SUM_TYPE = double> class Pedestal {
m_mean = 0; m_mean = 0;
} }
void clear(const uint32_t row, const uint32_t col) { void clear(const uint32_t row, const uint32_t col) {
m_sum(row, col) = 0; m_sum(row, col) = 0;
m_sum2(row, col) = 0; m_sum2(row, col) = 0;
@ -101,13 +93,11 @@ template <typename SUM_TYPE = double> class Pedestal {
m_mean(row, col) = 0; m_mean(row, col) = 0;
} }
template <typename T> void push(NDView<T, 2> frame) { template <typename T> void push(NDView<T, 2> frame) {
assert(frame.size() == m_rows * m_cols); assert(frame.size() == m_rows * m_cols);
// TODO! move away from m_rows, m_cols // TODO! move away from m_rows, m_cols
if (frame.shape() != std::array<int64_t, 2>{m_rows, m_cols}) { if (frame.shape() != std::array<ssize_t, 2>{m_rows, m_cols}) {
throw std::runtime_error( throw std::runtime_error(
"Frame shape does not match pedestal shape"); "Frame shape does not match pedestal shape");
} }
@ -128,7 +118,7 @@ template <typename SUM_TYPE = double> class Pedestal {
assert(frame.size() == m_rows * m_cols); assert(frame.size() == m_rows * m_cols);
// TODO! move away from m_rows, m_cols // TODO! move away from m_rows, m_cols
if (frame.shape() != std::array<int64_t, 2>{m_rows, m_cols}) { if (frame.shape() != std::array<ssize_t, 2>{m_rows, m_cols}) {
throw std::runtime_error( throw std::runtime_error(
"Frame shape does not match pedestal shape"); "Frame shape does not match pedestal shape");
} }
@ -140,9 +130,6 @@ template <typename SUM_TYPE = double> class Pedestal {
} }
} }
template <typename T> void push(Frame &frame) { template <typename T> void push(Frame &frame) {
assert(frame.rows() == static_cast<size_t>(m_rows) && assert(frame.rows() == static_cast<size_t>(m_rows) &&
frame.cols() == static_cast<size_t>(m_cols)); frame.cols() == static_cast<size_t>(m_cols));
@ -170,7 +157,8 @@ template <typename SUM_TYPE = double> class Pedestal {
m_sum(row, col) += val - m_sum(row, col) / m_samples; m_sum(row, col) += val - m_sum(row, col) / m_samples;
m_sum2(row, col) += val * val - m_sum2(row, col) / m_samples; m_sum2(row, col) += val * val - m_sum2(row, col) / m_samples;
} }
//Since we just did a push we know that m_cur_samples(row, col) is at least 1 // Since we just did a push we know that m_cur_samples(row, col) is at
// least 1
m_mean(row, col) = m_sum(row, col) / m_cur_samples(row, col); m_mean(row, col) = m_sum(row, col) / m_cur_samples(row, col);
} }
@ -183,7 +171,8 @@ template <typename SUM_TYPE = double> class Pedestal {
m_cur_samples(row, col)++; m_cur_samples(row, col)++;
} else { } else {
m_sum(row, col) += val - m_sum(row, col) / m_cur_samples(row, col); m_sum(row, col) += val - m_sum(row, col) / m_cur_samples(row, col);
m_sum2(row, col) += val * val - m_sum2(row, col) / m_cur_samples(row, col); m_sum2(row, col) +=
val * val - m_sum2(row, col) / m_cur_samples(row, col);
} }
} }
@ -191,19 +180,16 @@ template <typename SUM_TYPE = double> class Pedestal {
* @brief Update the mean of the pedestal. This is used after having done * @brief Update the mean of the pedestal. This is used after having done
* push_no_update. It is not necessary to call this function after push. * push_no_update. It is not necessary to call this function after push.
*/ */
void update_mean(){ void update_mean() { m_mean = m_sum / m_cur_samples; }
m_mean = m_sum / m_cur_samples;
}
template<typename T> template <typename T>
void push_fast(const uint32_t row, const uint32_t col, const T val_){ void push_fast(const uint32_t row, const uint32_t col, const T val_) {
//Assume we reached the steady state where all pixels have // Assume we reached the steady state where all pixels have
//m_samples samples // m_samples samples
SUM_TYPE val = static_cast<SUM_TYPE>(val_); SUM_TYPE val = static_cast<SUM_TYPE>(val_);
m_sum(row, col) += val - m_sum(row, col) / m_samples; m_sum(row, col) += val - m_sum(row, col) / m_samples;
m_sum2(row, col) += val * val - m_sum2(row, col) / m_samples; m_sum2(row, col) += val * val - m_sum2(row, col) / m_samples;
m_mean(row, col) = m_sum(row, col) / m_samples; m_mean(row, col) = m_sum(row, col) / m_samples;
} }
}; };
} // namespace aare } // namespace aare

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "aare/defs.hpp"
#include "aare/NDArray.hpp" #include "aare/NDArray.hpp"
#include "aare/defs.hpp"
namespace aare { namespace aare {
@ -10,11 +10,11 @@ NDArray<ssize_t, 2> GenerateMoench05PixelMap();
NDArray<ssize_t, 2> GenerateMoench05PixelMap1g(); NDArray<ssize_t, 2> GenerateMoench05PixelMap1g();
NDArray<ssize_t, 2> GenerateMoench05PixelMapOld(); NDArray<ssize_t, 2> GenerateMoench05PixelMapOld();
//Matterhorn02 // Matterhorn02
NDArray<ssize_t, 2>GenerateMH02SingleCounterPixelMap(); NDArray<ssize_t, 2> GenerateMH02SingleCounterPixelMap();
NDArray<ssize_t, 3> GenerateMH02FourCounterPixelMap(); NDArray<ssize_t, 3> GenerateMH02FourCounterPixelMap();
//Eiger // Eiger
NDArray<ssize_t, 2>GenerateEigerFlipRowsPixelMap(); NDArray<ssize_t, 2> GenerateEigerFlipRowsPixelMap();
} // namespace aare } // namespace aare

View File

@ -18,9 +18,9 @@
// @author Jordan DeLong (delong.j@fb.com) // @author Jordan DeLong (delong.j@fb.com)
// Changes made by PSD Detector Group: // Changes made by PSD Detector Group:
// Copied: Line 34 constexpr std::size_t hardware_destructive_interference_size = 128; from folly/lang/Align.h // Copied: Line 34 constexpr std::size_t hardware_destructive_interference_size
// Changed extension to .hpp // = 128; from folly/lang/Align.h Changed extension to .hpp Changed namespace to
// Changed namespace to aare // aare
#pragma once #pragma once
@ -45,15 +45,14 @@ template <class T> struct ProducerConsumerQueue {
ProducerConsumerQueue(const ProducerConsumerQueue &) = delete; ProducerConsumerQueue(const ProducerConsumerQueue &) = delete;
ProducerConsumerQueue &operator=(const ProducerConsumerQueue &) = delete; ProducerConsumerQueue &operator=(const ProducerConsumerQueue &) = delete;
ProducerConsumerQueue(ProducerConsumerQueue &&other) {
ProducerConsumerQueue(ProducerConsumerQueue &&other){
size_ = other.size_; size_ = other.size_;
records_ = other.records_; records_ = other.records_;
other.records_ = nullptr; other.records_ = nullptr;
readIndex_ = other.readIndex_.load(std::memory_order_acquire); readIndex_ = other.readIndex_.load(std::memory_order_acquire);
writeIndex_ = other.writeIndex_.load(std::memory_order_acquire); writeIndex_ = other.writeIndex_.load(std::memory_order_acquire);
} }
ProducerConsumerQueue &operator=(ProducerConsumerQueue &&other){ ProducerConsumerQueue &operator=(ProducerConsumerQueue &&other) {
size_ = other.size_; size_ = other.size_;
records_ = other.records_; records_ = other.records_;
other.records_ = nullptr; other.records_ = nullptr;
@ -62,15 +61,16 @@ template <class T> struct ProducerConsumerQueue {
return *this; return *this;
} }
ProducerConsumerQueue() : ProducerConsumerQueue(2){};
ProducerConsumerQueue():ProducerConsumerQueue(2){};
// size must be >= 2. // size must be >= 2.
// //
// Also, note that the number of usable slots in the queue at any // Also, note that the number of usable slots in the queue at any
// given time is actually (size-1), so if you start with an empty queue, // given time is actually (size-1), so if you start with an empty queue,
// isFull() will return true after size-1 insertions. // isFull() will return true after size-1 insertions.
explicit ProducerConsumerQueue(uint32_t size) explicit ProducerConsumerQueue(uint32_t size)
: size_(size), records_(static_cast<T *>(std::malloc(sizeof(T) * size))), readIndex_(0), writeIndex_(0) { : size_(size),
records_(static_cast<T *>(std::malloc(sizeof(T) * size))),
readIndex_(0), writeIndex_(0) {
assert(size >= 2); assert(size >= 2);
if (!records_) { if (!records_) {
throw std::bad_alloc(); throw std::bad_alloc();
@ -154,7 +154,8 @@ template <class T> struct ProducerConsumerQueue {
} }
bool isEmpty() const { bool isEmpty() const {
return readIndex_.load(std::memory_order_acquire) == writeIndex_.load(std::memory_order_acquire); return readIndex_.load(std::memory_order_acquire) ==
writeIndex_.load(std::memory_order_acquire);
} }
bool isFull() const { bool isFull() const {
@ -175,7 +176,8 @@ template <class T> struct ProducerConsumerQueue {
// be removing items concurrently). // be removing items concurrently).
// * It is undefined to call this from any other thread. // * It is undefined to call this from any other thread.
size_t sizeGuess() const { size_t sizeGuess() const {
int ret = writeIndex_.load(std::memory_order_acquire) - readIndex_.load(std::memory_order_acquire); int ret = writeIndex_.load(std::memory_order_acquire) -
readIndex_.load(std::memory_order_acquire);
if (ret < 0) { if (ret < 0) {
ret += size_; ret += size_;
} }
@ -192,7 +194,7 @@ template <class T> struct ProducerConsumerQueue {
// const uint32_t size_; // const uint32_t size_;
uint32_t size_; uint32_t size_;
// T *const records_; // T *const records_;
T* records_; T *records_;
alignas(hardware_destructive_interference_size) AtomicIndex readIndex_; alignas(hardware_destructive_interference_size) AtomicIndex readIndex_;
alignas(hardware_destructive_interference_size) AtomicIndex writeIndex_; alignas(hardware_destructive_interference_size) AtomicIndex writeIndex_;

View File

@ -1,27 +1,23 @@
#pragma once #pragma once
#include "aare/DetectorGeometry.hpp"
#include "aare/FileInterface.hpp" #include "aare/FileInterface.hpp"
#include "aare/RawMasterFile.hpp"
#include "aare/Frame.hpp" #include "aare/Frame.hpp"
#include "aare/NDArray.hpp" //for pixel map #include "aare/NDArray.hpp" //for pixel map
#include "aare/RawMasterFile.hpp"
#include "aare/RawSubFile.hpp" #include "aare/RawSubFile.hpp"
#ifdef AARE_TESTS
#include "../tests/friend_test.hpp"
#endif
#include <optional> #include <optional>
namespace aare { namespace aare {
struct ModuleConfig { #ifdef AARE_TESTS
int module_gap_row{}; TEST_CASE_PRIVATE_FWD(check_find_geometry) // forward declaration
int module_gap_col{}; TEST_CASE_PRIVATE_FWD(open_multi_module_file_with_roi)
#endif
bool operator==(const ModuleConfig &other) const {
if (module_gap_col != other.module_gap_col)
return false;
if (module_gap_row != other.module_gap_row)
return false;
return true;
}
};
/** /**
* @brief Class to read .raw files. The class will parse the master file * @brief Class to read .raw files. The class will parse the master file
@ -30,21 +26,16 @@ struct ModuleConfig {
* Consider using that unless you need raw file specific functionality. * Consider using that unless you need raw file specific functionality.
*/ */
class RawFile : public FileInterface { class RawFile : public FileInterface {
size_t n_subfiles{}; //f0,f1...fn
size_t n_subfile_parts{}; // d0,d1...dn
//TODO! move to vector of SubFile instead of pointers
std::vector<std::vector<RawSubFile *>> subfiles; //subfiles[f0,f1...fn][d0,d1...dn]
// std::vector<xy> positions;
ModuleConfig cfg{0, 0}; #ifdef AARE_TESTS
FRIEND_TEST(check_find_geometry)
FRIEND_TEST(open_multi_module_file_with_roi)
#endif
std::vector<std::unique_ptr<RawSubFile>> m_subfiles;
RawMasterFile m_master; RawMasterFile m_master;
size_t m_current_frame{}; size_t m_current_frame{};
size_t m_current_subfile{};
// std::vector<ModuleGeometry> m_module_pixel_0;
// size_t m_rows{};
// size_t m_cols{};
DetectorGeometry m_geometry; DetectorGeometry m_geometry;
@ -56,7 +47,7 @@ class RawFile : public FileInterface {
*/ */
RawFile(const std::filesystem::path &fname, const std::string &mode = "r"); RawFile(const std::filesystem::path &fname, const std::string &mode = "r");
virtual ~RawFile() override; virtual ~RawFile() override = default;
Frame read_frame() override; Frame read_frame() override;
Frame read_frame(size_t frame_number) override; Frame read_frame(size_t frame_number) override;
@ -64,10 +55,10 @@ class RawFile : public FileInterface {
void read_into(std::byte *image_buf) override; void read_into(std::byte *image_buf) override;
void read_into(std::byte *image_buf, size_t n_frames) override; void read_into(std::byte *image_buf, size_t n_frames) override;
//TODO! do we need to adapt the API? // TODO! do we need to adapt the API?
void read_into(std::byte *image_buf, DetectorHeader *header); void read_into(std::byte *image_buf, DetectorHeader *header);
void read_into(std::byte *image_buf, size_t n_frames, DetectorHeader *header); void read_into(std::byte *image_buf, size_t n_frames,
DetectorHeader *header);
size_t frame_number(size_t frame_index) override; size_t frame_number(size_t frame_index) override;
size_t bytes_per_frame() override; size_t bytes_per_frame() override;
@ -80,34 +71,13 @@ class RawFile : public FileInterface {
size_t cols() const override; size_t cols() const override;
size_t bitdepth() const override; size_t bitdepth() const override;
xy geometry(); xy geometry();
size_t n_mod() const; size_t n_modules() const;
size_t n_modules_in_roi() const;
RawMasterFile master() const; RawMasterFile master() const;
DetectorType detector_type() const override; DetectorType detector_type() const override;
private:
/**
* @brief read the frame at the given frame index into the image buffer
* @param frame_number frame number to read
* @param image_buf buffer to store the frame
*/
void get_frame_into(size_t frame_index, std::byte *frame_buffer, DetectorHeader *header = nullptr);
/**
* @brief get the frame at the given frame index
* @param frame_number frame number to read
* @return Frame
*/
Frame get_frame(size_t frame_index);
/** /**
* @brief read the header of the file * @brief read the header of the file
* @param fname path to the data subfile * @param fname path to the data subfile
@ -115,12 +85,24 @@ class RawFile : public FileInterface {
*/ */
static DetectorHeader read_header(const std::filesystem::path &fname); static DetectorHeader read_header(const std::filesystem::path &fname);
// void update_geometry_with_roi(); private:
int find_number_of_subfiles(); /**
* @brief read the frame at the given frame index into the image buffer
* @param frame_number frame number to read
* @param image_buf buffer to store the frame
*/
void get_frame_into(size_t frame_index, std::byte *frame_buffer,
DetectorHeader *header = nullptr);
/**
* @brief get the frame at the given frame index
* @param frame_number frame number to read
* @return Frame
*/
Frame get_frame(size_t frame_index);
void open_subfiles(); void open_subfiles();
void find_geometry();
}; };
} // namespace aare } // namespace aare

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "aare/defs.hpp" #include "aare/defs.hpp"
#include <algorithm>
#include <filesystem> #include <filesystem>
#include <fmt/format.h> #include <fmt/format.h>
#include <fstream> #include <fstream>
@ -45,7 +46,7 @@ class ScanParameters {
int m_start = 0; int m_start = 0;
int m_stop = 0; int m_stop = 0;
int m_step = 0; int m_step = 0;
//TODO! add settleTime, requires string to time conversion // TODO! add settleTime, requires string to time conversion
public: public:
ScanParameters(const std::string &par); ScanParameters(const std::string &par);
@ -61,7 +62,6 @@ class ScanParameters {
void increment_stop(); void increment_stop();
}; };
/** /**
* @brief Class for parsing a master file either in our .json format or the old * @brief Class for parsing a master file either in our .json format or the old
* .raw format * .raw format
@ -78,8 +78,11 @@ class RawMasterFile {
size_t m_pixels_y{}; size_t m_pixels_y{};
size_t m_pixels_x{}; size_t m_pixels_x{};
size_t m_bitdepth{}; size_t m_bitdepth{};
size_t m_num_udp_interfaces_per_module = 1;
uint8_t m_quad = 0;
xy m_geometry{}; xy m_geometry{};
xy m_udp_interfaces_per_module{1, 1};
size_t m_max_frames_per_file{}; size_t m_max_frames_per_file{};
// uint32_t m_adc_mask{}; // TODO! implement reading // uint32_t m_adc_mask{}; // TODO! implement reading
@ -97,11 +100,9 @@ class RawMasterFile {
std::optional<size_t> m_digital_samples; std::optional<size_t> m_digital_samples;
std::optional<size_t> m_transceiver_samples; std::optional<size_t> m_transceiver_samples;
std::optional<size_t> m_number_of_rows; std::optional<size_t> m_number_of_rows;
std::optional<uint8_t> m_quad;
std::optional<ROI> m_roi; std::optional<ROI> m_roi;
public: public:
RawMasterFile(const std::filesystem::path &fpath); RawMasterFile(const std::filesystem::path &fpath);
@ -117,26 +118,27 @@ class RawMasterFile {
size_t max_frames_per_file() const; size_t max_frames_per_file() const;
size_t bitdepth() const; size_t bitdepth() const;
size_t frame_padding() const; size_t frame_padding() const;
xy udp_interfaces_per_module() const;
const FrameDiscardPolicy &frame_discard_policy() const; const FrameDiscardPolicy &frame_discard_policy() const;
size_t total_frames_expected() const; size_t total_frames_expected() const;
xy geometry() const; xy geometry() const;
size_t n_modules() const;
uint8_t quad() const;
std::optional<size_t> analog_samples() const; std::optional<size_t> analog_samples() const;
std::optional<size_t> digital_samples() const; std::optional<size_t> digital_samples() const;
std::optional<size_t> transceiver_samples() const; std::optional<size_t> transceiver_samples() const;
std::optional<size_t> number_of_rows() const; std::optional<size_t> number_of_rows() const;
std::optional<uint8_t> quad() const;
std::optional<ROI> roi() const; std::optional<ROI> roi() const;
ScanParameters scan_parameters() const; ScanParameters scan_parameters() const;
private: private:
void parse_json(const std::filesystem::path &fpath); void parse_json(const std::filesystem::path &fpath);
void parse_raw(const std::filesystem::path &fpath); void parse_raw(const std::filesystem::path &fpath);
void retrieve_geometry();
}; };
} // namespace aare } // namespace aare

View File

@ -10,23 +10,34 @@
namespace aare { namespace aare {
/** /**
* @brief Class to read a singe subfile written in .raw format. Used from RawFile to read * @brief Class to read a singe subfile written in .raw format. Used from
* the entire detector. Can be used directly to read part of the image. * RawFile to read the entire detector. Can be used directly to read part of the
* image.
*/ */
class RawSubFile { class RawSubFile {
protected: protected:
std::ifstream m_file; std::ifstream m_file;
DetectorType m_detector_type; DetectorType m_detector_type;
size_t m_bitdepth; size_t m_bitdepth;
std::filesystem::path m_fname; std::filesystem::path m_path; //!< path to the subfile
std::string m_base_name; //!< base name used for formatting file names
size_t m_offset{}; //!< file index of the first file, allow starting at non
//!< zero file
size_t m_total_frames{}; //!< total number of frames in the series of files
size_t m_rows{}; size_t m_rows{};
size_t m_cols{}; size_t m_cols{};
size_t m_bytes_per_frame{}; size_t m_bytes_per_frame{};
size_t m_num_frames{};
int m_module_index{};
size_t m_current_file_index{}; //!< The index of the open file
size_t m_current_frame_index{}; //!< The index of the current frame (with
//!< reference to all files)
std::vector<size_t>
m_last_frame_in_file{}; //!< Used for seeking to the correct file
uint32_t m_pos_row{}; uint32_t m_pos_row{};
uint32_t m_pos_col{}; uint32_t m_pos_col{};
std::optional<NDArray<ssize_t, 2>> m_pixel_map; std::optional<NDArray<ssize_t, 2>> m_pixel_map;
public: public:
@ -40,12 +51,14 @@ class RawSubFile {
* @throws std::invalid_argument if the detector,type pair is not supported * @throws std::invalid_argument if the detector,type pair is not supported
*/ */
RawSubFile(const std::filesystem::path &fname, DetectorType detector, RawSubFile(const std::filesystem::path &fname, DetectorType detector,
size_t rows, size_t cols, size_t bitdepth, uint32_t pos_row = 0, uint32_t pos_col = 0); size_t rows, size_t cols, size_t bitdepth, uint32_t pos_row = 0,
uint32_t pos_col = 0);
~RawSubFile() = default; ~RawSubFile() = default;
/** /**
* @brief Seek to the given frame number * @brief Seek to the given frame number
* @note Puts the file pointer at the start of the header, not the start of the data * @note Puts the file pointer at the start of the header, not the start of
* the data
* @param frame_index frame position in file to seek to * @param frame_index frame position in file to seek to
* @throws std::runtime_error if the frame number is out of range * @throws std::runtime_error if the frame number is out of range
*/ */
@ -53,7 +66,8 @@ class RawSubFile {
size_t tell(); size_t tell();
void read_into(std::byte *image_buf, DetectorHeader *header = nullptr); void read_into(std::byte *image_buf, DetectorHeader *header = nullptr);
void read_into(std::byte *image_buf, size_t n_frames, DetectorHeader *header= nullptr); void read_into(std::byte *image_buf, size_t n_frames,
DetectorHeader *header = nullptr);
void get_part(std::byte *buffer, size_t frame_index); void get_part(std::byte *buffer, size_t frame_index);
void read_header(DetectorHeader *header); void read_header(DetectorHeader *header);
@ -67,12 +81,15 @@ class RawSubFile {
size_t pixels_per_frame() const { return m_rows * m_cols; } size_t pixels_per_frame() const { return m_rows * m_cols; }
size_t bytes_per_pixel() const { return m_bitdepth / bits_per_byte; } size_t bytes_per_pixel() const { return m_bitdepth / bits_per_byte; }
size_t frames_in_file() const { return m_num_frames; } size_t frames_in_file() const { return m_total_frames; }
private: private:
template <typename T> template <typename T> void read_with_map(std::byte *image_buf);
void read_with_map(std::byte *image_buf);
void parse_fname(const std::filesystem::path &fname);
void scan_files();
void open_file(size_t file_index);
std::filesystem::path fpath(size_t file_index) const;
}; };
} // namespace aare } // namespace aare

View File

@ -28,7 +28,7 @@ template <typename T> class VarClusterFinder {
}; };
private: private:
const std::array<int64_t, 2> shape_; const std::array<ssize_t, 2> shape_;
NDView<T, 2> original_; NDView<T, 2> original_;
NDArray<int, 2> labeled_; NDArray<int, 2> labeled_;
NDArray<int, 2> peripheral_labeled_; NDArray<int, 2> peripheral_labeled_;
@ -38,11 +38,13 @@ template <typename T> class VarClusterFinder {
bool use_noise_map = false; bool use_noise_map = false;
int peripheralThresholdFactor_ = 5; int peripheralThresholdFactor_ = 5;
int current_label; int current_label;
const std::array<int, 4> di{{0, -1, -1, -1}}; // row ### 8-neighbour by scaning from left to right const std::array<int, 4> di{
const std::array<int, 4> dj{{-1, -1, 0, 1}}; // col ### 8-neighbour by scaning from top to bottom {0, -1, -1, -1}}; // row ### 8-neighbour by scaning from left to right
const std::array<int, 4> dj{
{-1, -1, 0, 1}}; // col ### 8-neighbour by scaning from top to bottom
const std::array<int, 8> di_{{0, 0, -1, 1, -1, 1, -1, 1}}; // row const std::array<int, 8> di_{{0, 0, -1, 1, -1, 1, -1, 1}}; // row
const std::array<int, 8> dj_{{-1, 1, 0, 0, 1, -1, -1, 1}}; // col const std::array<int, 8> dj_{{-1, 1, 0, 0, 1, -1, -1, 1}}; // col
std::map<int, int> child; // heirachy: key: child; val: parent std::map<int, int> child; // heirachy: key: child; val: parent
std::unordered_map<int, Hit> h_size; std::unordered_map<int, Hit> h_size;
std::vector<Hit> hits; std::vector<Hit> hits;
// std::vector<std::vector<int16_t>> row // std::vector<std::vector<int16_t>> row
@ -50,7 +52,8 @@ template <typename T> class VarClusterFinder {
public: public:
VarClusterFinder(Shape<2> shape, T threshold) VarClusterFinder(Shape<2> shape, T threshold)
: shape_(shape), labeled_(shape, 0), peripheral_labeled_(shape, 0), binary_(shape), threshold_(threshold) { : shape_(shape), labeled_(shape, 0), peripheral_labeled_(shape, 0),
binary_(shape), threshold_(threshold) {
hits.reserve(2000); hits.reserve(2000);
} }
@ -60,7 +63,9 @@ template <typename T> class VarClusterFinder {
noiseMap = noise_map; noiseMap = noise_map;
use_noise_map = true; use_noise_map = true;
} }
void set_peripheralThresholdFactor(int factor) { peripheralThresholdFactor_ = factor; } void set_peripheralThresholdFactor(int factor) {
peripheralThresholdFactor_ = factor;
}
void find_clusters(NDView<T, 2> img); void find_clusters(NDView<T, 2> img);
void find_clusters_X(NDView<T, 2> img); void find_clusters_X(NDView<T, 2> img);
void rec_FillHit(int clusterIndex, int i, int j); void rec_FillHit(int clusterIndex, int i, int j);
@ -144,7 +149,8 @@ template <typename T> int VarClusterFinder<T>::check_neighbours(int i, int j) {
} }
} }
template <typename T> void VarClusterFinder<T>::find_clusters(NDView<T, 2> img) { template <typename T>
void VarClusterFinder<T>::find_clusters(NDView<T, 2> img) {
original_ = img; original_ = img;
labeled_ = 0; labeled_ = 0;
peripheral_labeled_ = 0; peripheral_labeled_ = 0;
@ -156,7 +162,8 @@ template <typename T> void VarClusterFinder<T>::find_clusters(NDView<T, 2> img)
store_clusters(); store_clusters();
} }
template <typename T> void VarClusterFinder<T>::find_clusters_X(NDView<T, 2> img) { template <typename T>
void VarClusterFinder<T>::find_clusters_X(NDView<T, 2> img) {
original_ = img; original_ = img;
int clusterIndex = 0; int clusterIndex = 0;
for (int i = 0; i < shape_[0]; ++i) { for (int i = 0; i < shape_[0]; ++i) {
@ -175,7 +182,8 @@ template <typename T> void VarClusterFinder<T>::find_clusters_X(NDView<T, 2> img
h_size.clear(); h_size.clear();
} }
template <typename T> void VarClusterFinder<T>::rec_FillHit(int clusterIndex, int i, int j) { template <typename T>
void VarClusterFinder<T>::rec_FillHit(int clusterIndex, int i, int j) {
// printf("original_(%d, %d)=%f\n", i, j, original_(i,j)); // printf("original_(%d, %d)=%f\n", i, j, original_(i,j));
// printf("h_size[%d].size=%d\n", clusterIndex, h_size[clusterIndex].size); // printf("h_size[%d].size=%d\n", clusterIndex, h_size[clusterIndex].size);
if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE) { if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE) {
@ -203,11 +211,15 @@ template <typename T> void VarClusterFinder<T>::rec_FillHit(int clusterIndex, in
} else { } else {
// if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE){ // if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE){
// h_size[clusterIndex].size += 1; // h_size[clusterIndex].size += 1;
// h_size[clusterIndex].rows[h_size[clusterIndex].size] = row; // h_size[clusterIndex].rows[h_size[clusterIndex].size] =
// h_size[clusterIndex].cols[h_size[clusterIndex].size] = col; // row; h_size[clusterIndex].cols[h_size[clusterIndex].size]
// h_size[clusterIndex].enes[h_size[clusterIndex].size] = original_(row, col); // = col;
// h_size[clusterIndex].enes[h_size[clusterIndex].size] =
// original_(row, col);
// }// ? weather to include peripheral pixels // }// ? weather to include peripheral pixels
original_(row, col) = 0; // remove peripheral pixels, to avoid potential influence for pedestal updating original_(row, col) =
0; // remove peripheral pixels, to avoid potential influence
// for pedestal updating
} }
} }
} }
@ -275,8 +287,8 @@ template <typename T> void VarClusterFinder<T>::store_clusters() {
for (int i = 0; i < shape_[0]; ++i) { for (int i = 0; i < shape_[0]; ++i) {
for (int j = 0; j < shape_[1]; ++j) { for (int j = 0; j < shape_[1]; ++j) {
if (labeled_(i, j) != 0 || false if (labeled_(i, j) != 0 || false
// (i-1 >= 0 and labeled_(i-1, j) != 0) or // another circle of peripheral pixels // (i-1 >= 0 and labeled_(i-1, j) != 0) or // another circle of
// (j-1 >= 0 and labeled_(i, j-1) != 0) or // peripheral pixels (j-1 >= 0 and labeled_(i, j-1) != 0) or
// (i+1 < shape_[0] and labeled_(i+1, j) != 0) or // (i+1 < shape_[0] and labeled_(i+1, j) != 0) or
// (j+1 < shape_[1] and labeled_(i, j+1) != 0) // (j+1 < shape_[1] and labeled_(i, j+1) != 0)
) { ) {

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <aare/NDArray.hpp>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <vector> #include <vector>
#include <aare/NDArray.hpp>
namespace aare { namespace aare {
/** /**
@ -18,23 +18,21 @@ namespace aare {
* *
*/ */
template <typename T> template <typename T>
size_t last_smaller(const T* first, const T* last, T val) { size_t last_smaller(const T *first, const T *last, T val) {
for (auto iter = first+1; iter != last; ++iter) { for (auto iter = first + 1; iter != last; ++iter) {
if (*iter >= val) { if (*iter >= val) {
return std::distance(first, iter-1); return std::distance(first, iter - 1);
} }
} }
return std::distance(first, last-1); return std::distance(first, last - 1);
} }
template <typename T> template <typename T> size_t last_smaller(const NDArray<T, 1> &arr, T val) {
size_t last_smaller(const NDArray<T, 1>& arr, T val) {
return last_smaller(arr.begin(), arr.end(), val); return last_smaller(arr.begin(), arr.end(), val);
} }
template <typename T> template <typename T> size_t last_smaller(const std::vector<T> &vec, T val) {
size_t last_smaller(const std::vector<T>& vec, T val) { return last_smaller(vec.data(), vec.data() + vec.size(), val);
return last_smaller(vec.data(), vec.data()+vec.size(), val);
} }
/** /**
@ -48,64 +46,67 @@ size_t last_smaller(const std::vector<T>& vec, T val) {
* @return index of the first element that is larger than val * @return index of the first element that is larger than val
*/ */
template <typename T> template <typename T>
size_t first_larger(const T* first, const T* last, T val) { size_t first_larger(const T *first, const T *last, T val) {
for (auto iter = first; iter != last; ++iter) { for (auto iter = first; iter != last; ++iter) {
if (*iter > val) { if (*iter > val) {
return std::distance(first, iter); return std::distance(first, iter);
} }
} }
return std::distance(first, last-1); return std::distance(first, last - 1);
} }
template <typename T> template <typename T> size_t first_larger(const NDArray<T, 1> &arr, T val) {
size_t first_larger(const NDArray<T, 1>& arr, T val) {
return first_larger(arr.begin(), arr.end(), val); return first_larger(arr.begin(), arr.end(), val);
} }
template <typename T> template <typename T> size_t first_larger(const std::vector<T> &vec, T val) {
size_t first_larger(const std::vector<T>& vec, T val) { return first_larger(vec.data(), vec.data() + vec.size(), val);
return first_larger(vec.data(), vec.data()+vec.size(), val);
} }
/** /**
* @brief Index of the nearest element to val. * @brief Index of the nearest element to val.
* Requires a sorted array. If there is no difference it takes the first element. * Requires a sorted array. If there is no difference it takes the first
* element.
* @param first iterator to the first element * @param first iterator to the first element
* @param last iterator to the last element * @param last iterator to the last element
* @param val value to compare * @param val value to compare
* @return index of the nearest element * @return index of the nearest element
*/ */
template <typename T> template <typename T>
size_t nearest_index(const T* first, const T* last, T val) { size_t nearest_index(const T *first, const T *last, T val) {
auto iter = std::min_element(first, last, auto iter = std::min_element(first, last, [val](T a, T b) {
[val](T a, T b) {
return std::abs(a - val) < std::abs(b - val); return std::abs(a - val) < std::abs(b - val);
}); });
return std::distance(first, iter); return std::distance(first, iter);
} }
template <typename T> template <typename T> size_t nearest_index(const NDArray<T, 1> &arr, T val) {
size_t nearest_index(const NDArray<T, 1>& arr, T val) {
return nearest_index(arr.begin(), arr.end(), val); return nearest_index(arr.begin(), arr.end(), val);
} }
template <typename T> template <typename T> size_t nearest_index(const std::vector<T> &vec, T val) {
size_t nearest_index(const std::vector<T>& vec, T val) { return nearest_index(vec.data(), vec.data() + vec.size(), val);
return nearest_index(vec.data(), vec.data()+vec.size(), val);
} }
template <typename T, size_t N> template <typename T, size_t N>
size_t nearest_index(const std::array<T,N>& arr, T val) { size_t nearest_index(const std::array<T, N> &arr, T val) {
return nearest_index(arr.data(), arr.data()+arr.size(), val); return nearest_index(arr.data(), arr.data() + arr.size(), val);
} }
template <typename T> template <typename T> std::vector<T> cumsum(const std::vector<T> &vec) {
std::vector<T> cumsum(const std::vector<T>& vec) {
std::vector<T> result(vec.size()); std::vector<T> result(vec.size());
std::partial_sum(vec.begin(), vec.end(), result.begin()); std::partial_sum(vec.begin(), vec.end(), result.begin());
return result; return result;
} }
template <typename Container> bool all_equal(const Container &c) {
if (!c.empty() &&
std::all_of(begin(c), end(c),
[c](const typename Container::value_type &element) {
return element == c.front();
}))
return true;
return false;
}
} // namespace aare } // namespace aare

View File

@ -1,26 +1,27 @@
#pragma once #pragma once
#include <aare/NDView.hpp>
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
#include <aare/NDView.hpp>
namespace aare { namespace aare {
uint16_t adc_sar_05_decode64to16(uint64_t input); uint16_t adc_sar_05_decode64to16(uint64_t input);
uint16_t adc_sar_04_decode64to16(uint64_t input); uint16_t adc_sar_04_decode64to16(uint64_t input);
void adc_sar_05_decode64to16(NDView<uint64_t, 2> input, NDView<uint16_t,2> output); void adc_sar_05_decode64to16(NDView<uint64_t, 2> input,
void adc_sar_04_decode64to16(NDView<uint64_t, 2> input, NDView<uint16_t,2> output); NDView<uint16_t, 2> output);
void adc_sar_04_decode64to16(NDView<uint64_t, 2> input,
NDView<uint16_t, 2> output);
/** /**
* @brief Apply custom weights to a 16-bit input value. Will sum up weights[i]**i * @brief Apply custom weights to a 16-bit input value. Will sum up
* for each bit i that is set in the input value. * weights[i]**i for each bit i that is set in the input value.
* @throws std::out_of_range if weights.size() < 16 * @throws std::out_of_range if weights.size() < 16
* @param input 16-bit input value * @param input 16-bit input value
* @param weights vector of weights, size must be less than or equal to 16 * @param weights vector of weights, size must be less than or equal to 16
*/ */
double apply_custom_weights(uint16_t input, const NDView<double, 1> weights); double apply_custom_weights(uint16_t input, const NDView<double, 1> weights);
void apply_custom_weights(NDView<uint16_t, 1> input, NDView<double, 1> output, const NDView<double, 1> weights); void apply_custom_weights(NDView<uint16_t, 1> input, NDView<double, 1> output,
const NDView<double, 1> weights);
} // namespace aare } // namespace aare

View File

@ -3,16 +3,15 @@
#include "aare/Dtype.hpp" #include "aare/Dtype.hpp"
#include <array> #include <array>
#include <stdexcept>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <stdexcept>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <variant> #include <variant>
#include <vector> #include <vector>
/** /**
* @brief LOCATION macro to get the current location in the code * @brief LOCATION macro to get the current location in the code
*/ */
@ -20,28 +19,24 @@
std::string(__FILE__) + std::string(":") + std::to_string(__LINE__) + \ std::string(__FILE__) + std::string(":") + std::to_string(__LINE__) + \
":" + std::string(__func__) + ":" ":" + std::string(__func__) + ":"
#ifdef AARE_CUSTOM_ASSERT #ifdef AARE_CUSTOM_ASSERT
#define AARE_ASSERT(expr)\ #define AARE_ASSERT(expr) \
if (expr)\ if (expr) { \
{}\ } else \
else\
aare::assert_failed(LOCATION + " Assertion failed: " + #expr + "\n"); aare::assert_failed(LOCATION + " Assertion failed: " + #expr + "\n");
#else #else
#define AARE_ASSERT(cond)\ #define AARE_ASSERT(cond) \
do { (void)sizeof(cond); } while(0) do { \
(void)sizeof(cond); \
} while (0)
#endif #endif
namespace aare { namespace aare {
inline constexpr size_t bits_per_byte = 8; inline constexpr size_t bits_per_byte = 8;
void assert_failed(const std::string &msg); void assert_failed(const std::string &msg);
class DynamicCluster { class DynamicCluster {
public: public:
int cluster_sizeX; int cluster_sizeX;
@ -55,7 +50,7 @@ class DynamicCluster {
public: public:
DynamicCluster(int cluster_sizeX_, int cluster_sizeY_, DynamicCluster(int cluster_sizeX_, int cluster_sizeY_,
Dtype dt_ = Dtype(typeid(int32_t))) Dtype dt_ = Dtype(typeid(int32_t)))
: cluster_sizeX(cluster_sizeX_), cluster_sizeY(cluster_sizeY_), : cluster_sizeX(cluster_sizeX_), cluster_sizeY(cluster_sizeY_),
dt(dt_) { dt(dt_) {
m_data = new std::byte[cluster_sizeX * cluster_sizeY * dt.bytes()]{}; m_data = new std::byte[cluster_sizeX * cluster_sizeY * dt.bytes()]{};
@ -179,50 +174,22 @@ template <typename T> struct t_xy {
}; };
using xy = t_xy<uint32_t>; using xy = t_xy<uint32_t>;
struct ROI {
ssize_t xmin{};
ssize_t xmax{};
ssize_t ymin{};
ssize_t ymax{};
/** ssize_t height() const { return ymax - ymin; }
* @brief Class to hold the geometry of a module. Where pixel 0 is located and the size of the module ssize_t width() const { return xmax - xmin; }
*/ bool contains(ssize_t x, ssize_t y) const {
struct ModuleGeometry{
int origin_x{};
int origin_y{};
int height{};
int width{};
int row_index{};
int col_index{};
};
/**
* @brief Class to hold the geometry of a detector. Number of modules, their size and where pixel 0
* for each module is located
*/
struct DetectorGeometry{
int modules_x{};
int modules_y{};
int pixels_x{};
int pixels_y{};
int module_gap_row{};
int module_gap_col{};
std::vector<ModuleGeometry> module_pixel_0;
};
struct ROI{
int64_t xmin{};
int64_t xmax{};
int64_t ymin{};
int64_t ymax{};
int64_t height() const { return ymax - ymin; }
int64_t width() const { return xmax - xmin; }
bool contains(int64_t x, int64_t y) const {
return x >= xmin && x < xmax && y >= ymin && y < ymax; return x >= xmin && x < xmax && y >= ymin && y < ymax;
} }
}; };
using dynamic_shape = std::vector<ssize_t>;
using dynamic_shape = std::vector<int64_t>; // TODO! Can we uniform enums between the libraries?
//TODO! Can we uniform enums between the libraries?
/** /**
* @brief Enum class to identify different detectors. * @brief Enum class to identify different detectors.
@ -230,7 +197,7 @@ using dynamic_shape = std::vector<int64_t>;
* Different spelling to avoid confusion with the slsDetectorPackage * Different spelling to avoid confusion with the slsDetectorPackage
*/ */
enum class DetectorType { enum class DetectorType {
//Standard detectors match the enum values from slsDetectorPackage // Standard detectors match the enum values from slsDetectorPackage
Generic, Generic,
Eiger, Eiger,
Gotthard, Gotthard,
@ -241,8 +208,9 @@ enum class DetectorType {
Gotthard2, Gotthard2,
Xilinx_ChipTestBoard, Xilinx_ChipTestBoard,
//Additional detectors used for defining processing. Variants of the standard ones. // Additional detectors used for defining processing. Variants of the
Moench03=100, // standard ones.
Moench03 = 100,
Moench03_old, Moench03_old,
Unknown Unknown
}; };

View File

@ -1,16 +0,0 @@
#pragma once
#include "aare/defs.hpp"
#include "aare/RawMasterFile.hpp" //ROI refactor away
namespace aare{
/**
* @brief Update the detector geometry given a region of interest
*
* @param geo
* @param roi
* @return DetectorGeometry
*/
DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, ROI roi);
} // namespace aare

141
include/aare/logger.hpp Normal file
View File

@ -0,0 +1,141 @@
#pragma once
/*Utility to log to console*/
#include <iostream>
#include <sstream>
#include <sys/time.h>
namespace aare {
#define RED "\x1b[31m"
#define GREEN "\x1b[32m"
#define YELLOW "\x1b[33m"
#define BLUE "\x1b[34m"
#define MAGENTA "\x1b[35m"
#define CYAN "\x1b[36m"
#define GRAY "\x1b[37m"
#define DARKGRAY "\x1b[30m"
#define BG_BLACK "\x1b[48;5;232m"
#define BG_RED "\x1b[41m"
#define BG_GREEN "\x1b[42m"
#define BG_YELLOW "\x1b[43m"
#define BG_BLUE "\x1b[44m"
#define BG_MAGENTA "\x1b[45m"
#define BG_CYAN "\x1b[46m"
#define RESET "\x1b[0m"
#define BOLD "\x1b[1m"
enum TLogLevel {
logERROR,
logWARNING,
logINFOBLUE,
logINFOGREEN,
logINFORED,
logINFOCYAN,
logINFOMAGENTA,
logINFO,
logDEBUG, // constructors, destructors etc. should still give too much
// output
logDEBUG1,
logDEBUG2,
logDEBUG3,
logDEBUG4,
logDEBUG5
};
// Compiler should optimize away anything below this value
#ifndef AARE_LOG_LEVEL
#define AARE_LOG_LEVEL \
"LOG LEVEL NOT SET IN CMAKE" // This is configured in the main
// CMakeLists.txt
#endif
#define __AT__ \
std::string(__FILE__) + std::string("::") + std::string(__func__) + \
std::string("(): ")
#define __SHORT_FORM_OF_FILE__ \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define __SHORT_AT__ \
std::string(__SHORT_FORM_OF_FILE__) + std::string("::") + \
std::string(__func__) + std::string("(): ")
class Logger {
std::ostringstream os;
TLogLevel m_level = AARE_LOG_LEVEL;
public:
Logger() = default;
explicit Logger(TLogLevel level) : m_level(level){};
~Logger() {
// output in the destructor to allow for << syntax
os << RESET << '\n';
std::clog << os.str() << std::flush; // Single write
}
static TLogLevel &
ReportingLevel() { // singelton eeh TODO! Do we need a runtime option?
static TLogLevel reportingLevel = logDEBUG5;
return reportingLevel;
}
// Danger this buffer need as many elements as TLogLevel
static const char *Color(TLogLevel level) noexcept {
static const char *const colors[] = {
RED BOLD, YELLOW BOLD, BLUE, GREEN, RED, CYAN, MAGENTA,
RESET, RESET, RESET, RESET, RESET, RESET, RESET};
// out of bounds
if (level < 0 || level >= sizeof(colors) / sizeof(colors[0])) {
return RESET;
}
return colors[level];
}
// Danger this buffer need as many elements as TLogLevel
static std::string ToString(TLogLevel level) {
static const char *const buffer[] = {
"ERROR", "WARNING", "INFO", "INFO", "INFO",
"INFO", "INFO", "INFO", "DEBUG", "DEBUG1",
"DEBUG2", "DEBUG3", "DEBUG4", "DEBUG5"};
// out of bounds
if (level < 0 || level >= sizeof(buffer) / sizeof(buffer[0])) {
return "UNKNOWN";
}
return buffer[level];
}
std::ostringstream &Get() {
os << Color(m_level) << "- " << Timestamp() << " " << ToString(m_level)
<< ": ";
return os;
}
static std::string Timestamp() {
constexpr size_t buffer_len = 12;
char buffer[buffer_len];
time_t t;
::time(&t);
tm r;
strftime(buffer, buffer_len, "%X", localtime_r(&t, &r));
buffer[buffer_len - 1] = '\0';
struct timeval tv;
gettimeofday(&tv, nullptr);
constexpr size_t result_len = 100;
char result[result_len];
snprintf(result, result_len, "%s.%03ld", buffer,
static_cast<long>(tv.tv_usec) / 1000);
result[result_len - 1] = '\0';
return result;
}
};
// TODO! Do we need to keep the runtime option?
#define LOG(level) \
if (level > AARE_LOG_LEVEL) \
; \
else if (level > aare::Logger::ReportingLevel()) \
; \
else \
aare::Logger(level).Get()
} // namespace aare

View File

@ -6,7 +6,7 @@ namespace aare {
/** /**
* @brief Get the error message from an ifstream object * @brief Get the error message from an ifstream object
*/ */
std::string ifstream_error_msg(std::ifstream &ifs); std::string ifstream_error_msg(std::ifstream &ifs);
} // namespace aare } // namespace aare

View File

@ -1,18 +1,18 @@
#include <thread> #include <thread>
#include <vector>
#include <utility> #include <utility>
#include <vector>
namespace aare { namespace aare {
template<typename F> template <typename F>
void RunInParallel(F func, const std::vector<std::pair<int, int>>& tasks) { void RunInParallel(F func, const std::vector<std::pair<int, int>> &tasks) {
// auto tasks = split_task(0, y.shape(0), n_threads); // auto tasks = split_task(0, y.shape(0), n_threads);
std::vector<std::thread> threads; std::vector<std::thread> threads;
for (auto &task : tasks) { for (auto &task : tasks) {
threads.push_back(std::thread(func, task.first, task.second)); threads.push_back(std::thread(func, task.first, task.second));
}
for (auto &thread : threads) {
thread.join();
}
} }
for (auto &thread : threads) {
thread.join();
}
}
} // namespace aare } // namespace aare

View File

@ -1,10 +1,16 @@
[tool.scikit-build.metadata.version]
provider = "scikit_build_core.metadata.regex"
input = "VERSION"
regex = '^(?P<version>\d+(?:\.\d+)*(?:[\.\+\w]+)?)$'
result = "{version}"
[build-system] [build-system]
requires = ["scikit-build-core>=0.10", "pybind11", "numpy"] requires = ["scikit-build-core>=0.10", "pybind11", "numpy"]
build-backend = "scikit_build_core.build" build-backend = "scikit_build_core.build"
[project] [project]
name = "aare" name = "aare"
version = "2025.4.22" dynamic = ["version"]
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"numpy", "numpy",

View File

@ -29,6 +29,9 @@ target_link_libraries(_aare PRIVATE aare_core aare_compiler_flags)
set( PYTHON_FILES set( PYTHON_FILES
aare/__init__.py aare/__init__.py
aare/CtbRawFile.py aare/CtbRawFile.py
aare/ClusterFinder.py
aare/ClusterVector.py
aare/func.py aare/func.py
aare/RawFile.py aare/RawFile.py
aare/transform.py aare/transform.py
@ -36,6 +39,7 @@ set( PYTHON_FILES
aare/utils.py aare/utils.py
) )
# Copy the python files to the build directory # Copy the python files to the build directory
foreach(FILE ${PYTHON_FILES}) foreach(FILE ${PYTHON_FILES})
configure_file(${FILE} ${CMAKE_BINARY_DIR}/${FILE} ) configure_file(${FILE} ${CMAKE_BINARY_DIR}/${FILE} )

View File

@ -0,0 +1,84 @@
# from ._aare import ClusterFinder_Cluster3x3i, ClusterFinder_Cluster2x2i, ClusterFinderMT_Cluster3x3i, ClusterFinderMT_Cluster2x2i, ClusterCollector_Cluster3x3i, ClusterCollector_Cluster2x2i
# from ._aare import ClusterFileSink_Cluster3x3i, ClusterFileSink_Cluster2x2i
from . import _aare
import numpy as np
_supported_cluster_sizes = [(2,2), (3,3), (5,5), (7,7), (9,9),]
# def _get_class()
def _type_to_char(dtype):
if dtype == np.int32:
return 'i'
elif dtype == np.float32:
return 'f'
elif dtype == np.float64:
return 'd'
else:
raise ValueError(f"Unsupported dtype: {dtype}. Only np.int32, np.float32, and np.float64 are supported.")
def _get_class(name, cluster_size, dtype):
"""
Helper function to get the class based on the name, cluster size, and dtype.
"""
try:
class_name = f"{name}_Cluster{cluster_size[0]}x{cluster_size[1]}{_type_to_char(dtype)}"
cls = getattr(_aare, class_name)
except AttributeError:
raise ValueError(f"Unsupported combination of type and cluster size: {dtype}/{cluster_size} when requesting {class_name}")
return cls
def ClusterFinder(image_size, cluster_size, n_sigma=5, dtype = np.int32, capacity = 1024):
"""
Factory function to create a ClusterFinder object. Provides a cleaner syntax for
the templated ClusterFinder in C++.
"""
cls = _get_class("ClusterFinder", cluster_size, dtype)
return cls(image_size, n_sigma=n_sigma, capacity=capacity)
def ClusterFinderMT(image_size, cluster_size = (3,3), dtype=np.int32, n_sigma=5, capacity = 1024, n_threads = 3):
"""
Factory function to create a ClusterFinderMT object. Provides a cleaner syntax for
the templated ClusterFinderMT in C++.
"""
cls = _get_class("ClusterFinderMT", cluster_size, dtype)
return cls(image_size, n_sigma=n_sigma, capacity=capacity, n_threads=n_threads)
def ClusterCollector(clusterfindermt, cluster_size = (3,3), dtype=np.int32):
"""
Factory function to create a ClusterCollector object. Provides a cleaner syntax for
the templated ClusterCollector in C++.
"""
cls = _get_class("ClusterCollector", cluster_size, dtype)
return cls(clusterfindermt)
def ClusterFileSink(clusterfindermt, cluster_file, dtype=np.int32):
"""
Factory function to create a ClusterCollector object. Provides a cleaner syntax for
the templated ClusterCollector in C++.
"""
cls = _get_class("ClusterFileSink", clusterfindermt.cluster_size, dtype)
return cls(clusterfindermt, cluster_file)
def ClusterFile(fname, cluster_size=(3,3), dtype=np.int32):
"""
Factory function to create a ClusterFile object. Provides a cleaner syntax for
the templated ClusterFile in C++.
"""
cls = _get_class("ClusterFile", cluster_size, dtype)
return cls(fname)

View File

@ -0,0 +1,11 @@
from ._aare import ClusterVector_Cluster3x3i
import numpy as np
def ClusterVector(cluster_size, dtype = np.int32):
if dtype == np.int32 and cluster_size == (3,3):
return ClusterVector_Cluster3x3i()
else:
raise ValueError(f"Unsupported dtype: {dtype}. Only np.int32 is supported.")

View File

@ -3,16 +3,20 @@ from . import _aare
from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile
from ._aare import Pedestal_d, Pedestal_f, ClusterFinder, VarClusterFinder from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder
from ._aare import DetectorType from ._aare import DetectorType
from ._aare import ClusterFile
from ._aare import hitmap from ._aare import hitmap
from ._aare import ROI from ._aare import ROI
from ._aare import ClusterFinderMT, ClusterCollector, ClusterFileSink, ClusterVector_i # from ._aare import ClusterFinderMT, ClusterCollector, ClusterFileSink, ClusterVector_i
from ._aare import fit_gaus, fit_pol1 from .ClusterFinder import ClusterFinder, ClusterCollector, ClusterFinderMT, ClusterFileSink, ClusterFile
from .ClusterVector import ClusterVector
from ._aare import fit_gaus, fit_pol1, fit_scurve, fit_scurve2
from ._aare import Interpolator from ._aare import Interpolator
from ._aare import calculate_eta2
from ._aare import apply_custom_weights from ._aare import apply_custom_weights

View File

@ -1 +1 @@
from ._aare import gaus, pol1 from ._aare import gaus, pol1, scurve, scurve2

View File

@ -1,79 +1,89 @@
import sys import sys
sys.path.append('/home/l_msdetect/erik/aare/build') sys.path.append('/home/l_msdetect/erik/aare/build')
from aare._aare import ClusterVector_i, Interpolator
import pickle from aare import RawSubFile, DetectorType, RawFile
import numpy as np
import matplotlib.pyplot as plt from pathlib import Path
import boost_histogram as bh path = Path("/home/l_msdetect/erik/data/aare-test-data/raw/jungfrau/")
import torch f = RawSubFile(path/"jungfrau_single_d0_f0_0.raw", DetectorType.Jungfrau, 512, 1024, 16)
import math
import time # f = RawFile(path/"jungfrau_single_master_0.json")
# from aare._aare import ClusterVector_i, Interpolator
# import pickle
# import numpy as np
# import matplotlib.pyplot as plt
# import boost_histogram as bh
# import torch
# import math
# import time
def gaussian_2d(mx, my, sigma = 1, res=100, grid_size = 2): # def gaussian_2d(mx, my, sigma = 1, res=100, grid_size = 2):
""" # """
Generate a 2D gaussian as position mx, my, with sigma=sigma. # Generate a 2D gaussian as position mx, my, with sigma=sigma.
The gaussian is placed on a 2x2 pixel matrix with resolution # The gaussian is placed on a 2x2 pixel matrix with resolution
res in one dimesion. # res in one dimesion.
""" # """
x = torch.linspace(0, pixel_size*grid_size, res) # x = torch.linspace(0, pixel_size*grid_size, res)
x,y = torch.meshgrid(x,x, indexing="ij") # x,y = torch.meshgrid(x,x, indexing="ij")
return 1 / (2*math.pi*sigma**2) * \ # return 1 / (2*math.pi*sigma**2) * \
torch.exp(-((x - my)**2 / (2*sigma**2) + (y - mx)**2 / (2*sigma**2))) # torch.exp(-((x - my)**2 / (2*sigma**2) + (y - mx)**2 / (2*sigma**2)))
scale = 1000 #Scale factor when converting to integer # scale = 1000 #Scale factor when converting to integer
pixel_size = 25 #um # pixel_size = 25 #um
grid = 2 # grid = 2
resolution = 100 # resolution = 100
sigma_um = 10 # sigma_um = 10
xa = np.linspace(0,grid*pixel_size,resolution) # xa = np.linspace(0,grid*pixel_size,resolution)
ticks = [0, 25, 50] # ticks = [0, 25, 50]
hit = np.array((20,20)) # hit = np.array((20,20))
etahist_fname = "/home/l_msdetect/erik/tmp/test_hist.pkl" # etahist_fname = "/home/l_msdetect/erik/tmp/test_hist.pkl"
local_resolution = 99 # local_resolution = 99
grid_size = 3 # grid_size = 3
xaxis = np.linspace(0,grid_size*pixel_size, local_resolution) # xaxis = np.linspace(0,grid_size*pixel_size, local_resolution)
t = gaussian_2d(hit[0],hit[1], grid_size = grid_size, sigma = 10, res = local_resolution) # t = gaussian_2d(hit[0],hit[1], grid_size = grid_size, sigma = 10, res = local_resolution)
pixels = t.reshape(grid_size, t.shape[0] // grid_size, grid_size, t.shape[1] // grid_size).sum(axis = 3).sum(axis = 1) # pixels = t.reshape(grid_size, t.shape[0] // grid_size, grid_size, t.shape[1] // grid_size).sum(axis = 3).sum(axis = 1)
pixels = pixels.numpy() # pixels = pixels.numpy()
pixels = (pixels*scale).astype(np.int32) # pixels = (pixels*scale).astype(np.int32)
v = ClusterVector_i(3,3) # v = ClusterVector_i(3,3)
v.push_back(1,1, pixels) # v.push_back(1,1, pixels)
with open(etahist_fname, "rb") as f: # with open(etahist_fname, "rb") as f:
hist = pickle.load(f) # hist = pickle.load(f)
eta = hist.view().copy() # eta = hist.view().copy()
etabinsx = np.array(hist.axes.edges.T[0].flat) # etabinsx = np.array(hist.axes.edges.T[0].flat)
etabinsy = np.array(hist.axes.edges.T[1].flat) # etabinsy = np.array(hist.axes.edges.T[1].flat)
ebins = np.array(hist.axes.edges.T[2].flat) # ebins = np.array(hist.axes.edges.T[2].flat)
p = Interpolator(eta, etabinsx[0:-1], etabinsy[0:-1], ebins[0:-1]) # p = Interpolator(eta, etabinsx[0:-1], etabinsy[0:-1], ebins[0:-1])
#Generate the hit # #Generate the hit
tmp = p.interpolate(v) # tmp = p.interpolate(v)
print(f'tmp:{tmp}') # print(f'tmp:{tmp}')
pos = np.array((tmp['x'], tmp['y']))*25 # pos = np.array((tmp['x'], tmp['y']))*25
print(pixels) # print(pixels)
fig, ax = plt.subplots(figsize = (7,7)) # fig, ax = plt.subplots(figsize = (7,7))
ax.pcolormesh(xaxis, xaxis, t) # ax.pcolormesh(xaxis, xaxis, t)
ax.plot(*pos, 'o') # ax.plot(*pos, 'o')
ax.set_xticks([0,25,50,75]) # ax.set_xticks([0,25,50,75])
ax.set_yticks([0,25,50,75]) # ax.set_yticks([0,25,50,75])
ax.set_xlim(0,75) # ax.set_xlim(0,75)
ax.set_ylim(0,75) # ax.set_ylim(0,75)
ax.grid() # ax.grid()
print(f'{hit=}') # print(f'{hit=}')
print(f'{pos=}') # print(f'{pos=}')

View File

@ -0,0 +1,64 @@
#include "aare/Cluster.hpp"
#include <cstdint>
#include <filesystem>
#include <fmt/format.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using pd_type = double;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename Type, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType>
void define_Cluster(py::module &m, const std::string &typestr) {
auto class_name = fmt::format("Cluster{}", typestr);
py::class_<Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType>>(
m, class_name.c_str(), py::buffer_protocol())
.def(py::init([](uint8_t x, uint8_t y, py::array_t<Type> data) {
py::buffer_info buf_info = data.request();
Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType> cluster;
cluster.x = x;
cluster.y = y;
auto r = data.template unchecked<1>(); // no bounds checks
for (py::ssize_t i = 0; i < data.size(); ++i) {
cluster.data[i] = r(i);
}
return cluster;
}));
/*
//TODO! Review if to keep or not
.def_property(
"data",
[](ClusterType &c) -> py::array {
return py::array(py::buffer_info(
c.data, sizeof(Type),
py::format_descriptor<Type>::format(), // Type
// format
1, // Number of dimensions
{static_cast<ssize_t>(ClusterSizeX *
ClusterSizeY)}, // Shape (flattened)
{sizeof(Type)} // Stride (step size between elements)
));
},
[](ClusterType &c, py::array_t<Type> arr) {
py::buffer_info buf_info = arr.request();
Type *ptr = static_cast<Type *>(buf_info.ptr);
std::copy(ptr, ptr + ClusterSizeX * ClusterSizeY,
c.data); // TODO dont iterate over centers!!!
});
*/
}
#pragma GCC diagnostic pop

View File

@ -0,0 +1,44 @@
#include "aare/ClusterCollector.hpp"
#include "aare/ClusterFileSink.hpp"
#include "aare/ClusterFinder.hpp"
#include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDView.hpp"
#include "aare/Pedestal.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using pd_type = double;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterCollector(py::module &m, const std::string &typestr) {
auto class_name = fmt::format("ClusterCollector_{}", typestr);
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
py::class_<ClusterCollector<ClusterType>>(m, class_name.c_str())
.def(py::init<ClusterFinderMT<ClusterType, uint16_t, double> *>())
.def("stop", &ClusterCollector<ClusterType>::stop)
.def(
"steal_clusters",
[](ClusterCollector<ClusterType> &self) {
auto v = new std::vector<ClusterVector<ClusterType>>(
self.steal_clusters());
return v; // TODO change!!!
},
py::return_value_policy::take_ownership);
}
#pragma GCC diagnostic pop

View File

@ -0,0 +1,93 @@
#include "aare/CalculateEta.hpp"
#include "aare/ClusterFile.hpp"
#include "aare/defs.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/iostream.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl/filesystem.h>
#include <string>
// Disable warnings for unused parameters, as we ignore some
// in the __exit__ method
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
namespace py = pybind11;
using namespace ::aare;
template <typename Type, uint8_t CoordSizeX, uint8_t CoordSizeY,
typename CoordType = uint16_t>
void define_ClusterFile(py::module &m, const std::string &typestr) {
using ClusterType = Cluster<Type, CoordSizeX, CoordSizeY, CoordType>;
auto class_name = fmt::format("ClusterFile_{}", typestr);
py::class_<ClusterFile<ClusterType>>(m, class_name.c_str())
.def(py::init<const std::filesystem::path &, size_t,
const std::string &>(),
py::arg(), py::arg("chunk_size") = 1000, py::arg("mode") = "r")
.def(
"read_clusters",
[](ClusterFile<ClusterType> &self, size_t n_clusters) {
auto v = new ClusterVector<ClusterType>(
self.read_clusters(n_clusters));
return v;
},
py::return_value_policy::take_ownership)
.def("read_frame",
[](ClusterFile<ClusterType> &self) {
auto v = new ClusterVector<ClusterType>(self.read_frame());
return v;
})
.def("set_roi", &ClusterFile<ClusterType>::set_roi)
.def(
"set_noise_map",
[](ClusterFile<ClusterType> &self, py::array_t<int32_t> noise_map) {
auto view = make_view_2d(noise_map);
self.set_noise_map(view);
})
.def("set_gain_map",
[](ClusterFile<ClusterType> &self, py::array_t<double> gain_map) {
auto view = make_view_2d(gain_map);
self.set_gain_map(view);
})
.def("close", &ClusterFile<ClusterType>::close)
.def("write_frame", &ClusterFile<ClusterType>::write_frame)
.def("__enter__", [](ClusterFile<ClusterType> &self) { return &self; })
.def("__exit__",
[](ClusterFile<ClusterType> &self,
const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {
self.close();
})
.def("__iter__", [](ClusterFile<ClusterType> &self) { return &self; })
.def("__next__", [](ClusterFile<ClusterType> &self) {
auto v = new ClusterVector<ClusterType>(
self.read_clusters(self.chunk_size()));
if (v->size() == 0) {
throw py::stop_iteration();
}
return v;
});
}
template <typename Type, uint8_t CoordSizeX, uint8_t CoordSizeY,
typename CoordType = uint16_t>
void register_calculate_eta(py::module &m) {
using ClusterType = Cluster<Type, CoordSizeX, CoordSizeY, CoordType>;
m.def("calculate_eta2",
[](const aare::ClusterVector<ClusterType> &clusters) {
auto eta2 = new NDArray<double, 2>(calculate_eta2(clusters));
return return_image_data(eta2);
});
}
#pragma GCC diagnostic pop

View File

@ -0,0 +1,37 @@
#include "aare/ClusterCollector.hpp"
#include "aare/ClusterFileSink.hpp"
#include "aare/ClusterFinder.hpp"
#include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDView.hpp"
#include "aare/Pedestal.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using pd_type = double;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterFileSink(py::module &m, const std::string &typestr) {
auto class_name = fmt::format("ClusterFileSink_{}", typestr);
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
py::class_<ClusterFileSink<ClusterType>>(m, class_name.c_str())
.def(py::init<ClusterFinderMT<ClusterType, uint16_t, double> *,
const std::filesystem::path &>())
.def("stop", &ClusterFileSink<ClusterType>::stop);
}
#pragma GCC diagnostic pop

View File

@ -0,0 +1,77 @@
#include "aare/ClusterCollector.hpp"
#include "aare/ClusterFileSink.hpp"
#include "aare/ClusterFinder.hpp"
#include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDView.hpp"
#include "aare/Pedestal.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using pd_type = double;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterFinder(py::module &m, const std::string &typestr) {
auto class_name = fmt::format("ClusterFinder_{}", typestr);
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
py::class_<ClusterFinder<ClusterType, uint16_t, pd_type>>(
m, class_name.c_str())
.def(py::init<Shape<2>, pd_type, size_t>(), py::arg("image_size"),
py::arg("n_sigma") = 5.0, py::arg("capacity") = 1'000'000)
.def("push_pedestal_frame",
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self,
py::array_t<uint16_t> frame) {
auto view = make_view_2d(frame);
self.push_pedestal_frame(view);
})
.def("clear_pedestal",
&ClusterFinder<ClusterType, uint16_t, pd_type>::clear_pedestal)
.def_property_readonly(
"pedestal",
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self) {
auto pd = new NDArray<pd_type, 2>{};
*pd = self.pedestal();
return return_image_data(pd);
})
.def_property_readonly(
"noise",
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self) {
auto arr = new NDArray<pd_type, 2>{};
*arr = self.noise();
return return_image_data(arr);
})
.def(
"steal_clusters",
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self,
bool realloc_same_capacity) {
ClusterVector<ClusterType> clusters =
self.steal_clusters(realloc_same_capacity);
return clusters;
},
py::arg("realloc_same_capacity") = false)
.def(
"find_clusters",
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self,
py::array_t<uint16_t> frame, uint64_t frame_number) {
auto view = make_view_2d(frame);
self.find_clusters(view, frame_number);
return;
},
py::arg(), py::arg("frame_number") = 0);
}
#pragma GCC diagnostic pop

View File

@ -0,0 +1,81 @@
#include "aare/ClusterCollector.hpp"
#include "aare/ClusterFileSink.hpp"
#include "aare/ClusterFinder.hpp"
#include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDView.hpp"
#include "aare/Pedestal.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using pd_type = double;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterFinderMT(py::module &m, const std::string &typestr) {
auto class_name = fmt::format("ClusterFinderMT_{}", typestr);
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
py::class_<ClusterFinderMT<ClusterType, uint16_t, pd_type>>(
m, class_name.c_str())
.def(py::init<Shape<2>, pd_type, size_t, size_t>(),
py::arg("image_size"), py::arg("n_sigma") = 5.0,
py::arg("capacity") = 2048, py::arg("n_threads") = 3)
.def("push_pedestal_frame",
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
py::array_t<uint16_t> frame) {
auto view = make_view_2d(frame);
self.push_pedestal_frame(view);
})
.def(
"find_clusters",
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
py::array_t<uint16_t> frame, uint64_t frame_number) {
auto view = make_view_2d(frame);
self.find_clusters(view, frame_number);
return;
},
py::arg(), py::arg("frame_number") = 0)
.def_property_readonly(
"cluster_size",
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self) {
return py::make_tuple(ClusterSizeX, ClusterSizeY);
})
.def("clear_pedestal",
&ClusterFinderMT<ClusterType, uint16_t, pd_type>::clear_pedestal)
.def("sync", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::sync)
.def("stop", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::stop)
.def("start", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::start)
.def(
"pedestal",
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
size_t thread_index) {
auto pd = new NDArray<pd_type, 2>{};
*pd = self.pedestal(thread_index);
return return_image_data(pd);
},
py::arg("thread_index") = 0)
.def(
"noise",
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
size_t thread_index) {
auto arr = new NDArray<pd_type, 2>{};
*arr = self.noise(thread_index);
return return_image_data(arr);
},
py::arg("thread_index") = 0);
}
#pragma GCC diagnostic pop

View File

@ -0,0 +1,107 @@
#include "aare/ClusterCollector.hpp"
#include "aare/ClusterFileSink.hpp"
#include "aare/ClusterFinder.hpp"
#include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDView.hpp"
#include "aare/Pedestal.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using pd_type = double;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename Type, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterVector(py::module &m, const std::string &typestr) {
using ClusterType = Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType>;
auto class_name = fmt::format("ClusterVector_{}", typestr);
py::class_<ClusterVector<
Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType>, void>>(
m, class_name.c_str(),
py::buffer_protocol())
.def(py::init()) // TODO change!!!
.def("push_back",
[](ClusterVector<ClusterType> &self, const ClusterType &cluster) {
self.push_back(cluster);
})
.def("sum",
[](ClusterVector<ClusterType> &self) {
auto *vec = new std::vector<Type>(self.sum());
return return_vector(vec);
})
.def("sum_2x2",
[](ClusterVector<ClusterType> &self) {
auto *vec = new std::vector<Type>(self.sum_2x2());
return return_vector(vec);
})
.def_property_readonly("size", &ClusterVector<ClusterType>::size)
.def("item_size", &ClusterVector<ClusterType>::item_size)
.def_property_readonly("fmt",
[typestr](ClusterVector<ClusterType> &self) {
return fmt_format<ClusterType>;
})
.def_property_readonly("cluster_size_x",
&ClusterVector<ClusterType>::cluster_size_x)
.def_property_readonly("cluster_size_y",
&ClusterVector<ClusterType>::cluster_size_y)
.def_property_readonly("capacity",
&ClusterVector<ClusterType>::capacity)
.def_property("frame_number", &ClusterVector<ClusterType>::frame_number,
&ClusterVector<ClusterType>::set_frame_number)
.def_buffer(
[typestr](ClusterVector<ClusterType> &self) -> py::buffer_info {
return py::buffer_info(
self.data(), /* Pointer to buffer */
self.item_size(), /* Size of one scalar */
fmt_format<ClusterType>, /* Format descriptor */
1, /* Number of dimensions */
{self.size()}, /* Buffer dimensions */
{self.item_size()} /* Strides (in bytes) for each index */
);
});
// Free functions using ClusterVector
m.def("hitmap",
[](std::array<size_t, 2> image_size, ClusterVector<ClusterType> &cv) {
// Create a numpy array to hold the hitmap
// The shape of the array is (image_size[0], image_size[1])
// note that the python array is passed as [row, col] which
// is the opposite of the clusters [x,y]
py::array_t<int32_t> hitmap(image_size);
auto r = hitmap.mutable_unchecked<2>();
// Initialize hitmap to 0
for (py::ssize_t i = 0; i < r.shape(0); i++)
for (py::ssize_t j = 0; j < r.shape(1); j++)
r(i, j) = 0;
// Loop over the clusters and increment the hitmap
// Skip out of bound clusters
for (const auto &cluster : cv) {
auto x = cluster.x;
auto y = cluster.y;
if (x < image_size[1] && y < image_size[0])
r(cluster.y, cluster.x) += 1;
}
return hitmap;
});
}
#pragma GCC diagnostic pop

View File

@ -1,209 +0,0 @@
#include "aare/ClusterCollector.hpp"
#include "aare/ClusterFileSink.hpp"
#include "aare/ClusterFinder.hpp"
#include "aare/ClusterFinderMT.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/NDView.hpp"
#include "aare/Pedestal.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using pd_type = double;
template <typename T>
void define_cluster_vector(py::module &m, const std::string &typestr) {
auto class_name = fmt::format("ClusterVector_{}", typestr);
py::class_<ClusterVector<T>>(m, class_name.c_str(), py::buffer_protocol())
.def(py::init<int, int>(),
py::arg("cluster_size_x") = 3, py::arg("cluster_size_y") = 3)
.def("push_back",
[](ClusterVector<T> &self, int x, int y, py::array_t<T> data) {
// auto view = make_view_2d(data);
self.push_back(x, y, reinterpret_cast<const std::byte*>(data.data()));
})
.def_property_readonly("size", &ClusterVector<T>::size)
.def("item_size", &ClusterVector<T>::item_size)
.def_property_readonly("fmt",
[typestr](ClusterVector<T> &self) {
return fmt::format(
self.fmt_base(), self.cluster_size_x(),
self.cluster_size_y(), typestr);
})
.def("sum",
[](ClusterVector<T> &self) {
auto *vec = new std::vector<T>(self.sum());
return return_vector(vec);
})
.def("sum_2x2", [](ClusterVector<T> &self) {
auto *vec = new std::vector<T>(self.sum_2x2());
return return_vector(vec);
})
.def_property_readonly("cluster_size_x", &ClusterVector<T>::cluster_size_x)
.def_property_readonly("cluster_size_y", &ClusterVector<T>::cluster_size_y)
.def_property_readonly("capacity", &ClusterVector<T>::capacity)
.def_property("frame_number", &ClusterVector<T>::frame_number,
&ClusterVector<T>::set_frame_number)
.def_buffer([typestr](ClusterVector<T> &self) -> py::buffer_info {
return py::buffer_info(
self.data(), /* Pointer to buffer */
self.item_size(), /* Size of one scalar */
fmt::format(self.fmt_base(), self.cluster_size_x(),
self.cluster_size_y(),
typestr), /* Format descriptor */
1, /* Number of dimensions */
{self.size()}, /* Buffer dimensions */
{self.item_size()} /* Strides (in bytes) for each index */
);
});
}
void define_cluster_finder_mt_bindings(py::module &m) {
py::class_<ClusterFinderMT<uint16_t, pd_type>>(m, "ClusterFinderMT")
.def(py::init<Shape<2>, Shape<2>, pd_type, size_t, size_t>(),
py::arg("image_size"), py::arg("cluster_size"),
py::arg("n_sigma") = 5.0, py::arg("capacity") = 2048,
py::arg("n_threads") = 3)
.def("push_pedestal_frame",
[](ClusterFinderMT<uint16_t, pd_type> &self,
py::array_t<uint16_t> frame) {
auto view = make_view_2d(frame);
self.push_pedestal_frame(view);
})
.def(
"find_clusters",
[](ClusterFinderMT<uint16_t, pd_type> &self,
py::array_t<uint16_t> frame, uint64_t frame_number) {
auto view = make_view_2d(frame);
self.find_clusters(view, frame_number);
return;
},
py::arg(), py::arg("frame_number") = 0)
.def("clear_pedestal", &ClusterFinderMT<uint16_t, pd_type>::clear_pedestal)
.def("sync", &ClusterFinderMT<uint16_t, pd_type>::sync)
.def("stop", &ClusterFinderMT<uint16_t, pd_type>::stop)
.def("start", &ClusterFinderMT<uint16_t, pd_type>::start)
.def("pedestal",
[](ClusterFinderMT<uint16_t, pd_type> &self, size_t thread_index) {
auto pd = new NDArray<pd_type, 2>{};
*pd = self.pedestal(thread_index);
return return_image_data(pd);
},py::arg("thread_index") = 0)
.def("noise",
[](ClusterFinderMT<uint16_t, pd_type> &self, size_t thread_index) {
auto arr = new NDArray<pd_type, 2>{};
*arr = self.noise(thread_index);
return return_image_data(arr);
},py::arg("thread_index") = 0);
}
void define_cluster_collector_bindings(py::module &m) {
py::class_<ClusterCollector>(m, "ClusterCollector")
.def(py::init<ClusterFinderMT<uint16_t, double, int32_t> *>())
.def("stop", &ClusterCollector::stop)
.def(
"steal_clusters",
[](ClusterCollector &self) {
auto v =
new std::vector<ClusterVector<int>>(self.steal_clusters());
return v;
},
py::return_value_policy::take_ownership);
}
void define_cluster_file_sink_bindings(py::module &m) {
py::class_<ClusterFileSink>(m, "ClusterFileSink")
.def(py::init<ClusterFinderMT<uint16_t, double, int32_t> *,
const std::filesystem::path &>())
.def("stop", &ClusterFileSink::stop);
}
void define_cluster_finder_bindings(py::module &m) {
py::class_<ClusterFinder<uint16_t, pd_type>>(m, "ClusterFinder")
.def(py::init<Shape<2>, Shape<2>, pd_type, size_t>(),
py::arg("image_size"), py::arg("cluster_size"),
py::arg("n_sigma") = 5.0, py::arg("capacity") = 1'000'000)
.def("push_pedestal_frame",
[](ClusterFinder<uint16_t, pd_type> &self,
py::array_t<uint16_t> frame) {
auto view = make_view_2d(frame);
self.push_pedestal_frame(view);
})
.def("clear_pedestal", &ClusterFinder<uint16_t, pd_type>::clear_pedestal)
.def_property_readonly("pedestal",
[](ClusterFinder<uint16_t, pd_type> &self) {
auto pd = new NDArray<pd_type, 2>{};
*pd = self.pedestal();
return return_image_data(pd);
})
.def_property_readonly("noise",
[](ClusterFinder<uint16_t, pd_type> &self) {
auto arr = new NDArray<pd_type, 2>{};
*arr = self.noise();
return return_image_data(arr);
})
.def(
"steal_clusters",
[](ClusterFinder<uint16_t, pd_type> &self,
bool realloc_same_capacity) {
auto v = new ClusterVector<int>(
self.steal_clusters(realloc_same_capacity));
return v;
},
py::arg("realloc_same_capacity") = false)
.def(
"find_clusters",
[](ClusterFinder<uint16_t, pd_type> &self,
py::array_t<uint16_t> frame, uint64_t frame_number) {
auto view = make_view_2d(frame);
self.find_clusters(view, frame_number);
return;
},
py::arg(), py::arg("frame_number") = 0);
m.def("hitmap",
[](std::array<size_t, 2> image_size, ClusterVector<int32_t> &cv) {
py::array_t<int32_t> hitmap(image_size);
auto r = hitmap.mutable_unchecked<2>();
// Initialize hitmap to 0
for (py::ssize_t i = 0; i < r.shape(0); i++)
for (py::ssize_t j = 0; j < r.shape(1); j++)
r(i, j) = 0;
size_t stride = cv.item_size();
auto ptr = cv.data();
for (size_t i = 0; i < cv.size(); i++) {
auto x = *reinterpret_cast<int16_t *>(ptr);
auto y = *reinterpret_cast<int16_t *>(ptr + sizeof(int16_t));
r(y, x) += 1;
ptr += stride;
}
return hitmap;
});
define_cluster_vector<int>(m, "i");
define_cluster_vector<double>(m, "d");
define_cluster_vector<float>(m, "f");
py::class_<DynamicCluster>(m, "DynamicCluster", py::buffer_protocol())
.def(py::init<int, int, Dtype>())
.def("size", &DynamicCluster::size)
.def("begin", &DynamicCluster::begin)
.def("end", &DynamicCluster::end)
.def_readwrite("x", &DynamicCluster::x)
.def_readwrite("y", &DynamicCluster::y)
.def_buffer([](DynamicCluster &c) -> py::buffer_info {
return py::buffer_info(c.data(), c.dt.bytes(), c.dt.format_descr(),
1, {c.size()}, {c.dt.bytes()});
})
.def("__repr__", [](const DynamicCluster &a) {
return "<DynamicCluster: x: " + std::to_string(a.x) +
", y: " + std::to_string(a.y) + ">";
});
}

View File

@ -1,73 +0,0 @@
#include "aare/ClusterFile.hpp"
#include "aare/defs.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/iostream.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl/filesystem.h>
#include <string>
//Disable warnings for unused parameters, as we ignore some
//in the __exit__ method
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
namespace py = pybind11;
using namespace ::aare;
void define_cluster_file_io_bindings(py::module &m) {
PYBIND11_NUMPY_DTYPE(Cluster3x3, x, y, data);
py::class_<ClusterFile>(m, "ClusterFile")
.def(py::init<const std::filesystem::path &, size_t,
const std::string &>(),
py::arg(), py::arg("chunk_size") = 1000, py::arg("mode") = "r")
.def("read_clusters",
[](ClusterFile &self, size_t n_clusters) {
auto v = new ClusterVector<int32_t>(self.read_clusters(n_clusters));
return v;
},py::return_value_policy::take_ownership)
.def("read_frame",
[](ClusterFile &self) {
auto v = new ClusterVector<int32_t>(self.read_frame());
return v;
})
.def("set_roi", &ClusterFile::set_roi)
.def("set_noise_map", [](ClusterFile &self, py::array_t<int32_t> noise_map) {
auto view = make_view_2d(noise_map);
self.set_noise_map(view);
})
.def("set_gain_map", [](ClusterFile &self, py::array_t<double> gain_map) {
auto view = make_view_2d(gain_map);
self.set_gain_map(view);
})
.def("close", &ClusterFile::close)
.def("write_frame", &ClusterFile::write_frame)
.def("__enter__", [](ClusterFile &self) { return &self; })
.def("__exit__",
[](ClusterFile &self,
const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {
self.close();
})
.def("__iter__", [](ClusterFile &self) { return &self; })
.def("__next__", [](ClusterFile &self) {
auto v = new ClusterVector<int32_t>(self.read_clusters(self.chunk_size()));
if (v->size() == 0) {
throw py::stop_iteration();
}
return v;
});
m.def("calculate_eta2", []( aare::ClusterVector<int32_t> &clusters) {
auto eta2 = new NDArray<double, 2>(calculate_eta2(clusters));
return return_image_data(eta2);
});
}
#pragma GCC diagnostic pop

View File

@ -6,8 +6,8 @@
#include "aare/RawMasterFile.hpp" #include "aare/RawMasterFile.hpp"
#include "aare/RawSubFile.hpp" #include "aare/RawSubFile.hpp"
#include "aare/defs.hpp"
#include "aare/decode.hpp" #include "aare/decode.hpp"
#include "aare/defs.hpp"
// #include "aare/fClusterFileV2.hpp" // #include "aare/fClusterFileV2.hpp"
#include "np_helper.hpp" #include "np_helper.hpp"
@ -26,95 +26,103 @@ using namespace ::aare;
void define_ctb_raw_file_io_bindings(py::module &m) { void define_ctb_raw_file_io_bindings(py::module &m) {
m.def("adc_sar_05_decode64to16", [](py::array_t<uint8_t> input) { m.def("adc_sar_05_decode64to16", [](py::array_t<uint8_t> input) {
if (input.ndim() != 2) {
throw std::runtime_error(
"Only 2D arrays are supported at this moment");
}
// Create a 2D output array with the same shape as the input
std::vector<ssize_t> shape{input.shape(0),
input.shape(1) /
static_cast<ssize_t>(bits_per_byte)};
py::array_t<uint16_t> output(shape);
if(input.ndim() != 2){ // Create a view of the input and output arrays
throw std::runtime_error("Only 2D arrays are supported at this moment"); NDView<uint64_t, 2> input_view(
} reinterpret_cast<uint64_t *>(input.mutable_data()),
{output.shape(0), output.shape(1)});
NDView<uint16_t, 2> output_view(output.mutable_data(),
{output.shape(0), output.shape(1)});
//Create a 2D output array with the same shape as the input adc_sar_05_decode64to16(input_view, output_view);
std::vector<ssize_t> shape{input.shape(0), input.shape(1)/static_cast<int64_t>(bits_per_byte)};
py::array_t<uint16_t> output(shape);
//Create a view of the input and output arrays
NDView<uint64_t, 2> input_view(reinterpret_cast<uint64_t*>(input.mutable_data()), {output.shape(0), output.shape(1)});
NDView<uint16_t, 2> output_view(output.mutable_data(), {output.shape(0), output.shape(1)});
adc_sar_05_decode64to16(input_view, output_view);
return output;
});
m.def("adc_sar_04_decode64to16", [](py::array_t<uint8_t> input) {
if(input.ndim() != 2){
throw std::runtime_error("Only 2D arrays are supported at this moment");
}
//Create a 2D output array with the same shape as the input
std::vector<ssize_t> shape{input.shape(0), input.shape(1)/static_cast<int64_t>(bits_per_byte)};
py::array_t<uint16_t> output(shape);
//Create a view of the input and output arrays
NDView<uint64_t, 2> input_view(reinterpret_cast<uint64_t*>(input.mutable_data()), {output.shape(0), output.shape(1)});
NDView<uint16_t, 2> output_view(output.mutable_data(), {output.shape(0), output.shape(1)});
adc_sar_04_decode64to16(input_view, output_view);
return output;
});
m.def(
"apply_custom_weights",
[](py::array_t<uint16_t, py::array::c_style | py::array::forcecast> &input,
py::array_t<double, py::array::c_style | py::array::forcecast>
&weights) {
// Create new array with same shape as the input array (uninitialized values)
py::buffer_info buf = input.request();
py::array_t<double> output(buf.shape);
// Use NDViews to call into the C++ library
auto weights_view = make_view_1d(weights);
NDView<uint16_t, 1> input_view(input.mutable_data(), {input.size()});
NDView<double, 1> output_view(output.mutable_data(), {output.size()});
apply_custom_weights(input_view, output_view, weights_view);
return output; return output;
}); });
py::class_<CtbRawFile>(m, "CtbRawFile") m.def("adc_sar_04_decode64to16", [](py::array_t<uint8_t> input) {
.def(py::init<const std::filesystem::path &>()) if (input.ndim() != 2) {
.def("read_frame", throw std::runtime_error(
[](CtbRawFile &self) { "Only 2D arrays are supported at this moment");
size_t image_size = self.image_size_in_bytes(); }
py::array image;
std::vector<ssize_t> shape;
shape.reserve(2);
shape.push_back(1);
shape.push_back(image_size);
py::array_t<DetectorHeader> header(1); // Create a 2D output array with the same shape as the input
std::vector<ssize_t> shape{input.shape(0),
input.shape(1) /
static_cast<ssize_t>(bits_per_byte)};
py::array_t<uint16_t> output(shape);
// always read bytes // Create a view of the input and output arrays
image = py::array_t<uint8_t>(shape); NDView<uint64_t, 2> input_view(
reinterpret_cast<uint64_t *>(input.mutable_data()),
{output.shape(0), output.shape(1)});
NDView<uint16_t, 2> output_view(output.mutable_data(),
{output.shape(0), output.shape(1)});
self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()), adc_sar_04_decode64to16(input_view, output_view);
header.mutable_data());
return py::make_tuple(header, image); return output;
}) });
.def("seek", &CtbRawFile::seek)
.def("tell", &CtbRawFile::tell)
.def("master", &CtbRawFile::master)
.def_property_readonly("image_size_in_bytes", m.def("apply_custom_weights",
&CtbRawFile::image_size_in_bytes) [](py::array_t<uint16_t, py::array::c_style | py::array::forcecast>
&input,
py::array_t<double, py::array::c_style | py::array::forcecast>
&weights) {
// Create new array with same shape as the input array
// (uninitialized values)
py::buffer_info buf = input.request();
py::array_t<double> output(buf.shape);
.def_property_readonly("frames_in_file", &CtbRawFile::frames_in_file); // Use NDViews to call into the C++ library
auto weights_view = make_view_1d(weights);
NDView<uint16_t, 1> input_view(input.mutable_data(),
{input.size()});
NDView<double, 1> output_view(output.mutable_data(),
{output.size()});
apply_custom_weights(input_view, output_view, weights_view);
return output;
});
py::class_<CtbRawFile>(m, "CtbRawFile")
.def(py::init<const std::filesystem::path &>())
.def("read_frame",
[](CtbRawFile &self) {
size_t image_size = self.image_size_in_bytes();
py::array image;
std::vector<ssize_t> shape;
shape.reserve(2);
shape.push_back(1);
shape.push_back(image_size);
py::array_t<DetectorHeader> header(1);
// always read bytes
image = py::array_t<uint8_t>(shape);
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()),
header.mutable_data());
return py::make_tuple(header, image);
})
.def("seek", &CtbRawFile::seek)
.def("tell", &CtbRawFile::tell)
.def("master", &CtbRawFile::master)
.def_property_readonly("image_size_in_bytes",
&CtbRawFile::image_size_in_bytes)
.def_property_readonly("frames_in_file", &CtbRawFile::frames_in_file);
} }

View File

@ -20,17 +20,13 @@
namespace py = pybind11; namespace py = pybind11;
using namespace ::aare; using namespace ::aare;
// Disable warnings for unused parameters, as we ignore some
// in the __exit__ method
//Disable warnings for unused parameters, as we ignore some
//in the __exit__ method
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
void define_file_io_bindings(py::module &m) { void define_file_io_bindings(py::module &m) {
py::enum_<DetectorType>(m, "DetectorType") py::enum_<DetectorType>(m, "DetectorType")
.value("Jungfrau", DetectorType::Jungfrau) .value("Jungfrau", DetectorType::Jungfrau)
.value("Eiger", DetectorType::Eiger) .value("Eiger", DetectorType::Eiger)
@ -41,13 +37,10 @@ void define_file_io_bindings(py::module &m) {
.value("ChipTestBoard", DetectorType::ChipTestBoard) .value("ChipTestBoard", DetectorType::ChipTestBoard)
.value("Unknown", DetectorType::Unknown); .value("Unknown", DetectorType::Unknown);
PYBIND11_NUMPY_DTYPE(DetectorHeader, frameNumber, expLength, packetNumber, PYBIND11_NUMPY_DTYPE(DetectorHeader, frameNumber, expLength, packetNumber,
bunchId, timestamp, modId, row, column, reserved, bunchId, timestamp, modId, row, column, reserved,
debug, roundRNumber, detType, version, packetMask); debug, roundRNumber, detType, version, packetMask);
py::class_<File>(m, "File") py::class_<File>(m, "File")
.def(py::init([](const std::filesystem::path &fname) { .def(py::init([](const std::filesystem::path &fname) {
return File(fname, "r", {}); return File(fname, "r", {});
@ -112,45 +105,18 @@ void define_file_io_bindings(py::module &m) {
reinterpret_cast<std::byte *>(image.mutable_data())); reinterpret_cast<std::byte *>(image.mutable_data()));
return image; return image;
}) })
.def("read_n", [](File &self, size_t n_frames) { .def("read_n",
//adjust for actual frames left in the file [](File &self, size_t n_frames) {
n_frames = std::min(n_frames, self.total_frames()-self.tell()); // adjust for actual frames left in the file
if(n_frames == 0){ n_frames =
throw std::runtime_error("No frames left in file"); std::min(n_frames, self.total_frames() - self.tell());
} if (n_frames == 0) {
std::vector<size_t> shape{n_frames, self.rows(), self.cols()}; throw std::runtime_error("No frames left in file");
}
std::vector<size_t> shape{n_frames, self.rows(), self.cols()};
py::array image;
const uint8_t item_size = self.bytes_per_pixel();
if (item_size == 1) {
image = py::array_t<uint8_t>(shape);
} else if (item_size == 2) {
image = py::array_t<uint16_t>(shape);
} else if (item_size == 4) {
image = py::array_t<uint32_t>(shape);
}
self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()),
n_frames);
return image;
})
.def("__enter__", [](File &self) { return &self; })
.def("__exit__",
[](File &self,
const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {
// self.close();
})
.def("__iter__", [](File &self) { return &self; })
.def("__next__", [](File &self) {
try{
const uint8_t item_size = self.bytes_per_pixel();
py::array image; py::array image;
std::vector<ssize_t> shape; const uint8_t item_size = self.bytes_per_pixel();
shape.reserve(2);
shape.push_back(self.rows());
shape.push_back(self.cols());
if (item_size == 1) { if (item_size == 1) {
image = py::array_t<uint8_t>(shape); image = py::array_t<uint8_t>(shape);
} else if (item_size == 2) { } else if (item_size == 2) {
@ -159,14 +125,41 @@ void define_file_io_bindings(py::module &m) {
image = py::array_t<uint32_t>(shape); image = py::array_t<uint32_t>(shape);
} }
self.read_into( self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data())); reinterpret_cast<std::byte *>(image.mutable_data()),
n_frames);
return image; return image;
}catch(std::runtime_error &e){ })
.def("__enter__", [](File &self) { return &self; })
.def("__exit__",
[](File &self, const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {
// self.close();
})
.def("__iter__", [](File &self) { return &self; })
.def("__next__", [](File &self) {
try {
const uint8_t item_size = self.bytes_per_pixel();
py::array image;
std::vector<ssize_t> shape;
shape.reserve(2);
shape.push_back(self.rows());
shape.push_back(self.cols());
if (item_size == 1) {
image = py::array_t<uint8_t>(shape);
} else if (item_size == 2) {
image = py::array_t<uint16_t>(shape);
} else if (item_size == 4) {
image = py::array_t<uint32_t>(shape);
}
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()));
return image;
} catch (std::runtime_error &e) {
throw py::stop_iteration(); throw py::stop_iteration();
} }
}); });
py::class_<FileConfig>(m, "FileConfig") py::class_<FileConfig>(m, "FileConfig")
.def(py::init<>()) .def(py::init<>())
.def_readwrite("rows", &FileConfig::rows) .def_readwrite("rows", &FileConfig::rows)
@ -183,8 +176,6 @@ void define_file_io_bindings(py::module &m) {
return "<FileConfig: " + a.to_string() + ">"; return "<FileConfig: " + a.to_string() + ">";
}); });
py::class_<ScanParameters>(m, "ScanParameters") py::class_<ScanParameters>(m, "ScanParameters")
.def(py::init<const std::string &>()) .def(py::init<const std::string &>())
.def(py::init<const ScanParameters &>()) .def(py::init<const ScanParameters &>())
@ -195,32 +186,29 @@ void define_file_io_bindings(py::module &m) {
.def_property_readonly("stop", &ScanParameters::stop) .def_property_readonly("stop", &ScanParameters::stop)
.def_property_readonly("step", &ScanParameters::step); .def_property_readonly("step", &ScanParameters::step);
py::class_<ROI>(m, "ROI") py::class_<ROI>(m, "ROI")
.def(py::init<>()) .def(py::init<>())
.def(py::init<int64_t, int64_t, int64_t, int64_t>(), py::arg("xmin"), .def(py::init<ssize_t, ssize_t, ssize_t, ssize_t>(), py::arg("xmin"),
py::arg("xmax"), py::arg("ymin"), py::arg("ymax")) py::arg("xmax"), py::arg("ymin"), py::arg("ymax"))
.def_readwrite("xmin", &ROI::xmin) .def_readwrite("xmin", &ROI::xmin)
.def_readwrite("xmax", &ROI::xmax) .def_readwrite("xmax", &ROI::xmax)
.def_readwrite("ymin", &ROI::ymin) .def_readwrite("ymin", &ROI::ymin)
.def_readwrite("ymax", &ROI::ymax) .def_readwrite("ymax", &ROI::ymax)
.def("__str__", [](const ROI& self){ .def("__str__",
return fmt::format("ROI: xmin: {} xmax: {} ymin: {} ymax: {}", self.xmin, self.xmax, self.ymin, self.ymax); [](const ROI &self) {
}) return fmt::format("ROI: xmin: {} xmax: {} ymin: {} ymax: {}",
.def("__repr__", [](const ROI& self){ self.xmin, self.xmax, self.ymin, self.ymax);
return fmt::format("<ROI: xmin: {} xmax: {} ymin: {} ymax: {}>", self.xmin, self.xmax, self.ymin, self.ymax); })
}) .def("__repr__",
[](const ROI &self) {
return fmt::format(
"<ROI: xmin: {} xmax: {} ymin: {} ymax: {}>", self.xmin,
self.xmax, self.ymin, self.ymax);
})
.def("__iter__", [](const ROI &self) { .def("__iter__", [](const ROI &self) {
return py::make_iterator(&self.xmin, &self.ymax+1); //NOLINT return py::make_iterator(&self.xmin, &self.ymax + 1); // NOLINT
}); });
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
// py::class_<ClusterHeader>(m, "ClusterHeader") // py::class_<ClusterHeader>(m, "ClusterHeader")
// .def(py::init<>()) // .def(py::init<>())

View File

@ -9,7 +9,6 @@
namespace py = pybind11; namespace py = pybind11;
using namespace pybind11::literals; using namespace pybind11::literals;
void define_fit_bindings(py::module &m) { void define_fit_bindings(py::module &m) {
// TODO! Evaluate without converting to double // TODO! Evaluate without converting to double
@ -55,6 +54,49 @@ void define_fit_bindings(py::module &m) {
)", )",
py::arg("x"), py::arg("par")); py::arg("x"), py::arg("par"));
m.def(
"scurve",
[](py::array_t<double, py::array::c_style | py::array::forcecast> x,
py::array_t<double, py::array::c_style | py::array::forcecast> par) {
auto x_view = make_view_1d(x);
auto par_view = make_view_1d(par);
auto y =
new NDArray<double, 1>{aare::func::scurve(x_view, par_view)};
return return_image_data(y);
},
R"(
Evaluate a 1D scurve function for all points in x using parameters par.
Parameters
----------
x : array_like
The points at which to evaluate the scurve function.
par : array_like
The parameters of the scurve function. The first element is the background slope, the second element is the background intercept, the third element is the mean, the fourth element is the standard deviation, the fifth element is inflexion point count number, and the sixth element is C.
)",
py::arg("x"), py::arg("par"));
m.def(
"scurve2",
[](py::array_t<double, py::array::c_style | py::array::forcecast> x,
py::array_t<double, py::array::c_style | py::array::forcecast> par) {
auto x_view = make_view_1d(x);
auto par_view = make_view_1d(par);
auto y =
new NDArray<double, 1>{aare::func::scurve2(x_view, par_view)};
return return_image_data(y);
},
R"(
Evaluate a 1D scurve2 function for all points in x using parameters par.
Parameters
----------
x : array_like
The points at which to evaluate the scurve function.
par : array_like
The parameters of the scurve2 function. The first element is the background slope, the second element is the background intercept, the third element is the mean, the fourth element is the standard deviation, the fifth element is inflexion point count number, and the sixth element is C.
)",
py::arg("x"), py::arg("par"));
m.def( m.def(
"fit_gaus", "fit_gaus",
@ -98,7 +140,6 @@ n_threads : int, optional
py::array_t<double, py::array::c_style | py::array::forcecast> y, py::array_t<double, py::array::c_style | py::array::forcecast> y,
py::array_t<double, py::array::c_style | py::array::forcecast> y_err, py::array_t<double, py::array::c_style | py::array::forcecast> y_err,
int n_threads) { int n_threads) {
if (y.ndim() == 3) { if (y.ndim() == 3) {
// Allocate memory for the output // Allocate memory for the output
// Need to have pointers to allow python to manage // Need to have pointers to allow python to manage
@ -132,7 +173,6 @@ n_threads : int, optional
auto y_view_err = make_view_1d(y_err); auto y_view_err = make_view_1d(y_err);
auto x_view = make_view_1d(x); auto x_view = make_view_1d(x);
double chi2 = 0; double chi2 = 0;
aare::fit_gaus(x_view, y_view, y_view_err, par->view(), aare::fit_gaus(x_view, y_view, y_view_err, par->view(),
par_err->view(), chi2); par_err->view(), chi2);
@ -211,7 +251,6 @@ n_threads : int, optional
"chi2"_a = return_image_data(chi2), "chi2"_a = return_image_data(chi2),
"Ndf"_a = y.shape(2) - 2); "Ndf"_a = y.shape(2) - 2);
} else if (y.ndim() == 1) { } else if (y.ndim() == 1) {
auto par = new NDArray<double, 1>({2}); auto par = new NDArray<double, 1>({2});
auto par_err = new NDArray<double, 1>({2}); auto par_err = new NDArray<double, 1>({2});
@ -235,6 +274,177 @@ n_threads : int, optional
R"( R"(
Fit a 1D polynomial to data with error estimates. Fit a 1D polynomial to data with error estimates.
Parameters
----------
x : array_like
The x values.
y : array_like
The y values.
y_err : array_like
The error in the y values.
n_threads : int, optional
The number of threads to use. Default is 4.
)",
py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4);
//=========
m.def(
"fit_scurve",
[](py::array_t<double, py::array::c_style | py::array::forcecast> x,
py::array_t<double, py::array::c_style | py::array::forcecast> y,
int n_threads) {
if (y.ndim() == 3) {
auto par = new NDArray<double, 3>{};
auto x_view = make_view_1d(x);
auto y_view = make_view_3d(y);
*par = aare::fit_scurve(x_view, y_view, n_threads);
return return_image_data(par);
} else if (y.ndim() == 1) {
auto par = new NDArray<double, 1>{};
auto x_view = make_view_1d(x);
auto y_view = make_view_1d(y);
*par = aare::fit_scurve(x_view, y_view);
return return_image_data(par);
} else {
throw std::runtime_error("Data must be 1D or 3D");
}
},
py::arg("x"), py::arg("y"), py::arg("n_threads") = 4);
m.def(
"fit_scurve",
[](py::array_t<double, py::array::c_style | py::array::forcecast> x,
py::array_t<double, py::array::c_style | py::array::forcecast> y,
py::array_t<double, py::array::c_style | py::array::forcecast> y_err,
int n_threads) {
if (y.ndim() == 3) {
auto par = new NDArray<double, 3>({y.shape(0), y.shape(1), 6});
auto par_err =
new NDArray<double, 3>({y.shape(0), y.shape(1), 6});
auto y_view = make_view_3d(y);
auto y_view_err = make_view_3d(y_err);
auto x_view = make_view_1d(x);
auto chi2 = new NDArray<double, 2>({y.shape(0), y.shape(1)});
aare::fit_scurve(x_view, y_view, y_view_err, par->view(),
par_err->view(), chi2->view(), n_threads);
return py::dict("par"_a = return_image_data(par),
"par_err"_a = return_image_data(par_err),
"chi2"_a = return_image_data(chi2),
"Ndf"_a = y.shape(2) - 2);
} else if (y.ndim() == 1) {
auto par = new NDArray<double, 1>({2});
auto par_err = new NDArray<double, 1>({2});
auto y_view = make_view_1d(y);
auto y_view_err = make_view_1d(y_err);
auto x_view = make_view_1d(x);
double chi2 = 0;
aare::fit_scurve(x_view, y_view, y_view_err, par->view(),
par_err->view(), chi2);
return py::dict("par"_a = return_image_data(par),
"par_err"_a = return_image_data(par_err),
"chi2"_a = chi2, "Ndf"_a = y.size() - 2);
} else {
throw std::runtime_error("Data must be 1D or 3D");
}
},
R"(
Fit a 1D polynomial to data with error estimates.
Parameters
----------
x : array_like
The x values.
y : array_like
The y values.
y_err : array_like
The error in the y values.
n_threads : int, optional
The number of threads to use. Default is 4.
)",
py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4);
m.def(
"fit_scurve2",
[](py::array_t<double, py::array::c_style | py::array::forcecast> x,
py::array_t<double, py::array::c_style | py::array::forcecast> y,
int n_threads) {
if (y.ndim() == 3) {
auto par = new NDArray<double, 3>{};
auto x_view = make_view_1d(x);
auto y_view = make_view_3d(y);
*par = aare::fit_scurve2(x_view, y_view, n_threads);
return return_image_data(par);
} else if (y.ndim() == 1) {
auto par = new NDArray<double, 1>{};
auto x_view = make_view_1d(x);
auto y_view = make_view_1d(y);
*par = aare::fit_scurve2(x_view, y_view);
return return_image_data(par);
} else {
throw std::runtime_error("Data must be 1D or 3D");
}
},
py::arg("x"), py::arg("y"), py::arg("n_threads") = 4);
m.def(
"fit_scurve2",
[](py::array_t<double, py::array::c_style | py::array::forcecast> x,
py::array_t<double, py::array::c_style | py::array::forcecast> y,
py::array_t<double, py::array::c_style | py::array::forcecast> y_err,
int n_threads) {
if (y.ndim() == 3) {
auto par = new NDArray<double, 3>({y.shape(0), y.shape(1), 6});
auto par_err =
new NDArray<double, 3>({y.shape(0), y.shape(1), 6});
auto y_view = make_view_3d(y);
auto y_view_err = make_view_3d(y_err);
auto x_view = make_view_1d(x);
auto chi2 = new NDArray<double, 2>({y.shape(0), y.shape(1)});
aare::fit_scurve2(x_view, y_view, y_view_err, par->view(),
par_err->view(), chi2->view(), n_threads);
return py::dict("par"_a = return_image_data(par),
"par_err"_a = return_image_data(par_err),
"chi2"_a = return_image_data(chi2),
"Ndf"_a = y.shape(2) - 2);
} else if (y.ndim() == 1) {
auto par = new NDArray<double, 1>({6});
auto par_err = new NDArray<double, 1>({6});
auto y_view = make_view_1d(y);
auto y_view_err = make_view_1d(y_err);
auto x_view = make_view_1d(x);
double chi2 = 0;
aare::fit_scurve2(x_view, y_view, y_view_err, par->view(),
par_err->view(), chi2);
return py::dict("par"_a = return_image_data(par),
"par_err"_a = return_image_data(par_err),
"chi2"_a = chi2, "Ndf"_a = y.size() - 2);
} else {
throw std::runtime_error("Data must be 1D or 3D");
}
},
R"(
Fit a 1D polynomial to data with error estimates.
Parameters Parameters
---------- ----------
x : array_like x : array_like

View File

@ -8,31 +8,55 @@
#include <pybind11/stl.h> #include <pybind11/stl.h>
namespace py = pybind11; namespace py = pybind11;
template <typename Type, uint8_t CoordSizeX, uint8_t CoordSizeY,
typename CoordType = uint16_t>
void register_interpolate(py::class_<aare::Interpolator> &interpolator) {
using ClusterType = Cluster<Type, CoordSizeX, CoordSizeY, CoordType>;
interpolator.def("interpolate",
[](aare::Interpolator &self,
const ClusterVector<ClusterType> &clusters) {
auto photons = self.interpolate<ClusterType>(clusters);
auto *ptr = new std::vector<Photon>{photons};
return return_vector(ptr);
});
}
void define_interpolation_bindings(py::module &m) { void define_interpolation_bindings(py::module &m) {
PYBIND11_NUMPY_DTYPE(aare::Photon, x,y,energy); PYBIND11_NUMPY_DTYPE(aare::Photon, x, y, energy);
py::class_<aare::Interpolator>(m, "Interpolator") auto interpolator =
.def(py::init([](py::array_t<double, py::array::c_style | py::array::forcecast> etacube, py::array_t<double> xbins, py::class_<aare::Interpolator>(m, "Interpolator")
py::array_t<double> ybins, py::array_t<double> ebins) { .def(py::init([](py::array_t<double, py::array::c_style |
return Interpolator(make_view_3d(etacube), make_view_1d(xbins), py::array::forcecast>
make_view_1d(ybins), make_view_1d(ebins)); etacube,
})) py::array_t<double> xbins,
.def("get_ietax", [](Interpolator& self){ py::array_t<double> ybins,
auto*ptr = new NDArray<double,3>{}; py::array_t<double> ebins) {
*ptr = self.get_ietax(); return Interpolator(make_view_3d(etacube), make_view_1d(xbins),
return return_image_data(ptr); make_view_1d(ybins), make_view_1d(ebins));
}) }))
.def("get_ietay", [](Interpolator& self){ .def("get_ietax",
auto*ptr = new NDArray<double,3>{}; [](Interpolator &self) {
*ptr = self.get_ietay(); auto *ptr = new NDArray<double, 3>{};
return return_image_data(ptr); *ptr = self.get_ietax();
}) return return_image_data(ptr);
.def("interpolate", [](Interpolator& self, const ClusterVector<int32_t>& clusters){ })
auto photons = self.interpolate(clusters); .def("get_ietay", [](Interpolator &self) {
auto* ptr = new std::vector<Photon>{photons}; auto *ptr = new NDArray<double, 3>{};
return return_vector(ptr); *ptr = self.get_ietay();
}); return return_image_data(ptr);
});
register_interpolate<int, 3, 3, uint16_t>(interpolator);
register_interpolate<float, 3, 3, uint16_t>(interpolator);
register_interpolate<double, 3, 3, uint16_t>(interpolator);
register_interpolate<int, 2, 2, uint16_t>(interpolator);
register_interpolate<float, 2, 2, uint16_t>(interpolator);
register_interpolate<double, 2, 2, uint16_t>(interpolator);
// TODO! Evaluate without converting to double // TODO! Evaluate without converting to double
m.def( m.def(

View File

@ -21,10 +21,7 @@ using namespace ::aare;
auto read_dat_frame(JungfrauDataFile &self) { auto read_dat_frame(JungfrauDataFile &self) {
py::array_t<JungfrauDataHeader> header(1); py::array_t<JungfrauDataHeader> header(1);
py::array_t<uint16_t> image({ py::array_t<uint16_t> image({self.rows(), self.cols()});
self.rows(),
self.cols()
});
self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()), self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()),
header.mutable_data()); header.mutable_data());
@ -40,9 +37,7 @@ auto read_n_dat_frames(JungfrauDataFile &self, size_t n_frames) {
} }
py::array_t<JungfrauDataHeader> header(n_frames); py::array_t<JungfrauDataHeader> header(n_frames);
py::array_t<uint16_t> image({ py::array_t<uint16_t> image({n_frames, self.rows(), self.cols()});
n_frames, self.rows(),
self.cols()});
self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()), self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()),
n_frames, header.mutable_data()); n_frames, header.mutable_data());

View File

@ -1,25 +1,53 @@
//Files with bindings to the different classes // Files with bindings to the different classes
#include "file.hpp"
#include "raw_file.hpp" // New style file naming
#include "bind_Cluster.hpp"
#include "bind_ClusterCollector.hpp"
#include "bind_ClusterFile.hpp"
#include "bind_ClusterFileSink.hpp"
#include "bind_ClusterFinder.hpp"
#include "bind_ClusterFinderMT.hpp"
#include "bind_ClusterVector.hpp"
// TODO! migrate the other names
#include "ctb_raw_file.hpp" #include "ctb_raw_file.hpp"
#include "raw_master_file.hpp" #include "file.hpp"
#include "var_cluster.hpp"
#include "pixel_map.hpp"
#include "pedestal.hpp"
#include "cluster.hpp"
#include "cluster_file.hpp"
#include "fit.hpp" #include "fit.hpp"
#include "interpolation.hpp" #include "interpolation.hpp"
#include "raw_sub_file.hpp"
#include "jungfrau_data_file.hpp" #include "jungfrau_data_file.hpp"
#include "pedestal.hpp"
#include "pixel_map.hpp"
#include "raw_file.hpp"
#include "raw_master_file.hpp"
#include "raw_sub_file.hpp"
#include "var_cluster.hpp"
//Pybind stuff // Pybind stuff
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/stl.h> #include <pybind11/stl.h>
namespace py = pybind11; namespace py = pybind11;
/* MACRO that defines Cluster bindings for a specific size and type
T - Storage type of the cluster data (int, float, double)
N - Number of rows in the cluster
M - Number of columns in the cluster
U - Type of the pixel data (e.g., uint16_t)
TYPE_CODE - A character representing the type code (e.g., 'i' for int, 'd' for
double, 'f' for float)
*/
#define DEFINE_CLUSTER_BINDINGS(T, N, M, U, TYPE_CODE) \
define_ClusterFile<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE); \
define_ClusterVector<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE); \
define_ClusterFinder<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE); \
define_ClusterFinderMT<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE); \
define_ClusterFileSink<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE); \
define_ClusterCollector<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE); \
define_Cluster<T, N, M, U>(m, #N "x" #M #TYPE_CODE); \
register_calculate_eta<T, N, M, U>(m);
PYBIND11_MODULE(_aare, m) { PYBIND11_MODULE(_aare, m) {
define_file_io_bindings(m); define_file_io_bindings(m);
define_raw_file_io_bindings(m); define_raw_file_io_bindings(m);
@ -30,13 +58,27 @@ PYBIND11_MODULE(_aare, m) {
define_pixel_map_bindings(m); define_pixel_map_bindings(m);
define_pedestal_bindings<double>(m, "Pedestal_d"); define_pedestal_bindings<double>(m, "Pedestal_d");
define_pedestal_bindings<float>(m, "Pedestal_f"); define_pedestal_bindings<float>(m, "Pedestal_f");
define_cluster_finder_bindings(m);
define_cluster_finder_mt_bindings(m);
define_cluster_file_io_bindings(m);
define_cluster_collector_bindings(m);
define_cluster_file_sink_bindings(m);
define_fit_bindings(m); define_fit_bindings(m);
define_interpolation_bindings(m); define_interpolation_bindings(m);
define_jungfrau_data_file_io_bindings(m); define_jungfrau_data_file_io_bindings(m);
DEFINE_CLUSTER_BINDINGS(int, 3, 3, uint16_t, i);
DEFINE_CLUSTER_BINDINGS(double, 3, 3, uint16_t, d);
DEFINE_CLUSTER_BINDINGS(float, 3, 3, uint16_t, f);
DEFINE_CLUSTER_BINDINGS(int, 2, 2, uint16_t, i);
DEFINE_CLUSTER_BINDINGS(double, 2, 2, uint16_t, d);
DEFINE_CLUSTER_BINDINGS(float, 2, 2, uint16_t, f);
DEFINE_CLUSTER_BINDINGS(int, 5, 5, uint16_t, i);
DEFINE_CLUSTER_BINDINGS(double, 5, 5, uint16_t, d);
DEFINE_CLUSTER_BINDINGS(float, 5, 5, uint16_t, f);
DEFINE_CLUSTER_BINDINGS(int, 7, 7, uint16_t, i);
DEFINE_CLUSTER_BINDINGS(double, 7, 7, uint16_t, d);
DEFINE_CLUSTER_BINDINGS(float, 7, 7, uint16_t, f);
DEFINE_CLUSTER_BINDINGS(int, 9, 9, uint16_t, i);
DEFINE_CLUSTER_BINDINGS(double, 9, 9, uint16_t, d);
DEFINE_CLUSTER_BINDINGS(float, 9, 9, uint16_t, f);
} }

View File

@ -10,9 +10,10 @@
#include "aare/NDView.hpp" #include "aare/NDView.hpp"
namespace py = pybind11; namespace py = pybind11;
using namespace aare;
// Pass image data back to python as a numpy array // Pass image data back to python as a numpy array
template <typename T, int64_t Ndim> template <typename T, ssize_t Ndim>
py::array return_image_data(aare::NDArray<T, Ndim> *image) { py::array return_image_data(aare::NDArray<T, Ndim> *image) {
py::capsule free_when_done(image, [](void *f) { py::capsule free_when_done(image, [](void *f) {
@ -40,25 +41,46 @@ template <typename T> py::array return_vector(std::vector<T> *vec) {
} }
// todo rewrite generic // todo rewrite generic
template <class T, int Flags> auto get_shape_3d(const py::array_t<T, Flags>& arr) { template <class T, int Flags>
auto get_shape_3d(const py::array_t<T, Flags> &arr) {
return aare::Shape<3>{arr.shape(0), arr.shape(1), arr.shape(2)}; return aare::Shape<3>{arr.shape(0), arr.shape(1), arr.shape(2)};
} }
template <class T, int Flags> auto make_view_3d(py::array_t<T, Flags>& arr) { template <class T, int Flags> auto make_view_3d(py::array_t<T, Flags> &arr) {
return aare::NDView<T, 3>(arr.mutable_data(), get_shape_3d<T, Flags>(arr)); return aare::NDView<T, 3>(arr.mutable_data(), get_shape_3d<T, Flags>(arr));
} }
template <class T, int Flags> auto get_shape_2d(const py::array_t<T, Flags>& arr) { template <class T, int Flags>
auto get_shape_2d(const py::array_t<T, Flags> &arr) {
return aare::Shape<2>{arr.shape(0), arr.shape(1)}; return aare::Shape<2>{arr.shape(0), arr.shape(1)};
} }
template <class T, int Flags> auto get_shape_1d(const py::array_t<T, Flags>& arr) { template <class T, int Flags>
auto get_shape_1d(const py::array_t<T, Flags> &arr) {
return aare::Shape<1>{arr.shape(0)}; return aare::Shape<1>{arr.shape(0)};
} }
template <class T, int Flags> auto make_view_2d(py::array_t<T, Flags>& arr) { template <class T, int Flags> auto make_view_2d(py::array_t<T, Flags> &arr) {
return aare::NDView<T, 2>(arr.mutable_data(), get_shape_2d<T, Flags>(arr)); return aare::NDView<T, 2>(arr.mutable_data(), get_shape_2d<T, Flags>(arr));
} }
template <class T, int Flags> auto make_view_1d(py::array_t<T, Flags>& arr) { template <class T, int Flags> auto make_view_1d(py::array_t<T, Flags> &arr) {
return aare::NDView<T, 1>(arr.mutable_data(), get_shape_1d<T, Flags>(arr)); return aare::NDView<T, 1>(arr.mutable_data(), get_shape_1d<T, Flags>(arr));
} }
template <typename ClusterType> struct fmt_format_trait; // forward declaration
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType>
struct fmt_format_trait<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> {
static std::string value() {
return fmt::format("T{{{}:x:{}:y:{}:data:}}",
py::format_descriptor<CoordType>::format(),
py::format_descriptor<CoordType>::format(),
fmt::format("({},{}){}", ClusterSizeX, ClusterSizeY,
py::format_descriptor<T>::format()));
}
};
template <typename ClusterType>
auto fmt_format = fmt_format_trait<ClusterType>::value();

View File

@ -9,7 +9,8 @@
namespace py = pybind11; namespace py = pybind11;
template <typename SUM_TYPE> void define_pedestal_bindings(py::module &m, const std::string &name) { template <typename SUM_TYPE>
void define_pedestal_bindings(py::module &m, const std::string &name) {
py::class_<Pedestal<SUM_TYPE>>(m, name.c_str()) py::class_<Pedestal<SUM_TYPE>>(m, name.c_str())
.def(py::init<int, int, int>()) .def(py::init<int, int, int>())
.def(py::init<int, int>()) .def(py::init<int, int>())
@ -19,16 +20,18 @@ template <typename SUM_TYPE> void define_pedestal_bindings(py::module &m, const
*mea = self.mean(); *mea = self.mean();
return return_image_data(mea); return return_image_data(mea);
}) })
.def("variance", [](Pedestal<SUM_TYPE> &self) { .def("variance",
auto var = new NDArray<SUM_TYPE, 2>{}; [](Pedestal<SUM_TYPE> &self) {
*var = self.variance(); auto var = new NDArray<SUM_TYPE, 2>{};
return return_image_data(var); *var = self.variance();
}) return return_image_data(var);
.def("std", [](Pedestal<SUM_TYPE> &self) { })
auto std = new NDArray<SUM_TYPE, 2>{}; .def("std",
*std = self.std(); [](Pedestal<SUM_TYPE> &self) {
return return_image_data(std); auto std = new NDArray<SUM_TYPE, 2>{};
}) *std = self.std();
return return_image_data(std);
})
.def("clear", py::overload_cast<>(&Pedestal<SUM_TYPE>::clear)) .def("clear", py::overload_cast<>(&Pedestal<SUM_TYPE>::clear))
.def_property_readonly("rows", &Pedestal<SUM_TYPE>::rows) .def_property_readonly("rows", &Pedestal<SUM_TYPE>::rows)
.def_property_readonly("cols", &Pedestal<SUM_TYPE>::cols) .def_property_readonly("cols", &Pedestal<SUM_TYPE>::cols)
@ -39,14 +42,19 @@ template <typename SUM_TYPE> void define_pedestal_bindings(py::module &m, const
[&](Pedestal<SUM_TYPE> &pedestal) { [&](Pedestal<SUM_TYPE> &pedestal) {
return Pedestal<SUM_TYPE>(pedestal); return Pedestal<SUM_TYPE>(pedestal);
}) })
//TODO! add push for other data types // TODO! add push for other data types
.def("push", [](Pedestal<SUM_TYPE> &pedestal, py::array_t<uint16_t> &f) { .def("push",
auto v = make_view_2d(f); [](Pedestal<SUM_TYPE> &pedestal, py::array_t<uint16_t> &f) {
pedestal.push(v); auto v = make_view_2d(f);
}) pedestal.push(v);
.def("push_no_update", [](Pedestal<SUM_TYPE> &pedestal, py::array_t<uint16_t, py::array::c_style> &f) { })
auto v = make_view_2d(f); .def(
pedestal.push_no_update(v); "push_no_update",
}, py::arg().noconvert()) [](Pedestal<SUM_TYPE> &pedestal,
py::array_t<uint16_t, py::array::c_style> &f) {
auto v = make_view_2d(f);
pedestal.push_no_update(v);
},
py::arg().noconvert())
.def("update_mean", &Pedestal<SUM_TYPE>::update_mean); .def("update_mean", &Pedestal<SUM_TYPE>::update_mean);
} }

View File

@ -1,41 +1,46 @@
#include "aare/PixelMap.hpp" #include "aare/PixelMap.hpp"
#include "np_helper.hpp" #include "np_helper.hpp"
#include <cstdint> #include <cstdint>
#include <pybind11/numpy.h> #include <pybind11/numpy.h>
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/stl.h> #include <pybind11/stl.h>
namespace py = pybind11; namespace py = pybind11;
using namespace::aare; using namespace ::aare;
void define_pixel_map_bindings(py::module &m) { void define_pixel_map_bindings(py::module &m) {
m.def("GenerateMoench03PixelMap", []() { m.def("GenerateMoench03PixelMap",
auto ptr = new NDArray<ssize_t,2>(GenerateMoench03PixelMap()); []() {
return return_image_data(ptr); auto ptr = new NDArray<ssize_t, 2>(GenerateMoench03PixelMap());
}) return return_image_data(ptr);
.def("GenerateMoench05PixelMap", []() { })
auto ptr = new NDArray<ssize_t,2>(GenerateMoench05PixelMap()); .def("GenerateMoench05PixelMap",
return return_image_data(ptr); []() {
}) auto ptr = new NDArray<ssize_t, 2>(GenerateMoench05PixelMap());
.def("GenerateMoench05PixelMap1g", []() { return return_image_data(ptr);
auto ptr = new NDArray<ssize_t,2>(GenerateMoench05PixelMap1g()); })
return return_image_data(ptr); .def("GenerateMoench05PixelMap1g",
}) []() {
.def("GenerateMoench05PixelMapOld", []() { auto ptr =
auto ptr = new NDArray<ssize_t,2>(GenerateMoench05PixelMapOld()); new NDArray<ssize_t, 2>(GenerateMoench05PixelMap1g());
return return_image_data(ptr); return return_image_data(ptr);
}) })
.def("GenerateMH02SingleCounterPixelMap", []() { .def("GenerateMoench05PixelMapOld",
auto ptr = new NDArray<ssize_t,2>(GenerateMH02SingleCounterPixelMap()); []() {
return return_image_data(ptr); auto ptr =
}) new NDArray<ssize_t, 2>(GenerateMoench05PixelMapOld());
.def("GenerateMH02FourCounterPixelMap", []() { return return_image_data(ptr);
auto ptr = new NDArray<ssize_t,3>(GenerateMH02FourCounterPixelMap()); })
return return_image_data(ptr); .def("GenerateMH02SingleCounterPixelMap",
}); []() {
auto ptr = new NDArray<ssize_t, 2>(
GenerateMH02SingleCounterPixelMap());
return return_image_data(ptr);
})
.def("GenerateMH02FourCounterPixelMap", []() {
auto ptr =
new NDArray<ssize_t, 3>(GenerateMH02FourCounterPixelMap());
return return_image_data(ptr);
});
} }

View File

@ -32,7 +32,7 @@ void define_raw_file_io_bindings(py::module &m) {
shape.push_back(self.cols()); shape.push_back(self.cols());
// return headers from all subfiles // return headers from all subfiles
py::array_t<DetectorHeader> header(self.n_mod()); py::array_t<DetectorHeader> header(self.n_modules());
const uint8_t item_size = self.bytes_per_pixel(); const uint8_t item_size = self.bytes_per_pixel();
if (item_size == 1) { if (item_size == 1) {
@ -61,10 +61,11 @@ void define_raw_file_io_bindings(py::module &m) {
// return headers from all subfiles // return headers from all subfiles
py::array_t<DetectorHeader> header; py::array_t<DetectorHeader> header;
if (self.n_mod() == 1) { if (self.n_modules() == 1) {
header = py::array_t<DetectorHeader>(n_frames); header = py::array_t<DetectorHeader>(n_frames);
} else { } else {
header = py::array_t<DetectorHeader>({self.n_mod(), n_frames}); header = py::array_t<DetectorHeader>(
{self.n_modules_in_roi(), n_frames});
} }
// py::array_t<DetectorHeader> header({self.n_mod(), n_frames}); // py::array_t<DetectorHeader> header({self.n_mod(), n_frames});
@ -100,7 +101,8 @@ void define_raw_file_io_bindings(py::module &m) {
.def_property_readonly("cols", &RawFile::cols) .def_property_readonly("cols", &RawFile::cols)
.def_property_readonly("bitdepth", &RawFile::bitdepth) .def_property_readonly("bitdepth", &RawFile::bitdepth)
.def_property_readonly("geometry", &RawFile::geometry) .def_property_readonly("geometry", &RawFile::geometry)
.def_property_readonly("n_mod", &RawFile::n_mod)
.def_property_readonly("detector_type", &RawFile::detector_type) .def_property_readonly("detector_type", &RawFile::detector_type)
.def_property_readonly("master", &RawFile::master); .def_property_readonly("master", &RawFile::master)
.def_property_readonly("n_modules", &RawFile::n_modules)
.def_property_readonly("n_modules_in_roi", &RawFile::n_modules_in_roi);
} }

View File

@ -57,7 +57,10 @@ void define_raw_master_file_bindings(py::module &m) {
.def_property_readonly("total_frames_expected", .def_property_readonly("total_frames_expected",
&RawMasterFile::total_frames_expected) &RawMasterFile::total_frames_expected)
.def_property_readonly("geometry", &RawMasterFile::geometry) .def_property_readonly("geometry", &RawMasterFile::geometry)
.def_property_readonly("analog_samples", &RawMasterFile::analog_samples, R"( .def_property_readonly("udp_interfaces_per_module",
&RawMasterFile::udp_interfaces_per_module)
.def_property_readonly("analog_samples", &RawMasterFile::analog_samples,
R"(
Number of analog samples Number of analog samples
Returns Returns
@ -66,7 +69,7 @@ void define_raw_master_file_bindings(py::module &m) {
The number of analog samples in the file (or None if not enabled) The number of analog samples in the file (or None if not enabled)
)") )")
.def_property_readonly("digital_samples", .def_property_readonly("digital_samples",
&RawMasterFile::digital_samples, R"( &RawMasterFile::digital_samples, R"(
Number of digital samples Number of digital samples
Returns Returns

View File

@ -24,7 +24,7 @@ auto read_frame_from_RawSubFile(RawSubFile &self) {
py::array_t<DetectorHeader> header(1); py::array_t<DetectorHeader> header(1);
const uint8_t item_size = self.bytes_per_pixel(); const uint8_t item_size = self.bytes_per_pixel();
std::vector<ssize_t> shape{static_cast<ssize_t>(self.rows()), std::vector<ssize_t> shape{static_cast<ssize_t>(self.rows()),
static_cast<ssize_t>(self.cols())}; static_cast<ssize_t>(self.cols())};
py::array image; py::array image;
if (item_size == 1) { if (item_size == 1) {
@ -43,11 +43,9 @@ auto read_frame_from_RawSubFile(RawSubFile &self) {
auto read_n_frames_from_RawSubFile(RawSubFile &self, size_t n_frames) { auto read_n_frames_from_RawSubFile(RawSubFile &self, size_t n_frames) {
py::array_t<DetectorHeader> header(n_frames); py::array_t<DetectorHeader> header(n_frames);
const uint8_t item_size = self.bytes_per_pixel(); const uint8_t item_size = self.bytes_per_pixel();
std::vector<ssize_t> shape{ std::vector<ssize_t> shape{static_cast<ssize_t>(n_frames),
static_cast<ssize_t>(n_frames), static_cast<ssize_t>(self.rows()),
static_cast<ssize_t>(self.rows()), static_cast<ssize_t>(self.cols())};
static_cast<ssize_t>(self.cols())
};
py::array image; py::array image;
if (item_size == 1) { if (item_size == 1) {
@ -57,15 +55,14 @@ auto read_n_frames_from_RawSubFile(RawSubFile &self, size_t n_frames) {
} else if (item_size == 4) { } else if (item_size == 4) {
image = py::array_t<uint32_t>(shape); image = py::array_t<uint32_t>(shape);
} }
self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()), n_frames, self.read_into(reinterpret_cast<std::byte *>(image.mutable_data()),
header.mutable_data()); n_frames, header.mutable_data());
return py::make_tuple(header, image); return py::make_tuple(header, image);
} }
// Disable warnings for unused parameters, as we ignore some
//Disable warnings for unused parameters, as we ignore some // in the __exit__ method
//in the __exit__ method
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
@ -84,18 +81,17 @@ void define_raw_sub_file_io_bindings(py::module &m) {
.def_property_readonly("frames_in_file", &RawSubFile::frames_in_file) .def_property_readonly("frames_in_file", &RawSubFile::frames_in_file)
.def("read_frame", &read_frame_from_RawSubFile) .def("read_frame", &read_frame_from_RawSubFile)
.def("read_n", &read_n_frames_from_RawSubFile) .def("read_n", &read_n_frames_from_RawSubFile)
.def("read", [](RawSubFile &self){ .def("read",
self.seek(0); [](RawSubFile &self) {
auto n_frames = self.frames_in_file(); self.seek(0);
return read_n_frames_from_RawSubFile(self, n_frames); auto n_frames = self.frames_in_file();
}) return read_n_frames_from_RawSubFile(self, n_frames);
})
.def("__enter__", [](RawSubFile &self) { return &self; }) .def("__enter__", [](RawSubFile &self) { return &self; })
.def("__exit__", .def("__exit__",
[](RawSubFile &self, [](RawSubFile &self, const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value, const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) { const std::optional<pybind11::object> &traceback) {})
})
.def("__iter__", [](RawSubFile &self) { return &self; }) .def("__iter__", [](RawSubFile &self) { return &self; })
.def("__next__", [](RawSubFile &self) { .def("__next__", [](RawSubFile &self) {
try { try {
@ -104,7 +100,6 @@ void define_raw_sub_file_io_bindings(py::module &m) {
throw py::stop_iteration(); throw py::stop_iteration();
} }
}); });
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop

View File

@ -12,10 +12,8 @@
// #include <pybind11/stl/filesystem.h> // #include <pybind11/stl/filesystem.h>
// #include <string> // #include <string>
namespace py = pybind11; namespace py = pybind11;
using namespace::aare; using namespace ::aare;
void define_var_cluster_finder_bindings(py::module &m) { void define_var_cluster_finder_bindings(py::module &m) {
PYBIND11_NUMPY_DTYPE(VarClusterFinder<double>::Hit, size, row, col, PYBIND11_NUMPY_DTYPE(VarClusterFinder<double>::Hit, size, row, col,
@ -29,12 +27,12 @@ void define_var_cluster_finder_bindings(py::module &m) {
return return_image_data(ptr); return return_image_data(ptr);
}) })
.def("set_noiseMap", .def("set_noiseMap",
[](VarClusterFinder<double> &self, [](VarClusterFinder<double> &self,
py::array_t<double, py::array::c_style | py::array::forcecast> py::array_t<double, py::array::c_style | py::array::forcecast>
noise_map) { noise_map) {
auto noise_map_span = make_view_2d(noise_map); auto noise_map_span = make_view_2d(noise_map);
self.set_noiseMap(noise_map_span); self.set_noiseMap(noise_map_span);
}) })
.def("set_peripheralThresholdFactor", .def("set_peripheralThresholdFactor",
&VarClusterFinder<double>::set_peripheralThresholdFactor) &VarClusterFinder<double>::set_peripheralThresholdFactor)
.def("find_clusters", .def("find_clusters",
@ -65,9 +63,7 @@ void define_var_cluster_finder_bindings(py::module &m) {
return return_vector(ptr); return return_vector(ptr);
}) })
.def("clear_hits", .def("clear_hits",
[](VarClusterFinder<double> &self) { [](VarClusterFinder<double> &self) { self.clear_hits(); })
self.clear_hits();
})
.def("steal_hits", .def("steal_hits",
[](VarClusterFinder<double> &self) { [](VarClusterFinder<double> &self) {
auto ptr = new std::vector<VarClusterFinder<double>::Hit>( auto ptr = new std::vector<VarClusterFinder<double>::Hit>(
@ -75,5 +71,4 @@ void define_var_cluster_finder_bindings(py::module &m) {
return return_vector(ptr); return return_vector(ptr);
}) })
.def("total_clusters", &VarClusterFinder<double>::total_clusters); .def("total_clusters", &VarClusterFinder<double>::total_clusters);
} }

View File

@ -25,5 +25,10 @@ def pytest_collection_modifyitems(config, items):
@pytest.fixture @pytest.fixture
def test_data_path(): def test_data_path():
return Path(os.environ["AARE_TEST_DATA"]) env_value = os.environ.get("AARE_TEST_DATA")
if not env_value:
raise RuntimeError("Environment variable AARE_TEST_DATA is not set or is empty")
return Path(env_value)

View File

@ -0,0 +1,110 @@
import pytest
import numpy as np
from aare import _aare #import the C++ module
from conftest import test_data_path
def test_cluster_vector_can_be_converted_to_numpy():
cv = _aare.ClusterVector_Cluster3x3i()
arr = np.array(cv, copy=False)
assert arr.shape == (0,) # 4 for x, y, size, energy and 9 for the cluster data
def test_ClusterVector():
"""Test ClusterVector"""
clustervector = _aare.ClusterVector_Cluster3x3i()
assert clustervector.cluster_size_x == 3
assert clustervector.cluster_size_y == 3
assert clustervector.item_size() == 4+9*4
assert clustervector.frame_number == 0
assert clustervector.size == 0
cluster = _aare.Cluster3x3i(0,0,np.ones(9, dtype=np.int32))
clustervector.push_back(cluster)
assert clustervector.size == 1
with pytest.raises(TypeError): # Or use the appropriate exception type
clustervector.push_back(_aare.Cluster2x2i(0,0,np.ones(4, dtype=np.int32)))
with pytest.raises(TypeError):
clustervector.push_back(_aare.Cluster3x3f(0,0,np.ones(9, dtype=np.float32)))
def test_Interpolator():
"""Test Interpolator"""
ebins = np.linspace(0,10, 20, dtype=np.float64)
xbins = np.linspace(0, 5, 30, dtype=np.float64)
ybins = np.linspace(0, 5, 30, dtype=np.float64)
etacube = np.zeros(shape=[30, 30, 20], dtype=np.float64)
interpolator = _aare.Interpolator(etacube, xbins, ybins, ebins)
assert interpolator.get_ietax().shape == (30,30,20)
assert interpolator.get_ietay().shape == (30,30,20)
clustervector = _aare.ClusterVector_Cluster3x3i()
cluster = _aare.Cluster3x3i(0,0, np.ones(9, dtype=np.int32))
clustervector.push_back(cluster)
interpolated_photons = interpolator.interpolate(clustervector)
assert interpolated_photons.size == 1
assert interpolated_photons[0]["x"] == -1
assert interpolated_photons[0]["y"] == -1
assert interpolated_photons[0]["energy"] == 4 #eta_sum = 4, dx, dy = -1,-1 m_ietax = 0, m_ietay = 0
clustervector = _aare.ClusterVector_Cluster2x2i()
cluster = _aare.Cluster2x2i(0,0, np.ones(4, dtype=np.int32))
clustervector.push_back(cluster)
interpolated_photons = interpolator.interpolate(clustervector)
assert interpolated_photons.size == 1
assert interpolated_photons[0]["x"] == 0
assert interpolated_photons[0]["y"] == 0
assert interpolated_photons[0]["energy"] == 4
def test_calculate_eta():
"""Calculate Eta"""
clusters = _aare.ClusterVector_Cluster3x3i()
clusters.push_back(_aare.Cluster3x3i(0,0, np.ones(9, dtype=np.int32)))
clusters.push_back(_aare.Cluster3x3i(0,0, np.array([1,1,1,2,2,2,3,3,3])))
eta2 = _aare.calculate_eta2(clusters)
assert eta2.shape == (2,2)
assert eta2[0,0] == 0.5
assert eta2[0,1] == 0.5
assert eta2[1,0] == 0.5
assert eta2[1,1] == 0.6 #1/5
def test_cluster_finder():
"""Test ClusterFinder"""
clusterfinder = _aare.ClusterFinder_Cluster3x3i([100,100])
#frame = np.random.rand(100,100)
frame = np.zeros(shape=[100,100])
clusterfinder.find_clusters(frame)
clusters = clusterfinder.steal_clusters(False) #conversion does not work
assert clusters.size == 0

View File

@ -0,0 +1,64 @@
import pytest
import numpy as np
import boost_histogram as bh
import time
from pathlib import Path
import pickle
from aare import ClusterFile
from conftest import test_data_path
@pytest.mark.files
def test_cluster_file(test_data_path):
"""Test ClusterFile"""
f = ClusterFile(test_data_path / "clust/single_frame_97_clustrers.clust")
cv = f.read_clusters(10) #conversion does not work
assert cv.frame_number == 135
assert cv.size == 10
#Known data
#frame_number, num_clusters [135] 97
#[ 1 200] [0 1 2 3 4 5 6 7 8]
#[ 2 201] [ 9 10 11 12 13 14 15 16 17]
#[ 3 202] [18 19 20 21 22 23 24 25 26]
#[ 4 203] [27 28 29 30 31 32 33 34 35]
#[ 5 204] [36 37 38 39 40 41 42 43 44]
#[ 6 205] [45 46 47 48 49 50 51 52 53]
#[ 7 206] [54 55 56 57 58 59 60 61 62]
#[ 8 207] [63 64 65 66 67 68 69 70 71]
#[ 9 208] [72 73 74 75 76 77 78 79 80]
#[ 10 209] [81 82 83 84 85 86 87 88 89]
#conversion to numpy array
arr = np.array(cv, copy = False)
assert arr.size == 10
for i in range(10):
assert arr[i]['x'] == i+1
@pytest.mark.files
def test_read_clusters_and_fill_histogram(test_data_path):
# Create the histogram
n_bins = 100
xmin = -100
xmax = 1e4
hist_aare = bh.Histogram(bh.axis.Regular(n_bins, xmin, xmax))
fname = test_data_path / "clust/beam_En700eV_-40deg_300V_10us_d0_f0_100.clust"
#Read clusters and fill the histogram with pixel values
with ClusterFile(fname, chunk_size = 10000) as f:
for clusters in f:
arr = np.array(clusters, copy = False)
hist_aare.fill(arr['data'].flat)
#Load the histogram from the pickle file
with open(fname.with_suffix('.pkl'), 'rb') as f:
hist_py = pickle.load(f)
#Compare the two histograms
assert hist_aare == hist_py

View File

@ -0,0 +1,54 @@
import pytest
import numpy as np
import boost_histogram as bh
import time
from pathlib import Path
import pickle
from aare import ClusterFile
from aare import _aare
from conftest import test_data_path
def test_create_cluster_vector():
cv = _aare.ClusterVector_Cluster3x3i()
assert cv.cluster_size_x == 3
assert cv.cluster_size_y == 3
assert cv.size == 0
def test_push_back_on_cluster_vector():
cv = _aare.ClusterVector_Cluster2x2i()
assert cv.cluster_size_x == 2
assert cv.cluster_size_y == 2
assert cv.size == 0
cluster = _aare.Cluster2x2i(19, 22, np.ones(4, dtype=np.int32))
cv.push_back(cluster)
assert cv.size == 1
arr = np.array(cv, copy=False)
assert arr[0]['x'] == 19
assert arr[0]['y'] == 22
def test_make_a_hitmap_from_cluster_vector():
cv = _aare.ClusterVector_Cluster3x3i()
# Push back 4 clusters with different positions
cv.push_back(_aare.Cluster3x3i(0, 0, np.ones(9, dtype=np.int32)))
cv.push_back(_aare.Cluster3x3i(1, 1, np.ones(9, dtype=np.int32)))
cv.push_back(_aare.Cluster3x3i(1, 1, np.ones(9, dtype=np.int32)))
cv.push_back(_aare.Cluster3x3i(2, 2, np.ones(9, dtype=np.int32)))
ref = np.zeros((5, 5), dtype=np.int32)
ref[0,0] = 1
ref[1,1] = 2
ref[2,2] = 1
img = _aare.hitmap((5,5), cv)
# print(img)
# print(ref)
assert (img == ref).all()

View File

@ -0,0 +1,14 @@
import pytest
from aare import RawFile
@pytest.mark.files
def test_read_rawfile_with_roi(test_data_path):
# Starting with f1 there is now 7 frames left in the series of files
print(test_data_path)
with RawFile(test_data_path / "raw/SingleChipROI/Data_master_0.json") as f:
headers, frames = f.read()
assert headers.size == 10100
assert frames.shape == (10100, 256, 256)

View File

@ -5,32 +5,35 @@ from aare import RawSubFile, DetectorType
@pytest.mark.files @pytest.mark.files
def test_read_a_jungfrau_RawSubFile(test_data_path): def test_read_a_jungfrau_RawSubFile(test_data_path):
# Starting with f1 there is now 7 frames left in the series of files
with RawSubFile(test_data_path / "raw/jungfrau/jungfrau_single_d0_f1_0.raw", DetectorType.Jungfrau, 512, 1024, 16) as f: with RawSubFile(test_data_path / "raw/jungfrau/jungfrau_single_d0_f1_0.raw", DetectorType.Jungfrau, 512, 1024, 16) as f:
assert f.frames_in_file == 3 assert f.frames_in_file == 7
headers, frames = f.read() headers, frames = f.read()
assert headers.size == 3 assert headers.size == 7
assert frames.shape == (3, 512, 1024) assert frames.shape == (7, 512, 1024)
# Frame numbers in this file should be 4, 5, 6
for i,h in zip(range(4,7,1), headers): for i,h in zip(range(4,11,1), headers):
assert h["frameNumber"] == i assert h["frameNumber"] == i
# Compare to canned data using numpy # Compare to canned data using numpy
data = np.load(test_data_path / "raw/jungfrau/jungfrau_single_0.npy") data = np.load(test_data_path / "raw/jungfrau/jungfrau_single_0.npy")
assert np.all(data[3:6] == frames) assert np.all(data[3:] == frames)
@pytest.mark.files @pytest.mark.files
def test_iterate_over_a_jungfrau_RawSubFile(test_data_path): def test_iterate_over_a_jungfrau_RawSubFile(test_data_path):
data = np.load(test_data_path / "raw/jungfrau/jungfrau_single_0.npy") data = np.load(test_data_path / "raw/jungfrau/jungfrau_single_0.npy")
# Given the first subfile in a series we can read all frames from f0, f1, f2...fN
with RawSubFile(test_data_path / "raw/jungfrau/jungfrau_single_d0_f0_0.raw", DetectorType.Jungfrau, 512, 1024, 16) as f: with RawSubFile(test_data_path / "raw/jungfrau/jungfrau_single_d0_f0_0.raw", DetectorType.Jungfrau, 512, 1024, 16) as f:
i = 0 i = 0
for header, frame in f: for header, frame in f:
assert header["frameNumber"] == i+1 assert header["frameNumber"] == i+1
assert np.all(frame == data[i]) assert np.all(frame == data[i])
i += 1 i += 1
assert i == 3 assert i == 10
assert header["frameNumber"] == 3 assert header["frameNumber"] == 10

127
src/CalculateEta.test.cpp Normal file
View File

@ -0,0 +1,127 @@
/************************************************
* @file CalculateEta.test.cpp
* @short test case to calculate_eta2
***********************************************/
#include "aare/CalculateEta.hpp"
#include "aare/Cluster.hpp"
#include "aare/ClusterFile.hpp"
// #include "catch.hpp"
#include <array>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
using namespace aare;
using ClusterTypes =
std::variant<Cluster<int, 2, 2>, Cluster<int, 3, 3>, Cluster<int, 5, 5>,
Cluster<int, 4, 2>, Cluster<int, 2, 3>>;
auto get_test_parameters() {
return GENERATE(
std::make_tuple(ClusterTypes{Cluster<int, 2, 2>{0, 0, {1, 2, 3, 1}}},
Eta2<int>{2. / 3, 3. / 4,
static_cast<int>(corner::cBottomLeft), 7}),
std::make_tuple(
ClusterTypes{Cluster<int, 3, 3>{0, 0, {1, 2, 3, 4, 5, 6, 1, 2, 7}}},
Eta2<int>{6. / 11, 2. / 7, static_cast<int>(corner::cTopRight),
20}),
std::make_tuple(ClusterTypes{Cluster<int, 5, 5>{
0, 0, {1, 6, 7, 6, 5, 4, 3, 2, 1, 2, 8, 9, 8,
1, 4, 5, 6, 7, 8, 4, 1, 1, 1, 1, 1}}},
Eta2<int>{8. / 17, 7. / 15, 9, 30}),
std::make_tuple(
ClusterTypes{Cluster<int, 4, 2>{0, 0, {1, 4, 7, 2, 5, 6, 4, 3}}},
Eta2<int>{4. / 10, 4. / 11, 1, 21}),
std::make_tuple(
ClusterTypes{Cluster<int, 2, 3>{0, 0, {1, 3, 2, 3, 4, 2}}},
Eta2<int>{3. / 5, 2. / 5, 1, 11}));
}
TEST_CASE("compute_largest_2x2_subcluster", "[eta_calculation]") {
auto [cluster, expected_eta] = get_test_parameters();
auto [sum, index] = std::visit(
[](const auto &clustertype) { return clustertype.max_sum_2x2(); },
cluster);
CHECK(expected_eta.c == index);
CHECK(expected_eta.sum == sum);
}
TEST_CASE("calculate_eta2", "[eta_calculation]") {
auto [cluster, expected_eta] = get_test_parameters();
auto eta = std::visit(
[](const auto &clustertype) { return calculate_eta2(clustertype); },
cluster);
CHECK(eta.x == expected_eta.x);
CHECK(eta.y == expected_eta.y);
CHECK(eta.c == expected_eta.c);
CHECK(eta.sum == expected_eta.sum);
}
// 3x3 cluster layout (rotated to match the cBottomLeft enum):
// 6, 7, 8
// 3, 4, 5
// 0, 1, 2
TEST_CASE("Calculate eta2 for a 3x3 int32 cluster with the largest 2x2 sum in "
"the bottom left",
"[eta_calculation]") {
// Create a 3x3 cluster
Cluster<int32_t, 3, 3> cl;
cl.x = 0;
cl.y = 0;
cl.data[0] = 30;
cl.data[1] = 23;
cl.data[2] = 5;
cl.data[3] = 20;
cl.data[4] = 50;
cl.data[5] = 3;
cl.data[6] = 8;
cl.data[7] = 2;
cl.data[8] = 3;
// 8, 2, 3
// 20, 50, 3
// 30, 23, 5
auto eta = calculate_eta2(cl);
CHECK(eta.c == static_cast<int>(corner::cBottomLeft));
CHECK(eta.x == 50.0 / (20 + 50)); // 4/(3+4)
CHECK(eta.y == 50.0 / (23 + 50)); // 4/(1+4)
CHECK(eta.sum == 30 + 23 + 20 + 50);
}
TEST_CASE("Calculate eta2 for a 3x3 int32 cluster with the largest 2x2 sum in "
"the top left",
"[eta_calculation]") {
// Create a 3x3 cluster
Cluster<int32_t, 3, 3> cl;
cl.x = 0;
cl.y = 0;
cl.data[0] = 8;
cl.data[1] = 12;
cl.data[2] = 5;
cl.data[3] = 77;
cl.data[4] = 80;
cl.data[5] = 3;
cl.data[6] = 82;
cl.data[7] = 91;
cl.data[8] = 3;
// 82, 91, 3
// 77, 80, 3
// 8, 12, 5
auto eta = calculate_eta2(cl);
CHECK(eta.c == static_cast<int>(corner::cTopLeft));
CHECK(eta.x == 80. / (77 + 80)); // 4/(3+4)
CHECK(eta.y == 91.0 / (91 + 80)); // 7/(7+4)
CHECK(eta.sum == 77 + 80 + 82 + 91);
}

21
src/Cluster.test.cpp Normal file
View File

@ -0,0 +1,21 @@
/************************************************
* @file test-Cluster.cpp
* @short test case for generic Cluster, ClusterVector, and calculate_eta2
***********************************************/
#include "aare/Cluster.hpp"
#include "aare/CalculateEta.hpp"
#include "aare/ClusterFile.hpp"
// #include "catch.hpp"
#include <array>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
using namespace aare;
TEST_CASE("Test sum of Cluster", "[.cluster]") {
Cluster<int, 2, 2> cluster{0, 0, {1, 2, 3, 4}};
CHECK(cluster.sum() == 10);
}

View File

@ -31,15 +31,13 @@ ClusterFile::ClusterFile(const std::filesystem::path &fname, size_t chunk_size,
} }
} }
void ClusterFile::set_roi(ROI roi){ void ClusterFile::set_roi(ROI roi) { m_roi = roi; }
m_roi = roi;
}
void ClusterFile::set_noise_map(const NDView<int32_t, 2> noise_map){ void ClusterFile::set_noise_map(const NDView<int32_t, 2> noise_map) {
m_noise_map = NDArray<int32_t, 2>(noise_map); m_noise_map = NDArray<int32_t, 2>(noise_map);
} }
void ClusterFile::set_gain_map(const NDView<double, 2> gain_map){ void ClusterFile::set_gain_map(const NDView<double, 2> gain_map) {
m_gain_map = NDArray<double, 2>(gain_map); m_gain_map = NDArray<double, 2>(gain_map);
// Gain map is passed as ADU/keV to avoid dividing in when applying the gain // Gain map is passed as ADU/keV to avoid dividing in when applying the gain
@ -66,42 +64,44 @@ void ClusterFile::write_frame(const ClusterVector<int32_t> &clusters) {
!(clusters.cluster_size_y() == 3)) { !(clusters.cluster_size_y() == 3)) {
throw std::runtime_error("Only 3x3 clusters are supported"); throw std::runtime_error("Only 3x3 clusters are supported");
} }
//First write the frame number - 4 bytes // First write the frame number - 4 bytes
int32_t frame_number = clusters.frame_number(); int32_t frame_number = clusters.frame_number();
if(fwrite(&frame_number, sizeof(frame_number), 1, fp)!=1){ if (fwrite(&frame_number, sizeof(frame_number), 1, fp) != 1) {
throw std::runtime_error(LOCATION + "Could not write frame number"); throw std::runtime_error(LOCATION + "Could not write frame number");
} }
//Then write the number of clusters - 4 bytes // Then write the number of clusters - 4 bytes
uint32_t n_clusters = clusters.size(); uint32_t n_clusters = clusters.size();
if(fwrite(&n_clusters, sizeof(n_clusters), 1, fp)!=1){ if (fwrite(&n_clusters, sizeof(n_clusters), 1, fp) != 1) {
throw std::runtime_error(LOCATION + "Could not write number of clusters"); throw std::runtime_error(LOCATION +
"Could not write number of clusters");
} }
//Now write the clusters in the frame // Now write the clusters in the frame
if(fwrite(clusters.data(), clusters.item_size(), clusters.size(), fp)!=clusters.size()){ if (fwrite(clusters.data(), clusters.item_size(), clusters.size(), fp) !=
clusters.size()) {
throw std::runtime_error(LOCATION + "Could not write clusters"); throw std::runtime_error(LOCATION + "Could not write clusters");
} }
} }
ClusterVector<int32_t> ClusterFile::read_clusters(size_t n_clusters) {
ClusterVector<int32_t> ClusterFile::read_clusters(size_t n_clusters){
if (m_mode != "r") { if (m_mode != "r") {
throw std::runtime_error("File not opened for reading"); throw std::runtime_error("File not opened for reading");
} }
if (m_noise_map || m_roi){ if (m_noise_map || m_roi) {
return read_clusters_with_cut(n_clusters); return read_clusters_with_cut(n_clusters);
}else{ } else {
return read_clusters_without_cut(n_clusters); return read_clusters_without_cut(n_clusters);
} }
} }
ClusterVector<int32_t> ClusterFile::read_clusters_without_cut(size_t n_clusters) { ClusterVector<int32_t>
ClusterFile::read_clusters_without_cut(size_t n_clusters) {
if (m_mode != "r") { if (m_mode != "r") {
throw std::runtime_error("File not opened for reading"); throw std::runtime_error("File not opened for reading");
} }
ClusterVector<int32_t> clusters(3,3, n_clusters); ClusterVector<int32_t> clusters(3, 3, n_clusters);
int32_t iframe = 0; // frame number needs to be 4 bytes! int32_t iframe = 0; // frame number needs to be 4 bytes!
size_t nph_read = 0; size_t nph_read = 0;
@ -119,7 +119,7 @@ ClusterVector<int32_t> ClusterFile::read_clusters_without_cut(size_t n_clusters)
} else { } else {
nn = nph; nn = nph;
} }
nph_read += fread((buf + nph_read*clusters.item_size()), nph_read += fread((buf + nph_read * clusters.item_size()),
clusters.item_size(), nn, fp); clusters.item_size(), nn, fp);
m_num_left = nph - nn; // write back the number of photons left m_num_left = nph - nn; // write back the number of photons left
} }
@ -135,7 +135,7 @@ ClusterVector<int32_t> ClusterFile::read_clusters_without_cut(size_t n_clusters)
else else
nn = nph; nn = nph;
nph_read += fread((buf + nph_read*clusters.item_size()), nph_read += fread((buf + nph_read * clusters.item_size()),
clusters.item_size(), nn, fp); clusters.item_size(), nn, fp);
m_num_left = nph - nn; m_num_left = nph - nn;
} }
@ -147,22 +147,22 @@ ClusterVector<int32_t> ClusterFile::read_clusters_without_cut(size_t n_clusters)
// Resize the vector to the number of clusters. // Resize the vector to the number of clusters.
// No new allocation, only change bounds. // No new allocation, only change bounds.
clusters.resize(nph_read); clusters.resize(nph_read);
if(m_gain_map) if (m_gain_map)
clusters.apply_gain_map(m_gain_map->view()); clusters.apply_gain_map(m_gain_map->view());
return clusters; return clusters;
} }
ClusterVector<int32_t> ClusterFile::read_clusters_with_cut(size_t n_clusters) { ClusterVector<int32_t> ClusterFile::read_clusters_with_cut(size_t n_clusters) {
ClusterVector<int32_t> clusters(3,3); ClusterVector<int32_t> clusters(3, 3);
clusters.reserve(n_clusters); clusters.reserve(n_clusters);
// if there are photons left from previous frame read them first // if there are photons left from previous frame read them first
if (m_num_left) { if (m_num_left) {
while(m_num_left && clusters.size() < n_clusters){ while (m_num_left && clusters.size() < n_clusters) {
Cluster3x3 c = read_one_cluster(); Cluster3x3 c = read_one_cluster();
if(is_selected(c)){ if (is_selected(c)) {
clusters.push_back(c.x, c.y, reinterpret_cast<std::byte*>(c.data)); clusters.push_back(c.x, c.y,
reinterpret_cast<std::byte *>(c.data));
} }
} }
} }
@ -172,17 +172,21 @@ ClusterVector<int32_t> ClusterFile::read_clusters_with_cut(size_t n_clusters) {
if (clusters.size() < n_clusters) { if (clusters.size() < n_clusters) {
// sanity check // sanity check
if (m_num_left) { if (m_num_left) {
throw std::runtime_error(LOCATION + "Entered second loop with clusters left\n"); throw std::runtime_error(
LOCATION + "Entered second loop with clusters left\n");
} }
int32_t frame_number = 0; // frame number needs to be 4 bytes! int32_t frame_number = 0; // frame number needs to be 4 bytes!
while (fread(&frame_number, sizeof(frame_number), 1, fp)) { while (fread(&frame_number, sizeof(frame_number), 1, fp)) {
if (fread(&m_num_left, sizeof(m_num_left), 1, fp)) { if (fread(&m_num_left, sizeof(m_num_left), 1, fp)) {
clusters.set_frame_number(frame_number); //cluster vector will hold the last frame number clusters.set_frame_number(
while(m_num_left && clusters.size() < n_clusters){ frame_number); // cluster vector will hold the last frame
// number
while (m_num_left && clusters.size() < n_clusters) {
Cluster3x3 c = read_one_cluster(); Cluster3x3 c = read_one_cluster();
if(is_selected(c)){ if (is_selected(c)) {
clusters.push_back(c.x, c.y, reinterpret_cast<std::byte*>(c.data)); clusters.push_back(
c.x, c.y, reinterpret_cast<std::byte *>(c.data));
} }
} }
} }
@ -191,15 +195,14 @@ ClusterVector<int32_t> ClusterFile::read_clusters_with_cut(size_t n_clusters) {
if (clusters.size() >= n_clusters) if (clusters.size() >= n_clusters)
break; break;
} }
} }
if(m_gain_map) if (m_gain_map)
clusters.apply_gain_map(m_gain_map->view()); clusters.apply_gain_map(m_gain_map->view());
return clusters; return clusters;
} }
Cluster3x3 ClusterFile::read_one_cluster(){ Cluster3x3 ClusterFile::read_one_cluster() {
Cluster3x3 c; Cluster3x3 c;
auto rc = fread(&c, sizeof(c), 1, fp); auto rc = fread(&c, sizeof(c), 1, fp);
if (rc != 1) { if (rc != 1) {
@ -209,13 +212,13 @@ Cluster3x3 ClusterFile::read_one_cluster(){
return c; return c;
} }
ClusterVector<int32_t> ClusterFile::read_frame(){ ClusterVector<int32_t> ClusterFile::read_frame() {
if (m_mode != "r") { if (m_mode != "r") {
throw std::runtime_error(LOCATION + "File not opened for reading"); throw std::runtime_error(LOCATION + "File not opened for reading");
} }
if (m_noise_map || m_roi){ if (m_noise_map || m_roi) {
return read_frame_with_cut(); return read_frame_with_cut();
}else{ } else {
return read_frame_without_cut(); return read_frame_without_cut();
} }
} }
@ -235,7 +238,8 @@ ClusterVector<int32_t> ClusterFile::read_frame_without_cut() {
int32_t n_clusters; // Saved as 32bit integer in the cluster file int32_t n_clusters; // Saved as 32bit integer in the cluster file
if (fread(&n_clusters, sizeof(n_clusters), 1, fp) != 1) { if (fread(&n_clusters, sizeof(n_clusters), 1, fp) != 1) {
throw std::runtime_error(LOCATION + "Could not read number of clusters"); throw std::runtime_error(LOCATION +
"Could not read number of clusters");
} }
ClusterVector<int32_t> clusters(3, 3, n_clusters); ClusterVector<int32_t> clusters(3, 3, n_clusters);
@ -264,7 +268,6 @@ ClusterVector<int32_t> ClusterFile::read_frame_with_cut() {
throw std::runtime_error("Could not read frame number"); throw std::runtime_error("Could not read frame number");
} }
if (fread(&m_num_left, sizeof(m_num_left), 1, fp) != 1) { if (fread(&m_num_left, sizeof(m_num_left), 1, fp) != 1) {
throw std::runtime_error("Could not read number of clusters"); throw std::runtime_error("Could not read number of clusters");
} }
@ -272,10 +275,10 @@ ClusterVector<int32_t> ClusterFile::read_frame_with_cut() {
ClusterVector<int32_t> clusters(3, 3); ClusterVector<int32_t> clusters(3, 3);
clusters.reserve(m_num_left); clusters.reserve(m_num_left);
clusters.set_frame_number(frame_number); clusters.set_frame_number(frame_number);
while(m_num_left){ while (m_num_left) {
Cluster3x3 c = read_one_cluster(); Cluster3x3 c = read_one_cluster();
if(is_selected(c)){ if (is_selected(c)) {
clusters.push_back(c.x, c.y, reinterpret_cast<std::byte*>(c.data)); clusters.push_back(c.x, c.y, reinterpret_cast<std::byte *>(c.data));
} }
} }
if (m_gain_map) if (m_gain_map)
@ -283,31 +286,30 @@ ClusterVector<int32_t> ClusterFile::read_frame_with_cut() {
return clusters; return clusters;
} }
bool ClusterFile::is_selected(Cluster3x3 &cl) { bool ClusterFile::is_selected(Cluster3x3 &cl) {
//Should fail fast // Should fail fast
if (m_roi) { if (m_roi) {
if (!(m_roi->contains(cl.x, cl.y))) { if (!(m_roi->contains(cl.x, cl.y))) {
return false; return false;
} }
} }
if (m_noise_map){ if (m_noise_map) {
int32_t sum_1x1 = cl.data[4]; // central pixel int32_t sum_1x1 = cl.data[4]; // central pixel
int32_t sum_2x2 = cl.sum_2x2(); // highest sum of 2x2 subclusters int32_t sum_2x2 = cl.sum_2x2(); // highest sum of 2x2 subclusters
int32_t sum_3x3 = cl.sum(); // sum of all pixels int32_t sum_3x3 = cl.sum(); // sum of all pixels
auto noise = (*m_noise_map)(cl.y, cl.x); //TODO! check if this is correct auto noise =
(*m_noise_map)(cl.y, cl.x); // TODO! check if this is correct
if (sum_1x1 <= noise || sum_2x2 <= 2 * noise || sum_3x3 <= 3 * noise) { if (sum_1x1 <= noise || sum_2x2 <= 2 * noise || sum_3x3 <= 3 * noise) {
return false; return false;
} }
} }
//we passed all checks // we passed all checks
return true; return true;
} }
NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters) { NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters) {
//TOTO! make work with 2x2 clusters // TOTO! make work with 2x2 clusters
NDArray<double, 2> eta2({static_cast<int64_t>(clusters.size()), 2}); NDArray<double, 2> eta2({static_cast<int64_t>(clusters.size()), 2});
if (clusters.cluster_size_x() == 3 || clusters.cluster_size_y() == 3) { if (clusters.cluster_size_x() == 3 || clusters.cluster_size_y() == 3) {
@ -316,13 +318,14 @@ NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters) {
eta2(i, 0) = e.x; eta2(i, 0) = e.x;
eta2(i, 1) = e.y; eta2(i, 1) = e.y;
} }
}else if(clusters.cluster_size_x() == 2 || clusters.cluster_size_y() == 2){ } else if (clusters.cluster_size_x() == 2 ||
clusters.cluster_size_y() == 2) {
for (size_t i = 0; i < clusters.size(); i++) { for (size_t i = 0; i < clusters.size(); i++) {
auto e = calculate_eta2(clusters.at<Cluster2x2>(i)); auto e = calculate_eta2(clusters.at<Cluster2x2>(i));
eta2(i, 0) = e.x; eta2(i, 0) = e.x;
eta2(i, 1) = e.y; eta2(i, 1) = e.y;
} }
}else{ } else {
throw std::runtime_error("Only 3x3 and 2x2 clusters are supported"); throw std::runtime_error("Only 3x3 and 2x2 clusters are supported");
} }
@ -330,9 +333,9 @@ NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters) {
} }
/** /**
* @brief Calculate the eta2 values for a 3x3 cluster and return them in a Eta2 struct * @brief Calculate the eta2 values for a 3x3 cluster and return them in a Eta2
* containing etay, etax and the corner of the cluster. * struct containing etay, etax and the corner of the cluster.
*/ */
Eta2 calculate_eta2(Cluster3x3 &cl) { Eta2 calculate_eta2(Cluster3x3 &cl) {
Eta2 eta{}; Eta2 eta{};
@ -347,56 +350,46 @@ Eta2 calculate_eta2(Cluster3x3 &cl) {
switch (c) { switch (c) {
case cBottomLeft: case cBottomLeft:
if ((cl.data[3] + cl.data[4]) != 0) if ((cl.data[3] + cl.data[4]) != 0)
eta.x = eta.x = static_cast<double>(cl.data[4]) / (cl.data[3] + cl.data[4]);
static_cast<double>(cl.data[4]) / (cl.data[3] + cl.data[4]);
if ((cl.data[1] + cl.data[4]) != 0) if ((cl.data[1] + cl.data[4]) != 0)
eta.y = eta.y = static_cast<double>(cl.data[4]) / (cl.data[1] + cl.data[4]);
static_cast<double>(cl.data[4]) / (cl.data[1] + cl.data[4]);
eta.c = cBottomLeft; eta.c = cBottomLeft;
break; break;
case cBottomRight: case cBottomRight:
if ((cl.data[2] + cl.data[5]) != 0) if ((cl.data[2] + cl.data[5]) != 0)
eta.x = eta.x = static_cast<double>(cl.data[5]) / (cl.data[4] + cl.data[5]);
static_cast<double>(cl.data[5]) / (cl.data[4] + cl.data[5]);
if ((cl.data[1] + cl.data[4]) != 0) if ((cl.data[1] + cl.data[4]) != 0)
eta.y = eta.y = static_cast<double>(cl.data[4]) / (cl.data[1] + cl.data[4]);
static_cast<double>(cl.data[4]) / (cl.data[1] + cl.data[4]);
eta.c = cBottomRight; eta.c = cBottomRight;
break; break;
case cTopLeft: case cTopLeft:
if ((cl.data[7] + cl.data[4]) != 0) if ((cl.data[7] + cl.data[4]) != 0)
eta.x = eta.x = static_cast<double>(cl.data[4]) / (cl.data[3] + cl.data[4]);
static_cast<double>(cl.data[4]) / (cl.data[3] + cl.data[4]);
if ((cl.data[7] + cl.data[4]) != 0) if ((cl.data[7] + cl.data[4]) != 0)
eta.y = eta.y = static_cast<double>(cl.data[7]) / (cl.data[7] + cl.data[4]);
static_cast<double>(cl.data[7]) / (cl.data[7] + cl.data[4]);
eta.c = cTopLeft; eta.c = cTopLeft;
break; break;
case cTopRight: case cTopRight:
if ((cl.data[5] + cl.data[4]) != 0) if ((cl.data[5] + cl.data[4]) != 0)
eta.x = eta.x = static_cast<double>(cl.data[5]) / (cl.data[5] + cl.data[4]);
static_cast<double>(cl.data[5]) / (cl.data[5] + cl.data[4]);
if ((cl.data[7] + cl.data[4]) != 0) if ((cl.data[7] + cl.data[4]) != 0)
eta.y = eta.y = static_cast<double>(cl.data[7]) / (cl.data[7] + cl.data[4]);
static_cast<double>(cl.data[7]) / (cl.data[7] + cl.data[4]);
eta.c = cTopRight; eta.c = cTopRight;
break; break;
// no default to allow compiler to warn about missing cases // no default to allow compiler to warn about missing cases
} }
return eta; return eta;
} }
Eta2 calculate_eta2(Cluster2x2 &cl) { Eta2 calculate_eta2(Cluster2x2 &cl) {
Eta2 eta{}; Eta2 eta{};
if ((cl.data[0] + cl.data[1]) != 0) if ((cl.data[0] + cl.data[1]) != 0)
eta.x = static_cast<double>(cl.data[1]) / (cl.data[0] + cl.data[1]); eta.x = static_cast<double>(cl.data[1]) / (cl.data[0] + cl.data[1]);
if ((cl.data[0] + cl.data[2]) != 0) if ((cl.data[0] + cl.data[2]) != 0)
eta.y = static_cast<double>(cl.data[2]) / (cl.data[0] + cl.data[2]); eta.y = static_cast<double>(cl.data[2]) / (cl.data[0] + cl.data[2]);
eta.sum = cl.data[0] + cl.data[1] + cl.data[2]+ cl.data[3]; eta.sum = cl.data[0] + cl.data[1] + cl.data[2] + cl.data[3];
eta.c = cBottomLeft; //TODO! This is not correct, but need to put something eta.c = cBottomLeft; // TODO! This is not correct, but need to put something
return eta; return eta;
} }
} // namespace aare } // namespace aare

View File

@ -1,35 +1,37 @@
#include "aare/ClusterFile.hpp" #include "aare/ClusterFile.hpp"
#include "test_config.hpp" #include "test_config.hpp"
#include "aare/defs.hpp" #include "aare/defs.hpp"
#include <algorithm>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <filesystem> #include <filesystem>
using aare::Cluster;
using aare::ClusterFile; using aare::ClusterFile;
using aare::ClusterVector;
TEST_CASE("Read one frame from a cluster file", "[.files]") {
TEST_CASE("Read one frame from a a cluster file", "[.files]") { // We know that the frame has 97 clusters
//We know that the frame has 97 clusters
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust"; auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
ClusterFile f(fpath); ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
auto clusters = f.read_frame(); auto clusters = f.read_frame();
REQUIRE(clusters.size() == 97); CHECK(clusters.size() == 97);
REQUIRE(clusters.frame_number() == 135); CHECK(clusters.frame_number() == 135);
CHECK(clusters[0].x == 1);
CHECK(clusters[0].y == 200);
int32_t expected_cluster_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
CHECK(std::equal(std::begin(clusters[0].data), std::end(clusters[0].data),
std::begin(expected_cluster_data)));
} }
TEST_CASE("Read one frame using ROI", "[.files]") { TEST_CASE("Read one frame using ROI", "[.files]") {
//We know that the frame has 97 clusters // We know that the frame has 97 clusters
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust"; auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
ClusterFile f(fpath); ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
aare::ROI roi; aare::ROI roi;
roi.xmin = 0; roi.xmin = 0;
roi.xmax = 50; roi.xmax = 50;
@ -40,45 +42,306 @@ TEST_CASE("Read one frame using ROI", "[.files]") {
REQUIRE(clusters.size() == 49); REQUIRE(clusters.size() == 49);
REQUIRE(clusters.frame_number() == 135); REQUIRE(clusters.frame_number() == 135);
//Check that all clusters are within the ROI // Check that all clusters are within the ROI
for (size_t i = 0; i < clusters.size(); i++) { for (size_t i = 0; i < clusters.size(); i++) {
auto c = clusters.at<aare::Cluster3x3>(i); auto c = clusters[i];
REQUIRE(c.x >= roi.xmin); REQUIRE(c.x >= roi.xmin);
REQUIRE(c.x <= roi.xmax); REQUIRE(c.x <= roi.xmax);
REQUIRE(c.y >= roi.ymin); REQUIRE(c.y >= roi.ymin);
REQUIRE(c.y <= roi.ymax); REQUIRE(c.y <= roi.ymax);
} }
CHECK(clusters[0].x == 1);
CHECK(clusters[0].y == 200);
int32_t expected_cluster_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
CHECK(std::equal(std::begin(clusters[0].data), std::end(clusters[0].data),
std::begin(expected_cluster_data)));
} }
TEST_CASE("Read clusters from single frame file", "[.files]") { TEST_CASE("Read clusters from single frame file", "[.files]") {
// frame_number, num_clusters [135] 97
// [ 1 200] [0 1 2 3 4 5 6 7 8]
// [ 2 201] [ 9 10 11 12 13 14 15 16 17]
// [ 3 202] [18 19 20 21 22 23 24 25 26]
// [ 4 203] [27 28 29 30 31 32 33 34 35]
// [ 5 204] [36 37 38 39 40 41 42 43 44]
// [ 6 205] [45 46 47 48 49 50 51 52 53]
// [ 7 206] [54 55 56 57 58 59 60 61 62]
// [ 8 207] [63 64 65 66 67 68 69 70 71]
// [ 9 208] [72 73 74 75 76 77 78 79 80]
// [ 10 209] [81 82 83 84 85 86 87 88 89]
// [ 11 210] [90 91 92 93 94 95 96 97 98]
// [ 12 211] [ 99 100 101 102 103 104 105 106 107]
// [ 13 212] [108 109 110 111 112 113 114 115 116]
// [ 14 213] [117 118 119 120 121 122 123 124 125]
// [ 15 214] [126 127 128 129 130 131 132 133 134]
// [ 16 215] [135 136 137 138 139 140 141 142 143]
// [ 17 216] [144 145 146 147 148 149 150 151 152]
// [ 18 217] [153 154 155 156 157 158 159 160 161]
// [ 19 218] [162 163 164 165 166 167 168 169 170]
// [ 20 219] [171 172 173 174 175 176 177 178 179]
// [ 21 220] [180 181 182 183 184 185 186 187 188]
// [ 22 221] [189 190 191 192 193 194 195 196 197]
// [ 23 222] [198 199 200 201 202 203 204 205 206]
// [ 24 223] [207 208 209 210 211 212 213 214 215]
// [ 25 224] [216 217 218 219 220 221 222 223 224]
// [ 26 225] [225 226 227 228 229 230 231 232 233]
// [ 27 226] [234 235 236 237 238 239 240 241 242]
// [ 28 227] [243 244 245 246 247 248 249 250 251]
// [ 29 228] [252 253 254 255 256 257 258 259 260]
// [ 30 229] [261 262 263 264 265 266 267 268 269]
// [ 31 230] [270 271 272 273 274 275 276 277 278]
// [ 32 231] [279 280 281 282 283 284 285 286 287]
// [ 33 232] [288 289 290 291 292 293 294 295 296]
// [ 34 233] [297 298 299 300 301 302 303 304 305]
// [ 35 234] [306 307 308 309 310 311 312 313 314]
// [ 36 235] [315 316 317 318 319 320 321 322 323]
// [ 37 236] [324 325 326 327 328 329 330 331 332]
// [ 38 237] [333 334 335 336 337 338 339 340 341]
// [ 39 238] [342 343 344 345 346 347 348 349 350]
// [ 40 239] [351 352 353 354 355 356 357 358 359]
// [ 41 240] [360 361 362 363 364 365 366 367 368]
// [ 42 241] [369 370 371 372 373 374 375 376 377]
// [ 43 242] [378 379 380 381 382 383 384 385 386]
// [ 44 243] [387 388 389 390 391 392 393 394 395]
// [ 45 244] [396 397 398 399 400 401 402 403 404]
// [ 46 245] [405 406 407 408 409 410 411 412 413]
// [ 47 246] [414 415 416 417 418 419 420 421 422]
// [ 48 247] [423 424 425 426 427 428 429 430 431]
// [ 49 248] [432 433 434 435 436 437 438 439 440]
// [ 50 249] [441 442 443 444 445 446 447 448 449]
// [ 51 250] [450 451 452 453 454 455 456 457 458]
// [ 52 251] [459 460 461 462 463 464 465 466 467]
// [ 53 252] [468 469 470 471 472 473 474 475 476]
// [ 54 253] [477 478 479 480 481 482 483 484 485]
// [ 55 254] [486 487 488 489 490 491 492 493 494]
// [ 56 255] [495 496 497 498 499 500 501 502 503]
// [ 57 256] [504 505 506 507 508 509 510 511 512]
// [ 58 257] [513 514 515 516 517 518 519 520 521]
// [ 59 258] [522 523 524 525 526 527 528 529 530]
// [ 60 259] [531 532 533 534 535 536 537 538 539]
// [ 61 260] [540 541 542 543 544 545 546 547 548]
// [ 62 261] [549 550 551 552 553 554 555 556 557]
// [ 63 262] [558 559 560 561 562 563 564 565 566]
// [ 64 263] [567 568 569 570 571 572 573 574 575]
// [ 65 264] [576 577 578 579 580 581 582 583 584]
// [ 66 265] [585 586 587 588 589 590 591 592 593]
// [ 67 266] [594 595 596 597 598 599 600 601 602]
// [ 68 267] [603 604 605 606 607 608 609 610 611]
// [ 69 268] [612 613 614 615 616 617 618 619 620]
// [ 70 269] [621 622 623 624 625 626 627 628 629]
// [ 71 270] [630 631 632 633 634 635 636 637 638]
// [ 72 271] [639 640 641 642 643 644 645 646 647]
// [ 73 272] [648 649 650 651 652 653 654 655 656]
// [ 74 273] [657 658 659 660 661 662 663 664 665]
// [ 75 274] [666 667 668 669 670 671 672 673 674]
// [ 76 275] [675 676 677 678 679 680 681 682 683]
// [ 77 276] [684 685 686 687 688 689 690 691 692]
// [ 78 277] [693 694 695 696 697 698 699 700 701]
// [ 79 278] [702 703 704 705 706 707 708 709 710]
// [ 80 279] [711 712 713 714 715 716 717 718 719]
// [ 81 280] [720 721 722 723 724 725 726 727 728]
// [ 82 281] [729 730 731 732 733 734 735 736 737]
// [ 83 282] [738 739 740 741 742 743 744 745 746]
// [ 84 283] [747 748 749 750 751 752 753 754 755]
// [ 85 284] [756 757 758 759 760 761 762 763 764]
// [ 86 285] [765 766 767 768 769 770 771 772 773]
// [ 87 286] [774 775 776 777 778 779 780 781 782]
// [ 88 287] [783 784 785 786 787 788 789 790 791]
// [ 89 288] [792 793 794 795 796 797 798 799 800]
// [ 90 289] [801 802 803 804 805 806 807 808 809]
// [ 91 290] [810 811 812 813 814 815 816 817 818]
// [ 92 291] [819 820 821 822 823 824 825 826 827]
// [ 93 292] [828 829 830 831 832 833 834 835 836]
// [ 94 293] [837 838 839 840 841 842 843 844 845]
// [ 95 294] [846 847 848 849 850 851 852 853 854]
// [ 96 295] [855 856 857 858 859 860 861 862 863]
// [ 97 296] [864 865 866 867 868 869 870 871 872]
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust"; auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
SECTION("Read fewer clusters than available") { SECTION("Read fewer clusters than available") {
ClusterFile f(fpath); ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
auto clusters = f.read_clusters(50); auto clusters = f.read_clusters(50);
REQUIRE(clusters.size() == 50); REQUIRE(clusters.size() == 50);
REQUIRE(clusters.frame_number() == 135); REQUIRE(clusters.frame_number() == 135);
int32_t expected_cluster_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
REQUIRE(clusters[0].x == 1);
REQUIRE(clusters[0].y == 200);
CHECK(std::equal(std::begin(clusters[0].data),
std::end(clusters[0].data),
std::begin(expected_cluster_data)));
} }
SECTION("Read more clusters than available") { SECTION("Read more clusters than available") {
ClusterFile f(fpath); ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
// 100 is the maximum number of clusters read // 100 is the maximum number of clusters read
auto clusters = f.read_clusters(100); auto clusters = f.read_clusters(100);
REQUIRE(clusters.size() == 97); REQUIRE(clusters.size() == 97);
REQUIRE(clusters.frame_number() == 135); REQUIRE(clusters.frame_number() == 135);
int32_t expected_cluster_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
REQUIRE(clusters[0].x == 1);
REQUIRE(clusters[0].y == 200);
CHECK(std::equal(std::begin(clusters[0].data),
std::end(clusters[0].data),
std::begin(expected_cluster_data)));
} }
SECTION("Read all clusters") { SECTION("Read all clusters") {
ClusterFile f(fpath); ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
auto clusters = f.read_clusters(97); auto clusters = f.read_clusters(97);
REQUIRE(clusters.size() == 97); REQUIRE(clusters.size() == 97);
REQUIRE(clusters.frame_number() == 135); REQUIRE(clusters.frame_number() == 135);
REQUIRE(clusters[0].x == 1);
REQUIRE(clusters[0].y == 200);
int32_t expected_cluster_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
CHECK(std::equal(std::begin(clusters[0].data),
std::end(clusters[0].data),
std::begin(expected_cluster_data)));
}
}
TEST_CASE("Read clusters from single frame file with ROI", "[.files]") {
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath));
ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
aare::ROI roi;
roi.xmin = 0;
roi.xmax = 50;
roi.ymin = 200;
roi.ymax = 249;
f.set_roi(roi);
auto clusters = f.read_clusters(10);
CHECK(clusters.size() == 10);
CHECK(clusters.frame_number() == 135);
CHECK(clusters[0].x == 1);
CHECK(clusters[0].y == 200);
int32_t expected_cluster_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
CHECK(std::equal(std::begin(clusters[0].data), std::end(clusters[0].data),
std::begin(expected_cluster_data)));
}
TEST_CASE("Read cluster from multiple frame file", "[.files]") {
using ClusterType = Cluster<double, 2, 2>;
auto fpath =
test_data_path() / "clust" / "Two_frames_2x2double_test_clusters.clust";
REQUIRE(std::filesystem::exists(fpath));
// Two_frames_2x2double_test_clusters.clust
// frame number, num_clusters 0, 4
//[10, 20], {0. ,0., 0., 0.}
//[11, 30], {1., 1., 1., 1.}
//[12, 40], {2., 2., 2., 2.}
//[13, 50], {3., 3., 3., 3.}
// 1,4
//[10, 20], {4., 4., 4., 4.}
//[11, 30], {5., 5., 5., 5.}
//[12, 40], {6., 6., 6., 6.}
//[13, 50], {7., 7., 7., 7.}
SECTION("Read clusters from both frames") {
ClusterFile<ClusterType> f(fpath);
auto clusters = f.read_clusters(2);
REQUIRE(clusters.size() == 2);
REQUIRE(clusters.frame_number() == 0);
auto clusters1 = f.read_clusters(3);
REQUIRE(clusters1.size() == 3);
REQUIRE(clusters1.frame_number() == 1);
} }
SECTION("Read all clusters") {
ClusterFile<ClusterType> f(fpath);
auto clusters = f.read_clusters(8);
REQUIRE(clusters.size() == 8);
REQUIRE(clusters.frame_number() == 1);
}
SECTION("Read clusters from one frame") {
ClusterFile<ClusterType> f(fpath);
auto clusters = f.read_clusters(2);
REQUIRE(clusters.size() == 2);
REQUIRE(clusters.frame_number() == 0);
auto clusters1 = f.read_clusters(1);
REQUIRE(clusters1.size() == 1);
REQUIRE(clusters1.frame_number() == 0);
}
}
TEST_CASE("Write cluster with potential padding", "[.files][.ClusterFile]") {
using ClusterType = Cluster<double, 3, 3>;
REQUIRE(std::filesystem::exists(test_data_path() / "clust"));
auto fpath = test_data_path() / "clust" / "single_frame_2_clusters.clust";
ClusterFile<ClusterType> file(fpath, 1000, "w");
ClusterVector<ClusterType> clustervec(2);
int16_t coordinate = 5;
clustervec.push_back(ClusterType{
coordinate, coordinate, {0., 0., 0., 0., 0., 0., 0., 0., 0.}});
clustervec.push_back(ClusterType{
coordinate, coordinate, {0., 0., 0., 0., 0., 0., 0., 0., 0.}});
file.write_frame(clustervec);
file.close();
file.open("r");
auto read_cluster_vector = file.read_frame();
CHECK(read_cluster_vector.size() == 2);
CHECK(read_cluster_vector.frame_number() == 0);
CHECK(read_cluster_vector[0].x == clustervec[0].x);
CHECK(read_cluster_vector[0].y == clustervec[0].y);
CHECK(std::equal(
clustervec[0].data.begin(), clustervec[0].data.end(),
read_cluster_vector[0].data.begin(), [](double a, double b) {
return std::abs(a - b) < std::numeric_limits<double>::epsilon();
}));
CHECK(read_cluster_vector[1].x == clustervec[1].x);
CHECK(read_cluster_vector[1].y == clustervec[1].y);
CHECK(std::equal(
clustervec[1].data.begin(), clustervec[1].data.end(),
read_cluster_vector[1].data.begin(), [](double a, double b) {
return std::abs(a - b) < std::numeric_limits<double>::epsilon();
}));
}
TEST_CASE("Read frame and modify cluster data", "[.files][.ClusterFile]") {
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath));
ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
auto clusters = f.read_frame();
CHECK(clusters.size() == 97);
CHECK(clusters.frame_number() == 135);
int32_t expected_cluster_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
clusters.push_back(
Cluster<int32_t, 3, 3>{0, 0, {0, 1, 2, 3, 4, 5, 6, 7, 8}});
CHECK(clusters.size() == 98);
CHECK(clusters[0].x == 1);
CHECK(clusters[0].y == 200);
CHECK(std::equal(std::begin(clusters[0].data), std::end(clusters[0].data),
std::begin(expected_cluster_data)));
} }

View File

@ -1,19 +1,18 @@
#include "aare/ClusterFinder.hpp" #include "aare/ClusterFinder.hpp"
#include "aare/Pedestal.hpp" #include "aare/Pedestal.hpp"
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <chrono> #include <chrono>
#include <random> #include <random>
using namespace aare; using namespace aare;
//TODO! Find a way to test the cluster finder // TODO! Find a way to test the cluster finder
// class ClusterFinderUnitTest : public ClusterFinder { // class ClusterFinderUnitTest : public ClusterFinder {
// public: // public:
// ClusterFinderUnitTest(int cluster_sizeX, int cluster_sizeY, double nSigma = 5.0, double threshold = 0.0) // ClusterFinderUnitTest(int cluster_sizeX, int cluster_sizeY, double nSigma
// = 5.0, double threshold = 0.0)
// : ClusterFinder(cluster_sizeX, cluster_sizeY, nSigma, threshold) {} // : ClusterFinder(cluster_sizeX, cluster_sizeY, nSigma, threshold) {}
// double get_c2() { return c2; } // double get_c2() { return c2; }
// double get_c3() { return c3; } // double get_c3() { return c3; }
@ -37,8 +36,8 @@ using namespace aare;
// REQUIRE_THAT(cf.get_c3(), Catch::Matchers::WithinRel(c3, 1e-9)); // REQUIRE_THAT(cf.get_c3(), Catch::Matchers::WithinRel(c3, 1e-9));
// } // }
TEST_CASE("Construct a cluster finder"){ TEST_CASE("Construct a cluster finder") {
ClusterFinder clusterFinder({400,400}, {3,3}); ClusterFinder clusterFinder({400, 400});
// REQUIRE(clusterFinder.get_cluster_sizeX() == 3); // REQUIRE(clusterFinder.get_cluster_sizeX() == 3);
// REQUIRE(clusterFinder.get_cluster_sizeY() == 3); // REQUIRE(clusterFinder.get_cluster_sizeY() == 3);
// REQUIRE(clusterFinder.get_threshold() == 1); // REQUIRE(clusterFinder.get_threshold() == 1);
@ -49,16 +48,17 @@ TEST_CASE("Construct a cluster finder"){
// aare::Pedestal pedestal(10, 10, 5); // aare::Pedestal pedestal(10, 10, 5);
// NDArray<double, 2> frame({10, 10}); // NDArray<double, 2> frame({10, 10});
// frame = 0; // frame = 0;
// ClusterFinder clusterFinder(3, 3, 1, 1); // 3x3 cluster, 1 nSigma, 1 threshold // ClusterFinder clusterFinder(3, 3, 1, 1); // 3x3 cluster, 1 nSigma, 1
// threshold
// auto clusters = clusterFinder.find_clusters_without_threshold(frame.span(), pedestal); // auto clusters =
// clusterFinder.find_clusters_without_threshold(frame.span(), pedestal);
// REQUIRE(clusters.size() == 0); // REQUIRE(clusters.size() == 0);
// frame(5, 5) = 10; // frame(5, 5) = 10;
// clusters = clusterFinder.find_clusters_without_threshold(frame.span(), pedestal); // clusters = clusterFinder.find_clusters_without_threshold(frame.span(),
// REQUIRE(clusters.size() == 1); // pedestal); REQUIRE(clusters.size() == 1); REQUIRE(clusters[0].x == 5);
// REQUIRE(clusters[0].x == 5);
// REQUIRE(clusters[0].y == 5); // REQUIRE(clusters[0].y == 5);
// for (int i = 0; i < 3; i++) { // for (int i = 0; i < 3; i++) {
// for (int j = 0; j < 3; j++) { // for (int j = 0; j < 3; j++) {

View File

@ -0,0 +1,101 @@
#include "aare/ClusterFinderMT.hpp"
#include "aare/Cluster.hpp"
#include "aare/ClusterCollector.hpp"
#include "aare/File.hpp"
#include "test_config.hpp"
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#include <memory>
using namespace aare;
// wrapper function to access private member variables for testing
template <typename ClusterType, typename FRAME_TYPE = uint16_t,
typename PEDESTAL_TYPE = double>
class ClusterFinderMTWrapper
: public ClusterFinderMT<ClusterType, FRAME_TYPE, PEDESTAL_TYPE> {
public:
ClusterFinderMTWrapper(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0,
size_t capacity = 2000, size_t n_threads = 3)
: ClusterFinderMT<ClusterType, FRAME_TYPE, PEDESTAL_TYPE>(
image_size, nSigma, capacity, n_threads) {}
size_t get_m_input_queues_size() const {
return this->m_input_queues.size();
}
size_t get_m_output_queues_size() const {
return this->m_output_queues.size();
}
size_t get_m_cluster_finders_size() const {
return this->m_cluster_finders.size();
}
bool m_output_queues_are_empty() const {
for (auto &queue : this->m_output_queues) {
if (!queue->isEmpty())
return false;
}
return true;
}
bool m_input_queues_are_empty() const {
for (auto &queue : this->m_input_queues) {
if (!queue->isEmpty())
return false;
}
return true;
}
bool m_sink_is_empty() const { return this->m_sink.isEmpty(); }
size_t m_sink_size() const { return this->m_sink.sizeGuess(); }
};
TEST_CASE("multithreaded cluster finder", "[.files][.ClusterFinder]") {
auto fpath =
test_data_path() / "clust/Moench03new/cu_half_speed_master_4.json";
REQUIRE(std::filesystem::exists(fpath));
File file(fpath);
size_t n_threads = 2;
size_t n_frames_pd = 10;
using ClusterType = Cluster<int32_t, 3, 3>;
ClusterFinderMTWrapper<ClusterType> cf(
{static_cast<int64_t>(file.rows()), static_cast<int64_t>(file.cols())},
5, 2000, n_threads); // no idea what frame type is!!! default uint16_t
CHECK(cf.get_m_input_queues_size() == n_threads);
CHECK(cf.get_m_output_queues_size() == n_threads);
CHECK(cf.get_m_cluster_finders_size() == n_threads);
CHECK(cf.m_output_queues_are_empty() == true);
CHECK(cf.m_input_queues_are_empty() == true);
for (size_t i = 0; i < n_frames_pd; ++i) {
cf.find_clusters(file.read_frame().view<uint16_t>());
}
cf.stop();
CHECK(cf.m_output_queues_are_empty() == true);
CHECK(cf.m_input_queues_are_empty() == true);
CHECK(cf.m_sink_size() == n_frames_pd);
ClusterCollector<ClusterType> clustercollector(&cf);
clustercollector.stop();
CHECK(cf.m_sink_size() == 0);
auto clustervec = clustercollector.steal_clusters();
// CHECK(clustervec.size() == ) //dont know how many clusters to expect
}

View File

@ -1,21 +1,52 @@
#include <cstdint>
#include "aare/ClusterVector.hpp" #include "aare/ClusterVector.hpp"
#include <cstdint>
#include <catch2/matchers/catch_matchers_floating_point.hpp> #include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
using aare::Cluster;
using aare::ClusterVector; using aare::ClusterVector;
struct Cluster_i2x2 { TEST_CASE("item_size return the size of the cluster stored") {
int16_t x; using C1 = Cluster<int32_t, 2, 2>;
int16_t y; ClusterVector<C1> cv(4);
int32_t data[4]; CHECK(cv.item_size() == sizeof(C1));
};
TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read") { // Sanity check
// 2*2*4 = 16 bytes of data for the cluster
// 2*2 = 4 bytes for the x and y coordinates
REQUIRE(cv.item_size() == 20);
using C2 = Cluster<int32_t, 3, 3>;
ClusterVector<C2> cv2(4);
CHECK(cv2.item_size() == sizeof(C2));
ClusterVector<int32_t> cv(2, 2, 4); using C3 = Cluster<double, 2, 3>;
ClusterVector<C3> cv3(4);
CHECK(cv3.item_size() == sizeof(C3));
using C4 = Cluster<char, 10, 5>;
ClusterVector<C4> cv4(4);
CHECK(cv4.item_size() == sizeof(C4));
using C5 = Cluster<int32_t, 2, 3>;
ClusterVector<C5> cv5(4);
CHECK(cv5.item_size() == sizeof(C5));
using C6 = Cluster<double, 5, 5>;
ClusterVector<C6> cv6(4);
CHECK(cv6.item_size() == sizeof(C6));
using C7 = Cluster<double, 3, 3>;
ClusterVector<C7> cv7(4);
CHECK(cv7.item_size() == sizeof(C7));
}
TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read",
"[.ClusterVector]") {
ClusterVector<Cluster<int32_t, 2, 2>> cv(4);
REQUIRE(cv.capacity() == 4); REQUIRE(cv.capacity() == 4);
REQUIRE(cv.size() == 0); REQUIRE(cv.size() == 0);
REQUIRE(cv.cluster_size_x() == 2); REQUIRE(cv.cluster_size_x() == 2);
@ -23,112 +54,102 @@ TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read") {
// int16_t, int16_t, 2x2 int32_t = 20 bytes // int16_t, int16_t, 2x2 int32_t = 20 bytes
REQUIRE(cv.item_size() == 20); REQUIRE(cv.item_size() == 20);
//Create a cluster and push back into the vector // Create a cluster and push back into the vector
Cluster_i2x2 c1 = {1, 2, {3, 4, 5, 6}}; Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
cv.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0])); cv.push_back(c1);
REQUIRE(cv.size() == 1); REQUIRE(cv.size() == 1);
REQUIRE(cv.capacity() == 4); REQUIRE(cv.capacity() == 4);
//Read the cluster back out using copy. TODO! Can we improve the API? auto c2 = cv[0];
Cluster_i2x2 c2;
std::byte *ptr = cv.element_ptr(0);
std::copy(ptr, ptr + cv.item_size(), reinterpret_cast<std::byte*>(&c2));
//Check that the data is the same // Check that the data is the same
REQUIRE(c1.x == c2.x); REQUIRE(c1.x == c2.x);
REQUIRE(c1.y == c2.y); REQUIRE(c1.y == c2.y);
for(size_t i = 0; i < 4; i++) { for (size_t i = 0; i < 4; i++) {
REQUIRE(c1.data[i] == c2.data[i]); REQUIRE(c1.data[i] == c2.data[i]);
} }
} }
TEST_CASE("Summing 3x1 clusters of int64"){ TEST_CASE("Summing 3x1 clusters of int64", "[.ClusterVector]") {
struct Cluster_l3x1{ ClusterVector<Cluster<int32_t, 3, 1>> cv(2);
int16_t x;
int16_t y;
int32_t data[3];
};
ClusterVector<int32_t> cv(3, 1, 2);
REQUIRE(cv.capacity() == 2); REQUIRE(cv.capacity() == 2);
REQUIRE(cv.size() == 0); REQUIRE(cv.size() == 0);
REQUIRE(cv.cluster_size_x() == 3); REQUIRE(cv.cluster_size_x() == 3);
REQUIRE(cv.cluster_size_y() == 1); REQUIRE(cv.cluster_size_y() == 1);
//Create a cluster and push back into the vector // Create a cluster and push back into the vector
Cluster_l3x1 c1 = {1, 2, {3, 4, 5}}; Cluster<int32_t, 3, 1> c1 = {1, 2, {3, 4, 5}};
cv.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0])); cv.push_back(c1);
REQUIRE(cv.capacity() == 2); REQUIRE(cv.capacity() == 2);
REQUIRE(cv.size() == 1); REQUIRE(cv.size() == 1);
Cluster_l3x1 c2 = {6, 7, {8, 9, 10}}; Cluster<int32_t, 3, 1> c2 = {6, 7, {8, 9, 10}};
cv.push_back(c2.x, c2.y, reinterpret_cast<std::byte*>(&c2.data[0])); cv.push_back(c2);
REQUIRE(cv.capacity() == 2); REQUIRE(cv.capacity() == 2);
REQUIRE(cv.size() == 2); REQUIRE(cv.size() == 2);
Cluster_l3x1 c3 = {11, 12, {13, 14, 15}}; Cluster<int32_t, 3, 1> c3 = {11, 12, {13, 14, 15}};
cv.push_back(c3.x, c3.y, reinterpret_cast<std::byte*>(&c3.data[0])); cv.push_back(c3);
REQUIRE(cv.capacity() == 4); REQUIRE(cv.capacity() == 4);
REQUIRE(cv.size() == 3); REQUIRE(cv.size() == 3);
/*
auto sums = cv.sum(); auto sums = cv.sum();
REQUIRE(sums.size() == 3); REQUIRE(sums.size() == 3);
REQUIRE(sums[0] == 12); REQUIRE(sums[0] == 12);
REQUIRE(sums[1] == 27); REQUIRE(sums[1] == 27);
REQUIRE(sums[2] == 42); REQUIRE(sums[2] == 42);
*/
} }
TEST_CASE("Storing floats"){ TEST_CASE("Storing floats", "[.ClusterVector]") {
struct Cluster_f4x2{ ClusterVector<Cluster<float, 2, 4>> cv(10);
int16_t x;
int16_t y;
float data[8];
};
ClusterVector<float> cv(2, 4, 10);
REQUIRE(cv.capacity() == 10); REQUIRE(cv.capacity() == 10);
REQUIRE(cv.size() == 0); REQUIRE(cv.size() == 0);
REQUIRE(cv.cluster_size_x() == 2); REQUIRE(cv.cluster_size_x() == 2);
REQUIRE(cv.cluster_size_y() == 4); REQUIRE(cv.cluster_size_y() == 4);
//Create a cluster and push back into the vector // Create a cluster and push back into the vector
Cluster_f4x2 c1 = {1, 2, {3.0, 4.0, 5.0, 6.0,3.0, 4.0, 5.0, 6.0}}; Cluster<float, 2, 4> c1 = {1, 2, {3.0, 4.0, 5.0, 6.0, 3.0, 4.0, 5.0, 6.0}};
cv.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0])); cv.push_back(c1);
REQUIRE(cv.capacity() == 10); REQUIRE(cv.capacity() == 10);
REQUIRE(cv.size() == 1); REQUIRE(cv.size() == 1);
Cluster<float, 2, 4> c2 = {
Cluster_f4x2 c2 = {6, 7, {8.0, 9.0, 10.0, 11.0,8.0, 9.0, 10.0, 11.0}}; 6, 7, {8.0, 9.0, 10.0, 11.0, 8.0, 9.0, 10.0, 11.0}};
cv.push_back(c2.x, c2.y, reinterpret_cast<std::byte*>(&c2.data[0])); cv.push_back(c2);
REQUIRE(cv.capacity() == 10); REQUIRE(cv.capacity() == 10);
REQUIRE(cv.size() == 2); REQUIRE(cv.size() == 2);
/*
auto sums = cv.sum(); auto sums = cv.sum();
REQUIRE(sums.size() == 2); REQUIRE(sums.size() == 2);
REQUIRE_THAT(sums[0], Catch::Matchers::WithinAbs(36.0, 1e-6)); REQUIRE_THAT(sums[0], Catch::Matchers::WithinAbs(36.0, 1e-6));
REQUIRE_THAT(sums[1], Catch::Matchers::WithinAbs(76.0, 1e-6)); REQUIRE_THAT(sums[1], Catch::Matchers::WithinAbs(76.0, 1e-6));
*/
} }
TEST_CASE("Push back more than initial capacity"){ TEST_CASE("Push back more than initial capacity", "[.ClusterVector]") {
ClusterVector<int32_t> cv(2, 2, 2); ClusterVector<Cluster<int32_t, 2, 2>> cv(2);
auto initial_data = cv.data(); auto initial_data = cv.data();
Cluster_i2x2 c1 = {1, 2, {3, 4, 5, 6}}; Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
cv.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0])); cv.push_back(c1);
REQUIRE(cv.size() == 1); REQUIRE(cv.size() == 1);
REQUIRE(cv.capacity() == 2); REQUIRE(cv.capacity() == 2);
Cluster_i2x2 c2 = {6, 7, {8, 9, 10, 11}}; Cluster<int32_t, 2, 2> c2 = {6, 7, {8, 9, 10, 11}};
cv.push_back(c2.x, c2.y, reinterpret_cast<std::byte*>(&c2.data[0])); cv.push_back(c2);
REQUIRE(cv.size() == 2); REQUIRE(cv.size() == 2);
REQUIRE(cv.capacity() == 2); REQUIRE(cv.capacity() == 2);
Cluster_i2x2 c3 = {11, 12, {13, 14, 15, 16}}; Cluster<int32_t, 2, 2> c3 = {11, 12, {13, 14, 15, 16}};
cv.push_back(c3.x, c3.y, reinterpret_cast<std::byte*>(&c3.data[0])); cv.push_back(c3);
REQUIRE(cv.size() == 3); REQUIRE(cv.size() == 3);
REQUIRE(cv.capacity() == 4); REQUIRE(cv.capacity() == 4);
Cluster_i2x2* ptr = reinterpret_cast<Cluster_i2x2*>(cv.data()); Cluster<int32_t, 2, 2> *ptr =
reinterpret_cast<Cluster<int32_t, 2, 2> *>(cv.data());
REQUIRE(ptr[0].x == 1); REQUIRE(ptr[0].x == 1);
REQUIRE(ptr[0].y == 2); REQUIRE(ptr[0].y == 2);
REQUIRE(ptr[1].x == 6); REQUIRE(ptr[1].x == 6);
@ -136,29 +157,31 @@ TEST_CASE("Push back more than initial capacity"){
REQUIRE(ptr[2].x == 11); REQUIRE(ptr[2].x == 11);
REQUIRE(ptr[2].y == 12); REQUIRE(ptr[2].y == 12);
//We should have allocated a new buffer, since we outgrew the initial capacity // We should have allocated a new buffer, since we outgrew the initial
// capacity
REQUIRE(initial_data != cv.data()); REQUIRE(initial_data != cv.data());
} }
TEST_CASE("Concatenate two cluster vectors where the first has enough capacity"){ TEST_CASE("Concatenate two cluster vectors where the first has enough capacity",
ClusterVector<int32_t> cv1(2, 2, 12); "[.ClusterVector]") {
Cluster_i2x2 c1 = {1, 2, {3, 4, 5, 6}}; ClusterVector<Cluster<int32_t, 2, 2>> cv1(12);
cv1.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0])); Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
Cluster_i2x2 c2 = {6, 7, {8, 9, 10, 11}}; cv1.push_back(c1);
cv1.push_back(c2.x, c2.y, reinterpret_cast<std::byte*>(&c2.data[0])); Cluster<int32_t, 2, 2> c2 = {6, 7, {8, 9, 10, 11}};
cv1.push_back(c2);
ClusterVector<int32_t> cv2(2, 2, 2); ClusterVector<Cluster<int32_t, 2, 2>> cv2(2);
Cluster_i2x2 c3 = {11, 12, {13, 14, 15, 16}}; Cluster<int32_t, 2, 2> c3 = {11, 12, {13, 14, 15, 16}};
cv2.push_back(c3.x, c3.y, reinterpret_cast<std::byte*>(&c3.data[0])); cv2.push_back(c3);
Cluster_i2x2 c4 = {16, 17, {18, 19, 20, 21}}; Cluster<int32_t, 2, 2> c4 = {16, 17, {18, 19, 20, 21}};
cv2.push_back(c4.x, c4.y, reinterpret_cast<std::byte*>(&c4.data[0])); cv2.push_back(c4);
cv1 += cv2; cv1 += cv2;
REQUIRE(cv1.size() == 4); REQUIRE(cv1.size() == 4);
REQUIRE(cv1.capacity() == 12); REQUIRE(cv1.capacity() == 12);
Cluster_i2x2* ptr = reinterpret_cast<Cluster_i2x2*>(cv1.data()); Cluster<int32_t, 2, 2> *ptr =
reinterpret_cast<Cluster<int32_t, 2, 2> *>(cv1.data());
REQUIRE(ptr[0].x == 1); REQUIRE(ptr[0].x == 1);
REQUIRE(ptr[0].y == 2); REQUIRE(ptr[0].y == 2);
REQUIRE(ptr[1].x == 6); REQUIRE(ptr[1].x == 6);
@ -169,24 +192,26 @@ TEST_CASE("Concatenate two cluster vectors where the first has enough capacity")
REQUIRE(ptr[3].y == 17); REQUIRE(ptr[3].y == 17);
} }
TEST_CASE("Concatenate two cluster vectors where we need to allocate"){ TEST_CASE("Concatenate two cluster vectors where we need to allocate",
ClusterVector<int32_t> cv1(2, 2, 2); "[.ClusterVector]") {
Cluster_i2x2 c1 = {1, 2, {3, 4, 5, 6}}; ClusterVector<Cluster<int32_t, 2, 2>> cv1(2);
cv1.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0])); Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
Cluster_i2x2 c2 = {6, 7, {8, 9, 10, 11}}; cv1.push_back(c1);
cv1.push_back(c2.x, c2.y, reinterpret_cast<std::byte*>(&c2.data[0])); Cluster<int32_t, 2, 2> c2 = {6, 7, {8, 9, 10, 11}};
cv1.push_back(c2);
ClusterVector<int32_t> cv2(2, 2, 2); ClusterVector<Cluster<int32_t, 2, 2>> cv2(2);
Cluster_i2x2 c3 = {11, 12, {13, 14, 15, 16}}; Cluster<int32_t, 2, 2> c3 = {11, 12, {13, 14, 15, 16}};
cv2.push_back(c3.x, c3.y, reinterpret_cast<std::byte*>(&c3.data[0])); cv2.push_back(c3);
Cluster_i2x2 c4 = {16, 17, {18, 19, 20, 21}}; Cluster<int32_t, 2, 2> c4 = {16, 17, {18, 19, 20, 21}};
cv2.push_back(c4.x, c4.y, reinterpret_cast<std::byte*>(&c4.data[0])); cv2.push_back(c4);
cv1 += cv2; cv1 += cv2;
REQUIRE(cv1.size() == 4); REQUIRE(cv1.size() == 4);
REQUIRE(cv1.capacity() == 4); REQUIRE(cv1.capacity() == 4);
Cluster_i2x2* ptr = reinterpret_cast<Cluster_i2x2*>(cv1.data()); Cluster<int32_t, 2, 2> *ptr =
reinterpret_cast<Cluster<int32_t, 2, 2> *>(cv1.data());
REQUIRE(ptr[0].x == 1); REQUIRE(ptr[0].x == 1);
REQUIRE(ptr[0].y == 2); REQUIRE(ptr[0].y == 2);
REQUIRE(ptr[1].x == 6); REQUIRE(ptr[1].x == 6);
@ -196,3 +221,48 @@ TEST_CASE("Concatenate two cluster vectors where we need to allocate"){
REQUIRE(ptr[3].x == 16); REQUIRE(ptr[3].x == 16);
REQUIRE(ptr[3].y == 17); REQUIRE(ptr[3].y == 17);
} }
struct ClusterTestData {
uint8_t ClusterSizeX;
uint8_t ClusterSizeY;
std::vector<int64_t> index_map_x;
std::vector<int64_t> index_map_y;
};
TEST_CASE("Gain Map Calculation Index Map", "[.ClusterVector][.gain_map]") {
auto clustertestdata = GENERATE(
ClusterTestData{3,
3,
{-1, 0, 1, -1, 0, 1, -1, 0, 1},
{-1, -1, -1, 0, 0, 0, 1, 1, 1}},
ClusterTestData{
4,
4,
{-2, -1, 0, 1, -2, -1, 0, 1, -2, -1, 0, 1, -2, -1, 0, 1},
{-2, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1}},
ClusterTestData{2, 2, {-1, 0, -1, 0}, {-1, -1, 0, 0}},
ClusterTestData{5,
5,
{-2, -1, 0, 1, 2, -2, -1, 0, 1, 2, -2, -1, 0,
1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2},
{-2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2}});
uint8_t ClusterSizeX = clustertestdata.ClusterSizeX;
uint8_t ClusterSizeY = clustertestdata.ClusterSizeY;
std::vector<int64_t> index_map_x(ClusterSizeX * ClusterSizeY);
std::vector<int64_t> index_map_y(ClusterSizeX * ClusterSizeY);
int64_t index_cluster_center_x = ClusterSizeX / 2;
int64_t index_cluster_center_y = ClusterSizeY / 2;
for (size_t j = 0; j < ClusterSizeX * ClusterSizeY; j++) {
index_map_x[j] = j % ClusterSizeX - index_cluster_center_x;
index_map_y[j] = j / ClusterSizeX - index_cluster_center_y;
}
CHECK(index_map_x == clustertestdata.index_map_x);
CHECK(index_map_y == clustertestdata.index_map_y);
}

View File

@ -14,22 +14,24 @@ CtbRawFile::CtbRawFile(const std::filesystem::path &fname) : m_master(fname) {
m_file.open(m_master.data_fname(0, 0), std::ios::binary); m_file.open(m_master.data_fname(0, 0), std::ios::binary);
} }
void CtbRawFile::read_into(std::byte *image_buf, DetectorHeader* header) { void CtbRawFile::read_into(std::byte *image_buf, DetectorHeader *header) {
if(m_current_frame >= m_master.frames_in_file()){ if (m_current_frame >= m_master.frames_in_file()) {
throw std::runtime_error(LOCATION + " End of file reached"); throw std::runtime_error(LOCATION + " End of file reached");
} }
if(m_current_frame != 0 && m_current_frame % m_master.max_frames_per_file() == 0){ if (m_current_frame != 0 &&
open_data_file(m_current_subfile+1); m_current_frame % m_master.max_frames_per_file() == 0) {
open_data_file(m_current_subfile + 1);
} }
if(header){ if (header) {
m_file.read(reinterpret_cast<char *>(header), sizeof(DetectorHeader)); m_file.read(reinterpret_cast<char *>(header), sizeof(DetectorHeader));
}else{ } else {
m_file.seekg(sizeof(DetectorHeader), std::ios::cur); m_file.seekg(sizeof(DetectorHeader), std::ios::cur);
} }
m_file.read(reinterpret_cast<char *>(image_buf), m_master.image_size_in_bytes()); m_file.read(reinterpret_cast<char *>(image_buf),
m_master.image_size_in_bytes());
m_current_frame++; m_current_frame++;
} }
@ -38,13 +40,16 @@ void CtbRawFile::seek(size_t frame_number) {
open_data_file(index); open_data_file(index);
} }
size_t frame_number_in_file = frame_number % m_master.max_frames_per_file(); size_t frame_number_in_file = frame_number % m_master.max_frames_per_file();
m_file.seekg((sizeof(DetectorHeader)+m_master.image_size_in_bytes()) * frame_number_in_file); m_file.seekg((sizeof(DetectorHeader) + m_master.image_size_in_bytes()) *
frame_number_in_file);
m_current_frame = frame_number; m_current_frame = frame_number;
} }
size_t CtbRawFile::tell() const { return m_current_frame; } size_t CtbRawFile::tell() const { return m_current_frame; }
size_t CtbRawFile::image_size_in_bytes() const { return m_master.image_size_in_bytes(); } size_t CtbRawFile::image_size_in_bytes() const {
return m_master.image_size_in_bytes();
}
size_t CtbRawFile::frames_in_file() const { return m_master.frames_in_file(); } size_t CtbRawFile::frames_in_file() const { return m_master.frames_in_file(); }
@ -63,12 +68,11 @@ void CtbRawFile::open_data_file(size_t subfile_index) {
throw std::runtime_error(LOCATION + "Subfile index out of range"); throw std::runtime_error(LOCATION + "Subfile index out of range");
} }
m_current_subfile = subfile_index; m_current_subfile = subfile_index;
m_file = std::ifstream(m_master.data_fname(0, subfile_index), std::ios::binary); // only one module for CTB m_file = std::ifstream(m_master.data_fname(0, subfile_index),
std::ios::binary); // only one module for CTB
if (!m_file.is_open()) { if (!m_file.is_open()) {
throw std::runtime_error(LOCATION + "Could not open data file"); throw std::runtime_error(LOCATION + "Could not open data file");
} }
} }
} // namespace aare } // namespace aare

152
src/DetectorGeometry.cpp Normal file
View File

@ -0,0 +1,152 @@
#include "aare/DetectorGeometry.hpp"
#include "fmt/core.h"
#include <iostream>
#include <vector>
namespace aare {
DetectorGeometry::DetectorGeometry(const xy &geometry,
const ssize_t module_pixels_x,
const ssize_t module_pixels_y,
const xy udp_interfaces_per_module,
const bool quad) {
size_t num_modules = geometry.col * geometry.row;
module_geometries.reserve(num_modules);
for (size_t col = 0; col < geometry.col;
col += udp_interfaces_per_module.col)
for (size_t row = 0; row < geometry.row;
row += udp_interfaces_per_module.row) {
for (size_t port_row = 0; port_row < udp_interfaces_per_module.row;
++port_row)
for (size_t port_col = 0;
port_col < udp_interfaces_per_module.col; ++port_col) {
ModuleGeometry g;
g.origin_x = (col + port_col) * module_pixels_x;
g.origin_y = (row + port_row) *
module_pixels_y; // TODO: quad doesnt seem
// to have an effect
g.row_index =
quad ? (row + port_row + 1) % 2 : (row + port_row);
g.col_index = col + port_col;
g.width = module_pixels_x;
g.height = module_pixels_y;
module_geometries.push_back(g);
}
}
m_pixels_y = (geometry.row * module_pixels_y);
m_pixels_x = (geometry.col * module_pixels_x);
m_modules_x = geometry.col;
m_modules_y = geometry.row;
m_pixels_y += static_cast<size_t>((geometry.row - 1) * cfg.module_gap_row);
modules_in_roi.resize(num_modules);
std::iota(modules_in_roi.begin(), modules_in_roi.end(), 0);
}
size_t DetectorGeometry::n_modules() const { return m_modules_x * m_modules_y; }
size_t DetectorGeometry::n_modules_in_roi() const {
return modules_in_roi.size();
};
size_t DetectorGeometry::pixels_x() const { return m_pixels_x; }
size_t DetectorGeometry::pixels_y() const { return m_pixels_y; }
size_t DetectorGeometry::modules_x() const { return m_modules_x; };
size_t DetectorGeometry::modules_y() const { return m_modules_y; };
const std::vector<ssize_t> &DetectorGeometry::get_modules_in_roi() const {
return modules_in_roi;
}
ssize_t DetectorGeometry::get_modules_in_roi(const size_t index) const {
return modules_in_roi[index];
}
const std::vector<ModuleGeometry> &
DetectorGeometry::get_module_geometries() const {
return module_geometries;
}
const ModuleGeometry &
DetectorGeometry::get_module_geometries(const size_t index) const {
return module_geometries[index];
}
void DetectorGeometry::update_geometry_with_roi(ROI roi) {
#ifdef AARE_VERBOSE
fmt::println("update_geometry_with_roi() called with ROI: {} {} {} {}",
roi.xmin, roi.xmax, roi.ymin, roi.ymax);
fmt::println("Geometry: {} {} {} {} {} {}", m_modules_x, m_modules_y,
m_pixels_x, m_pixels_y, cfg.module_gap_row,
cfg.module_gap_col);
#endif
modules_in_roi.clear();
modules_in_roi.reserve(m_modules_x * m_modules_y);
int pos_y = 0;
int pos_y_increment = 0;
for (size_t row = 0; row < m_modules_y; row++) {
int pos_x = 0;
for (size_t col = 0; col < m_modules_x; col++) {
auto &m = module_geometries[row * m_modules_x + col];
auto original_height = m.height;
auto original_width = m.width;
// module is to the left of the roi
if (m.origin_x + m.width < roi.xmin) {
m.width = 0;
// roi is in module
} else {
// here we only arrive when the roi is in or to the left of
// the module
if (roi.xmin > m.origin_x) {
m.width -= roi.xmin - m.origin_x;
}
if (roi.xmax < m.origin_x + original_width) {
m.width -= m.origin_x + original_width - roi.xmax;
}
m.origin_x = pos_x;
pos_x += m.width;
}
if (m.origin_y + m.height < roi.ymin) {
m.height = 0;
} else {
if ((roi.ymin > m.origin_y) &&
(roi.ymin < m.origin_y + m.height)) {
m.height -= roi.ymin - m.origin_y;
}
if (roi.ymax < m.origin_y + original_height) {
m.height -= m.origin_y + original_height - roi.ymax;
}
m.origin_y = pos_y;
pos_y_increment = m.height;
}
if (m.height != 0 && m.width != 0) {
modules_in_roi.push_back(row * m_modules_x + col);
}
#ifdef AARE_VERBOSE
fmt::println("Module {} {} {} {}", m.origin_x, m.origin_y, m.width,
m.height);
#endif
}
// increment pos_y
pos_y += pos_y_increment;
}
// m_rows = roi.height();
// m_cols = roi.width();
m_pixels_x = roi.width();
m_pixels_y = roi.height();
}
} // namespace aare

Some files were not shown because too many files have changed in this diff Show More