Compare commits
42 Commits
1.0.0-rc.1
...
1.0.0-test
| Author | SHA1 | Date | |
|---|---|---|---|
| 7044f7b085 | |||
| 165fe1a432 | |||
| ad2bc91582 | |||
| c1e9cc5d3c | |||
| eb0e5daf8f | |||
| 9f1b6a6e96 | |||
| 81db3af275 | |||
| 8cd5787848 | |||
| ebd3628eab | |||
| e02f8692f9 | |||
| f27126fa1f | |||
| d2c5b9c038 | |||
| ff191c8490 | |||
| ced40a7170 | |||
| 37e825a018 | |||
| 36257d7009 | |||
| 786b28ed78 | |||
| bd86809bdd | |||
| af366acb08 | |||
| 4fe2904b47 | |||
| c97749c16a | |||
| bd43b09b4c | |||
| dcd6088afe | |||
| b4c26b036f | |||
| b2a7dadef7 | |||
| 5808d2c5bc | |||
| c71728e16f | |||
| 10b8398bd0 | |||
| 9c1796a9e6 | |||
| 3cbf675120 | |||
| 1aa1da92ff | |||
| 07a1659e94 | |||
| bc1285acc6 | |||
| c330a0ed81 | |||
| cdba3000d2 | |||
| db47da3b6d | |||
| d3420d7f0c | |||
| 975442f452 | |||
| 4c87554233 | |||
| b5da66baf6 | |||
| 69d34e22f7 | |||
| 89cf520e52 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
cmake-build-debug/
|
||||
cmake-build-release/
|
||||
build*/
|
||||
@@ -7,6 +7,7 @@ stages:
|
||||
build:x86:gcc:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
tags:
|
||||
@@ -23,6 +24,7 @@ build:x86:gcc:
|
||||
build:x86:gcc_writer:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
tags:
|
||||
@@ -39,6 +41,7 @@ build:x86:gcc_writer:
|
||||
build:x86:driver:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
tags:
|
||||
@@ -57,6 +60,8 @@ build:x86:driver:
|
||||
|
||||
build:x86:vitis_hls:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
tags:
|
||||
- x86
|
||||
needs: []
|
||||
@@ -79,6 +84,8 @@ build:x86:vitis_hls:
|
||||
|
||||
build:x86:frontend:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
tags:
|
||||
- x86
|
||||
needs: []
|
||||
@@ -97,6 +104,7 @@ test:x86:gcc:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
@@ -110,9 +118,9 @@ test:x86:gcc:
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
- make -j48 jfjoch_test HDF5DatasetWriteTest
|
||||
- make -j48 CatchTest HDF5DatasetWriteTest
|
||||
- cd tests
|
||||
- ./jfjoch_test -r junit -o report.xml
|
||||
- ./CatchTest -r junit -o report.xml
|
||||
- cd ../tools
|
||||
- ./HDF5DatasetWriteTest ../../tests/test_data/compression_benchmark.h5
|
||||
artifacts:
|
||||
@@ -124,6 +132,7 @@ test:x86:crystfel:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
@@ -147,6 +156,7 @@ test:x86:xds_durin:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
@@ -170,6 +180,7 @@ test:x86:xds_neggia:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
@@ -193,6 +204,7 @@ test:x86:xia2.ssx:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
@@ -218,6 +230,7 @@ synthesis:vivado_pcie_100g:
|
||||
stage: synthesis
|
||||
dependencies: []
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
rules:
|
||||
@@ -230,6 +243,8 @@ synthesis:vivado_pcie_100g:
|
||||
- fpga/scripts/*
|
||||
- fpga/xdc/*
|
||||
- fpga/pcie_driver/jfjoch_fpga.h
|
||||
- when: manual
|
||||
allow_failure: true
|
||||
tags:
|
||||
- vivado
|
||||
artifacts:
|
||||
@@ -251,6 +266,7 @@ synthesis:vivado_pcie_8x10g:
|
||||
stage: synthesis
|
||||
dependencies: []
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
rules:
|
||||
@@ -263,6 +279,8 @@ synthesis:vivado_pcie_8x10g:
|
||||
- fpga/xdc/*
|
||||
- fpga/pcie_driver/jfjoch_fpga.h
|
||||
allow_failure: true
|
||||
- when: manual
|
||||
allow_failure: true
|
||||
tags:
|
||||
- vivado
|
||||
artifacts:
|
||||
@@ -282,9 +300,7 @@ synthesis:vivado_pcie_8x10g:
|
||||
|
||||
release:
|
||||
stage: release
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
when: manual
|
||||
when: manual
|
||||
tags:
|
||||
- x86
|
||||
dependencies:
|
||||
|
||||
@@ -31,6 +31,13 @@ SET(JFJOCH_WRITER_ONLY OFF CACHE BOOL "Compile HDF5 writer only")
|
||||
INCLUDE_DIRECTORIES(include)
|
||||
INCLUDE(CheckIncludeFile)
|
||||
|
||||
#This is to supress error in TORCH
|
||||
|
||||
IF ((NOT EXISTS /usr/bin/python) AND (EXISTS /usr/bin/python3))
|
||||
SET(PYTHON_EXECUTABLE /usr/bin/python3)
|
||||
ENDIF()
|
||||
|
||||
FIND_PACKAGE(Torch HINTS /opt/libtorch/share/cmake/Torch/)
|
||||
FIND_LIBRARY(NUMA_LIBRARY NAMES numa DOC "NUMA Library")
|
||||
CHECK_INCLUDE_FILE(numaif.h HAS_NUMAIF)
|
||||
CHECK_INCLUDE_FILE(numa.h HAS_NUMA_H)
|
||||
@@ -83,7 +90,8 @@ ELSE()
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(preview)
|
||||
SET(jfjoch_executables jfjoch_broker jfjoch_writer jfjoch_test CompressionBenchmark HDF5DatasetWriteTest jfjoch_udp_simulator sls_detector_put sls_detector_get)
|
||||
ADD_SUBDIRECTORY(resonet)
|
||||
SET(jfjoch_executables jfjoch_broker jfjoch_writer CatchTest CompressionBenchmark HDF5DatasetWriteTest jfjoch_udp_simulator sls_detector_put sls_detector_get)
|
||||
ENDIF()
|
||||
|
||||
ADD_CUSTOM_COMMAND(OUTPUT frontend_ui/build/index.html
|
||||
|
||||
21
README.md
21
README.md
@@ -39,9 +39,10 @@ Required:
|
||||
* JPEG library (turbo-jpeg is also OK)
|
||||
|
||||
Optional:
|
||||
* CUDA compiler version 11 or newer - required for MX fast feedback indexer
|
||||
* CUDA compiler version 11 or newer - required for MX indexing and ML resolution estimation
|
||||
* NUMA library - to pin threads to nodes/CPUs
|
||||
* Node.js - to make frontend
|
||||
* libtorch - for resolution estimation using model from Stanford - see below
|
||||
|
||||
Automatically downloaded by CMake:
|
||||
* SLS Detector Package - see [github.com/slsdetectorgroup/slsDetectorPackage](https://github.com/slsdetectorgroup/slsDetectorPackage)
|
||||
@@ -75,6 +76,7 @@ For license check LICENSE file in respective directory
|
||||
Use the following commands:
|
||||
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
@@ -86,6 +88,7 @@ In most use cases it is better to have a separate machine, with access to distri
|
||||
Such machine needs only a HDF5 writer service with fewer dependencies. For compilation use the following commands:
|
||||
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DJFJOCH_WRITER_ONLY=ON ..
|
||||
@@ -109,6 +112,7 @@ Frontend is written in TypeScript. For details see [frontend_ui/](frontend_ui) d
|
||||
|
||||
Jungfraujoch Cmake scripts have an option to start frontend build with the following command:
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
@@ -118,7 +122,7 @@ Contrary to standard CMake way, frontend will be built in "source" `frontend_ui/
|
||||
|
||||
## Tests
|
||||
|
||||
Automated test routine is then accessible as `tests/jfjoch_test`. There are also benchmark routines:
|
||||
Automated test routine is then accessible as `tests/CatchTest`. There are also benchmark routines:
|
||||
|
||||
* `HDF5DatasetWriteTest` to measure HDF5 dataset writing speed (single threaded)
|
||||
* `jfjoch_spot_finding_test` to apply spot finding and indexing routines in Jungfraujoch to an example dataset - this is equivalent to FPGA spot finding algorithm, but NOT performance equivalent as it is particularly not-efficient
|
||||
@@ -126,3 +130,16 @@ Automated test routine is then accessible as `tests/jfjoch_test`. There are also
|
||||
|
||||
In addition, tests are executed to verify that datasets written by Jungfraujoch are readable with XDS Durin plugin, XDS Neggia plygin and CrystFEL.
|
||||
Input files for these programs are placed in `xds_durin`, `xds_neggia` and `crystfel` folders. See `.gitlab-ci.yml` for details.
|
||||
|
||||
## Resolution estimation
|
||||
|
||||
Resolution estimation can be done with a recent deep learning model by D. Mendez et al.
|
||||
(see [Acta Cryst D, 80, 26-43](https://doi.org/10.1107/S2059798323010586)), adapted to Jungfraujoch.
|
||||
Model used in the original paper is located in the [resonet/](resonet) directory, after converting to TorchScript format.
|
||||
|
||||
To use the feature it is necessary to install [libtorch](https://pytorch.org/) library, preferably in `/opt/libtorch` location. The C++11 ABI version needs to be chosen.
|
||||
For RHEL 8 systems, please download older version 2.1.0, as version 2.2.0 requires newer `glibc` library than available with the operating system.
|
||||
Version 2.1.0 can be downloaded with the following command:
|
||||
```
|
||||
wget https://download.pytorch.org/libtorch/cu121/libtorch-cxx11-abi-shared-with-deps-2.1.0%2Bcu121.zip
|
||||
```
|
||||
|
||||
@@ -651,6 +651,10 @@ void JFJochBrokerHttp::preview_image_tiff_get(Pistache::Http::ResponseWriter &re
|
||||
response.send(Pistache::Http::Code::Not_Found);
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::plot_resolution_estimate_histogram_get(Pistache::Http::ResponseWriter &response) {
|
||||
GenericPlot(PlotType::ResEstimation, 0, response);
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::config_internal_generator_image_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
int64_t image_number = 0;
|
||||
|
||||
@@ -75,6 +75,8 @@ class JFJochBrokerHttp : public org::openapitools::server::api::DefaultApi {
|
||||
|
||||
void plot_rad_int_per_file_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void plot_resolution_estimate_histogram_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void statistics_calibration_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void statistics_data_collection_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
@@ -319,6 +319,7 @@ void ParseFacilityConfiguration(const nlohmann::json &input, const std::string&
|
||||
"omega_axis must be float array of 3");
|
||||
}
|
||||
|
||||
experiment.NeuralNetModelPath(GET_STR(j, "neural_net_model", ""));
|
||||
if (j.contains("pedestal_g0_frames"))
|
||||
experiment.PedestalG0Frames(GET_I64(j, "pedestal_g0_frames"));
|
||||
if (j.contains("pedestal_g1_frames"))
|
||||
|
||||
@@ -25,6 +25,7 @@ To run the test:
|
||||
|
||||
### Compile Jungfraujoch with frontend
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
|
||||
@@ -56,6 +56,7 @@ void DefaultApi::setupRoutes() {
|
||||
Routes::Get(*router, base + "/plot/rad_int_per_file", Routes::bind(&DefaultApi::plot_rad_int_per_file_get_handler, this));
|
||||
Routes::Get(*router, base + "/plot/receiver_delay", Routes::bind(&DefaultApi::plot_receiver_delay_get_handler, this));
|
||||
Routes::Get(*router, base + "/plot/receiver_free_send_buffers", Routes::bind(&DefaultApi::plot_receiver_free_send_buffers_get_handler, this));
|
||||
Routes::Get(*router, base + "/plot/resolution_estimate_histogram", Routes::bind(&DefaultApi::plot_resolution_estimate_histogram_get_handler, this));
|
||||
Routes::Get(*router, base + "/plot/roi_max_count", Routes::bind(&DefaultApi::plot_roi_max_count_get_handler, this));
|
||||
Routes::Get(*router, base + "/plot/roi_sum", Routes::bind(&DefaultApi::plot_roi_sum_get_handler, this));
|
||||
Routes::Get(*router, base + "/plot/roi_valid_pixels", Routes::bind(&DefaultApi::plot_roi_valid_pixels_get_handler, this));
|
||||
@@ -671,6 +672,26 @@ void DefaultApi::plot_receiver_free_send_buffers_get_handler(const Pistache::Res
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::plot_resolution_estimate_histogram_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
|
||||
try {
|
||||
this->plot_resolution_estimate_histogram_get(response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> errorInfo = this->handleOperationException(e);
|
||||
response.send(errorInfo.first, errorInfo.second);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (std::exception &e) {
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::plot_roi_max_count_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
@@ -80,6 +80,7 @@ private:
|
||||
void plot_rad_int_per_file_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void plot_receiver_delay_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void plot_receiver_free_send_buffers_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void plot_resolution_estimate_histogram_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void plot_roi_max_count_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void plot_roi_sum_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void plot_roi_valid_pixels_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
@@ -292,6 +293,13 @@ private:
|
||||
/// <param name="binning">Binning of frames for the plot (0 = default binning) (optional, default to 0)</param>
|
||||
virtual void plot_receiver_free_send_buffers_get(const std::optional<int32_t> &binning, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Generate resolution estimate histogram
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generate histogram of crystal resolutions from 1.0 to 5.0 A based on ML model
|
||||
/// </remarks>
|
||||
virtual void plot_resolution_estimate_histogram_get(Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Generate plot of ROI max count
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
|
||||
@@ -1445,6 +1445,17 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/plots'
|
||||
/plot/resolution_estimate_histogram:
|
||||
get:
|
||||
summary: Generate resolution estimate histogram
|
||||
description: Generate histogram of crystal resolutions from 1.0 to 5.0 A based on ML model
|
||||
responses:
|
||||
"200":
|
||||
description: Everything OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/plots'
|
||||
/plot/rad_int:
|
||||
get:
|
||||
summary: Generate radial integration profile
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1064,6 +1064,15 @@ const DetectorSetup &DiffractionExperiment::GetDetectorSetup() const {
|
||||
return detector;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::NeuralNetModelPath(const std::string &input) {
|
||||
neural_net_model_path = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string DiffractionExperiment::GetNeuralNetModelPath() const {
|
||||
return neural_net_model_path;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::PulsedSource(bool input) {
|
||||
pulsed_source = input;
|
||||
return *this;
|
||||
|
||||
@@ -89,6 +89,8 @@ class DiffractionExperiment {
|
||||
|
||||
uint64_t series_id;
|
||||
|
||||
std::string neural_net_model_path;
|
||||
|
||||
bool rad_int_solid_angle_corr;
|
||||
bool rad_int_polarization_corr;
|
||||
float rad_int_polarization_factor{};
|
||||
@@ -142,6 +144,7 @@ public:
|
||||
DiffractionExperiment& FixedGainG1(bool input);
|
||||
DiffractionExperiment& IncrementSeriesID();
|
||||
DiffractionExperiment& ConversionOnFPGA(bool input);
|
||||
DiffractionExperiment& NeuralNetModelPath(const std::string& input);
|
||||
DiffractionExperiment& PulsedSource(bool input);
|
||||
|
||||
DiffractionExperiment& ImagesPerTrigger(int64_t input);
|
||||
@@ -279,6 +282,7 @@ public:
|
||||
bool IsConversionOnFPGA() const;
|
||||
|
||||
const DetectorSetup& GetDetectorSetup() const;
|
||||
std::string GetNeuralNetModelPath() const;
|
||||
|
||||
bool IsPulsedSource() const;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
enum class PlotType {BkgEstimate, RadInt, RadIntPerTimePoint, SpotCount, IndexingRate, IndexingRatePerTimePoint,
|
||||
ErrorPixels, ImageCollectionEfficiency, ReceiverDelay, ReceiverFreeSendBuf, StrongPixels,
|
||||
ROISum, ROIMaxCount, ROIPixels};
|
||||
ROISum, ROIMaxCount, ROIPixels, ResEstimation};
|
||||
|
||||
struct PlotRequest {
|
||||
PlotType type;
|
||||
|
||||
@@ -75,6 +75,8 @@ struct DataMessage {
|
||||
std::optional<uint64_t> receiver_free_send_buf;
|
||||
std::optional<uint64_t> storage_cell;
|
||||
|
||||
std::optional<float> resolution_estimation;
|
||||
|
||||
std::optional<uint64_t> xfel_pulse_id;
|
||||
std::optional<uint64_t> xfel_event_code;
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ export enum PlotType {
|
||||
STRONG_PIXELS,
|
||||
ROI_SUM,
|
||||
ROI_MAX_COUNT,
|
||||
RECEIVER_FREE_SEND_BUFS
|
||||
RES_ESTIMATION,
|
||||
RECEIVER_FREE_SEND_BUFS,
|
||||
BEAM_CENTER_DRIFT
|
||||
}
|
||||
|
||||
type MyProps = {
|
||||
@@ -143,6 +145,13 @@ class DataProcessingPlot extends Component<MyProps, MyState> {
|
||||
this.setState({connection_error: true});
|
||||
});
|
||||
break;
|
||||
case PlotType.RES_ESTIMATION:
|
||||
DefaultService.getPlotResolutionEstimateHistogram()
|
||||
.then(data => this.setState({plots: data, connection_error: false}))
|
||||
.catch(error => {
|
||||
this.setState({connection_error: true});
|
||||
});
|
||||
break;
|
||||
case PlotType.RAD_INT_PER_FILE:
|
||||
DefaultService.getPlotRadIntPerFile()
|
||||
.then(data => this.setState({plots: data, connection_error: false}))
|
||||
|
||||
@@ -69,6 +69,9 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
case "11":
|
||||
this.setState({type: PlotType.ROI_SUM, xlabel: "Image number", ylabel: "Photon count"});
|
||||
break;
|
||||
case "12":
|
||||
this.setState({type: PlotType.RES_ESTIMATION, xlabel: "Resolution [Å]", ylabel: "Number of crystals"});
|
||||
break;
|
||||
case "13":
|
||||
this.setState({type: PlotType.RECEIVER_FREE_SEND_BUFS, xlabel: "Image number", ylabel: "Number of buffers"});
|
||||
break;
|
||||
@@ -97,6 +100,7 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
<MenuItem value={3}>Azimuthal integration profile</MenuItem>
|
||||
<MenuItem value={11}>ROI area sum</MenuItem>
|
||||
<MenuItem value={6}>ROI area max count</MenuItem>
|
||||
<MenuItem value={12}>Crystal resolution histogram</MenuItem>
|
||||
<MenuItem value={4}>Indexing rate (per time point)</MenuItem>
|
||||
<MenuItem value={5}>Azimuthal integration profile (per time point)</MenuItem>
|
||||
<MenuItem value={8}>Error and saturated pixels</MenuItem>
|
||||
|
||||
@@ -724,6 +724,19 @@ export class DefaultService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate resolution estimate histogram
|
||||
* Generate histogram of crystal resolutions from 1.0 to 5.0 A based on ML model
|
||||
* @returns plots Everything OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getPlotResolutionEstimateHistogram(): CancelablePromise<plots> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/plot/resolution_estimate_histogram',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate radial integration profile
|
||||
* Generate average radial integration profile
|
||||
|
||||
@@ -21,8 +21,8 @@ IF (CMAKE_CUDA_COMPILER)
|
||||
|
||||
FetchContent_Declare(
|
||||
fast-indexer
|
||||
GIT_REPOSITORY https://github.com/fleon-psi/fast-feedback-indexer/
|
||||
GIT_TAG 651cbc9
|
||||
GIT_REPOSITORY https://github.com/paulscherrerinstitute/fast-feedback-indexer/
|
||||
GIT_TAG 2d6fa7511f240eeb4918dd7647edd0642e987317
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(fast-indexer)
|
||||
|
||||
@@ -11,7 +11,7 @@ ADD_LIBRARY(JFJochReceiver STATIC
|
||||
LossyFilter.cpp
|
||||
LossyFilter.h)
|
||||
|
||||
TARGET_LINK_LIBRARIES(JFJochReceiver ImagePusher JFJochImageAnalysis JFJochAcquisitionDevice JFJochCommon JFJochHLSSimulation JFJochPreview)
|
||||
TARGET_LINK_LIBRARIES(JFJochReceiver ImagePusher JFJochImageAnalysis JFJochAcquisitionDevice JFJochCommon JFJochHLSSimulation JFJochPreview JFJochResonet)
|
||||
|
||||
ADD_EXECUTABLE(jfjoch_action_test jfjoch_action_test.cpp)
|
||||
TARGET_LINK_LIBRARIES(jfjoch_action_test JFJochReceiver)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "../image_analysis/MXAnalyzer.h"
|
||||
#include "../common/DiffractionGeometry.h"
|
||||
#include "ImageMetadata.h"
|
||||
#include "../resonet/NeuralNetResPredictor.h"
|
||||
#include "../common/time_utc.h"
|
||||
#include "../common/PixelMask.h"
|
||||
|
||||
@@ -265,6 +266,7 @@ void JFJochReceiver::FrameTransformationThread() {
|
||||
Cancel(e);
|
||||
}
|
||||
|
||||
NeuralNetResPredictor neural_net_predictor(experiment.GetNeuralNetModelPath());
|
||||
FrameTransformation transformation(experiment);
|
||||
|
||||
MXAnalyzer analyzer(experiment);
|
||||
@@ -337,6 +339,7 @@ void JFJochReceiver::FrameTransformationThread() {
|
||||
message.receiver_free_send_buf = send_buf_ctrl.GetAvailBufLocations();
|
||||
message.az_int_profile = az_int_profile_image.GetResult();
|
||||
message.bkg_estimate = az_int_profile_image.GetMeanValueOfBins(az_int_min_bin, az_int_max_bin);
|
||||
message.resolution_estimation = neural_net_predictor.Inference(experiment, transformation.GetImage());
|
||||
|
||||
plots.Add(message, image_number % experiment.GetTimePointNumber(), az_int_profile_image);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ JFJochReceiverPlots::JFJochReceiverPlots(const DiffractionExperiment &experiment
|
||||
const AzimuthalIntegrationMapping &mapping)
|
||||
: indexing_solution_per_time_point(experiment.GetTimePointNumber()),
|
||||
default_binning(experiment.GetDefaultPlotBinning()),
|
||||
az_int_profile(mapping) {
|
||||
az_int_profile(mapping), resolution_estimation(50, 1.0, 5.0) {
|
||||
az_int_profile.SetTitle("dataset");
|
||||
for (int i = 0; i < experiment.GetTimePointNumber(); i++) {
|
||||
az_int_profile_per_time_point.emplace_back(mapping);
|
||||
@@ -36,6 +36,9 @@ void JFJochReceiverPlots::Add(const DataMessage &msg, uint64_t file_number, cons
|
||||
indexing_solution.AddElement(msg.number, msg.indexing_result);
|
||||
indexing_solution_per_time_point.Add(file_number, msg.indexing_result);
|
||||
|
||||
if (msg.resolution_estimation)
|
||||
resolution_estimation.Add(msg.resolution_estimation.value());
|
||||
|
||||
az_int_profile += profile;
|
||||
az_int_profile_per_time_point.at(file_number) += profile;
|
||||
|
||||
@@ -118,6 +121,8 @@ MultiLinePlot JFJochReceiverPlots::GetPlots(const PlotRequest &request) {
|
||||
ret.emplace_back(tmp.at(0));
|
||||
}
|
||||
return ret;
|
||||
case PlotType::ResEstimation:
|
||||
return resolution_estimation.GetPlot();
|
||||
case PlotType::RadIntPerTimePoint:
|
||||
tmp = az_int_profile.GetPlot();
|
||||
ret.emplace_back(tmp.at(0));
|
||||
|
||||
@@ -31,6 +31,7 @@ class JFJochReceiverPlots {
|
||||
std::map<std::string, std::unique_ptr<StatusVector<int64_t>>> roi_sum;
|
||||
std::map<std::string, std::unique_ptr<StatusVector<int64_t>>> roi_max_count;
|
||||
std::map<std::string, std::unique_ptr<StatusVector<uint64_t>>> roi_pixels;
|
||||
FloatHistogram resolution_estimation;
|
||||
|
||||
AzimuthalIntegrationProfile az_int_profile;
|
||||
std::vector<AzimuthalIntegrationProfile> az_int_profile_per_time_point;
|
||||
|
||||
@@ -19,6 +19,7 @@ void print_usage(Logger &logger) {
|
||||
logger.Info(" -i<num> number of images");
|
||||
logger.Info(" -N<num> number of image processing threads");
|
||||
logger.Info(" -P<txt> NUMA Policy (none|n2g2|n8g4|n8g4_hbm), none is default");
|
||||
logger.Info(" -D<path> use resonet deep learning model for resolution estimation - path to TorchScript");
|
||||
logger.Info(" -B<num> size of send buffer in MiB (default 2048)");
|
||||
}
|
||||
|
||||
@@ -36,6 +37,7 @@ int main(int argc, char **argv) {
|
||||
std::string numa_policy_name;
|
||||
bool raw_data = false;
|
||||
bool force_32bit = false;
|
||||
std::string resonet_path;
|
||||
DetectorType detector_type = DetectorType::JUNGFRAU;
|
||||
bool hls_simulation = false;
|
||||
size_t send_buffer_size_MiB = 2048;
|
||||
@@ -46,7 +48,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "s:i:m:N:P:vRIS:EHB:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "s:i:m:N:P:vRIS:D:EHB:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
nimages = atol(optarg);
|
||||
@@ -75,6 +77,9 @@ int main(int argc, char **argv) {
|
||||
case 'I':
|
||||
force_32bit = true;
|
||||
break;
|
||||
case 'D':
|
||||
resonet_path = std::string(optarg);
|
||||
break;
|
||||
case 'E':
|
||||
detector_type = DetectorType::EIGER;
|
||||
break;
|
||||
@@ -109,6 +114,8 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (force_32bit)
|
||||
x.FPGAOutputMode(FPGAPixelOutput::Int32);
|
||||
if (!resonet_path.empty())
|
||||
x.NeuralNetModelPath(resonet_path);
|
||||
|
||||
logger.Info("Data streams {} Total modules {} Total images {} Threads {}", nstreams, nmodules, nimages, nthreads);
|
||||
|
||||
|
||||
10
resonet/CMakeLists.txt
Normal file
10
resonet/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
ADD_LIBRARY(JFJochResonet STATIC NeuralNetResPredictor.cpp NeuralNetResPredictor.h)
|
||||
TARGET_LINK_LIBRARIES(JFJochResonet JFJochCommon)
|
||||
|
||||
IF (${TORCH_FOUND})
|
||||
TARGET_COMPILE_DEFINITIONS(JFJochResonet PUBLIC -DJFJOCH_USE_TORCH)
|
||||
TARGET_LINK_LIBRARIES(JFJochResonet ${TORCH_LIBRARIES})
|
||||
TARGET_INCLUDE_DIRECTORIES(JFJochResonet PUBLIC ${TORCH_INCLUDE_DIRS})
|
||||
ADD_EXECUTABLE(resonet_test resonet_test.cpp)
|
||||
TARGET_LINK_LIBRARIES(resonet_test JFJochResonet JFJochWriter)
|
||||
ENDIF()
|
||||
28
resonet/LICENSE.resonet
Normal file
28
resonet/LICENSE.resonet
Normal file
@@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2023, Macromolecular Crystallography at SSRL
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
101
resonet/NeuralNetResPredictor.cpp
Normal file
101
resonet/NeuralNetResPredictor.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include "NeuralNetResPredictor.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
NeuralNetResPredictor::NeuralNetResPredictor(const std::string& model_path)
|
||||
: model_input(512*512),
|
||||
enable(!model_path.empty())
|
||||
#ifdef JFJOCH_USE_TORCH
|
||||
, device(torch::kCUDA)
|
||||
#endif
|
||||
{
|
||||
#ifdef JFJOCH_USE_TORCH
|
||||
if (enable) {
|
||||
module = torch::jit::load(model_path);
|
||||
module.to(device);
|
||||
}
|
||||
#else
|
||||
enable = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void NeuralNetResPredictor::PrepareInternal(const DiffractionExperiment& experiment, const T* image) {
|
||||
size_t pool_factor = GetMaxPoolFactor(experiment);
|
||||
size_t xpixel = experiment.GetXPixelsNum();
|
||||
size_t ypixel = experiment.GetYPixelsNum();
|
||||
size_t start_x = std::lround(experiment.GetBeamX_pxl());
|
||||
size_t start_y = std::lround(experiment.GetBeamY_pxl());
|
||||
|
||||
for (size_t y = 0; y < 512; y++) {
|
||||
size_t y0 = y * pool_factor + start_y;
|
||||
size_t min_yp = std::min(y0 + pool_factor, ypixel);
|
||||
for (size_t x = 0; x < 512; x++) {
|
||||
float val = 0.0;
|
||||
size_t x0 = x * pool_factor + start_x;
|
||||
size_t min_xp = std::min(x0 + pool_factor, ypixel);
|
||||
|
||||
for (size_t yp = y0; yp < min_yp; yp++) {
|
||||
for (size_t xp = x0; xp < min_xp; xp++) {
|
||||
int16_t pxl = image[yp * xpixel + xp];
|
||||
if (val < pxl)
|
||||
val = pxl;
|
||||
}
|
||||
}
|
||||
float max_pool = floorf(sqrtf(val));
|
||||
model_input[512 * y + x] = max_pool;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NeuralNetResPredictor::Prepare(const DiffractionExperiment& experiment, const int16_t *image) {
|
||||
PrepareInternal(experiment, image);
|
||||
}
|
||||
|
||||
void NeuralNetResPredictor::Prepare(const DiffractionExperiment& experiment, const int32_t *image) {
|
||||
PrepareInternal(experiment, image);
|
||||
}
|
||||
|
||||
size_t NeuralNetResPredictor::GetMaxPoolFactor(const DiffractionExperiment& experiment) const {
|
||||
float max_direction = std::max(experiment.GetXPixelsNum(), experiment.GetYPixelsNum()) / 2.0;
|
||||
size_t pool_factor = std::lround(max_direction / 512.0f);
|
||||
if (pool_factor <= 0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Detector size is too small");
|
||||
|
||||
if (pool_factor > 8)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Detector size is too large");
|
||||
return pool_factor;
|
||||
}
|
||||
|
||||
std::optional<float> NeuralNetResPredictor::Inference(const DiffractionExperiment& experiment, const void *image) {
|
||||
if (!enable)
|
||||
return {};
|
||||
#ifdef JFJOCH_USE_TORCH
|
||||
if (experiment.GetPixelDepth() == 2)
|
||||
Prepare(experiment, (int16_t *) image);
|
||||
else
|
||||
Prepare(experiment, (int32_t *) image);
|
||||
|
||||
auto options = torch::TensorOptions().dtype(at::kFloat);
|
||||
auto model_input_tensor = torch::from_blob(model_input.data(), {1,1,512,512}, options).to(device);
|
||||
|
||||
std::vector<torch::jit::IValue> pixels;
|
||||
pixels.emplace_back(std::move(model_input_tensor));
|
||||
|
||||
auto output = module.forward(pixels).toTensor();
|
||||
auto tensor_output = output[0].item<float>();
|
||||
float two_theta = atanf(((2.0f * experiment.GetPixelSize_mm() / experiment.GetDetectorDistance_mm()) * tensor_output));
|
||||
float stheta = sinf(two_theta * 0.5f);
|
||||
float resolution = experiment.GetWavelength_A() / (2.0f * stheta);
|
||||
return resolution;
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::vector<float> &NeuralNetResPredictor::GetModelInput() const {
|
||||
return model_input;
|
||||
}
|
||||
36
resonet/NeuralNetResPredictor.h
Normal file
36
resonet/NeuralNetResPredictor.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#ifndef JUNGFRAUJOCH_NEURALNETRESPREDICTOR_H
|
||||
#define JUNGFRAUJOCH_NEURALNETRESPREDICTOR_H
|
||||
|
||||
#ifdef JFJOCH_USE_TORCH
|
||||
#include <torch/script.h>
|
||||
#endif
|
||||
|
||||
#include "../common/DiffractionExperiment.h"
|
||||
|
||||
// Based on model described in:
|
||||
// Mendez, D., Holton, J. M., Lyubimov, A. Y., Hollatz, S., Mathews, I. I., Cichosz, A., Martirosyan, V.,
|
||||
// Zeng, T., Stofer, R., Liu, R., Song, J., McPhillips, S., Soltis, M. & Cohen, A. E. (2024).
|
||||
// Acta Cryst. D80, 26-43.
|
||||
|
||||
class NeuralNetResPredictor {
|
||||
std::vector<float> model_input;
|
||||
bool enable;
|
||||
#ifdef JFJOCH_USE_TORCH
|
||||
torch::Device device;
|
||||
torch::jit::script::Module module;
|
||||
#endif
|
||||
template<class T>
|
||||
void PrepareInternal(const DiffractionExperiment& experiment, const T* image);
|
||||
public:
|
||||
explicit NeuralNetResPredictor(const std::string& model_path);
|
||||
void Prepare(const DiffractionExperiment& experiment, const int16_t* image);
|
||||
void Prepare(const DiffractionExperiment& experiment, const int32_t* image);
|
||||
std::optional<float> Inference(const DiffractionExperiment& experiment, const void* image);
|
||||
size_t GetMaxPoolFactor(const DiffractionExperiment& experiment) const;
|
||||
const std::vector<float> &GetModelInput() const;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_NEURALNETRESPREDICTOR_H
|
||||
51
resonet/resonet_test.cpp
Normal file
51
resonet/resonet_test.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "NeuralNetResPredictor.h"
|
||||
#include "../writer/HDF5Objects.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Usage ./resonet_test <.pt file with traced model>" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
RegisterHDF5Filter();
|
||||
|
||||
DiffractionExperiment experiment(DetectorGeometry(8, 2, 8, 36));
|
||||
experiment.DetectorDistance_mm(75).PhotonEnergy_keV(12.4).BeamY_pxl(1136).BeamX_pxl(1090);
|
||||
|
||||
NeuralNetResPredictor predictor(argv[1]);
|
||||
|
||||
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
|
||||
HDF5DataSet dataset(data, "/entry/data/data");
|
||||
HDF5DataSpace file_space(dataset);
|
||||
|
||||
std::vector<int16_t> image_conv (file_space.GetDimensions()[1] * file_space.GetDimensions()[2]);
|
||||
|
||||
std::vector<hsize_t> start = {4,0,0};
|
||||
std::vector<hsize_t> file_size = {1, file_space.GetDimensions()[1], file_space.GetDimensions()[2]};
|
||||
dataset.ReadVector(image_conv, start, file_size);
|
||||
|
||||
std::cout << "Max pooling " << predictor.GetMaxPoolFactor(experiment) << std::endl;
|
||||
|
||||
float x = 0;
|
||||
size_t niterations = 25;
|
||||
std::optional<float> result;
|
||||
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
for (int i = 0; i < niterations; i++) {
|
||||
result = predictor.Inference(experiment, image_conv.data());
|
||||
if (result)
|
||||
x += result.value();
|
||||
}
|
||||
auto end_time = std::chrono::system_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
|
||||
int64_t frequency_Hz = (niterations * 1e6) / (double) (elapsed.count());
|
||||
|
||||
std::cout << ((double) elapsed.count()) / (1e3 * niterations) << " ms" << std::endl;
|
||||
std::cout << frequency_Hz << " Hz" << std::endl;
|
||||
if (result)
|
||||
std::cout << "Resolution " << result.value() << std::endl;
|
||||
}
|
||||
BIN
resonet/traced_resnet_model.pt
Normal file
BIN
resonet/traced_resnet_model.pt
Normal file
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
ADD_EXECUTABLE(jfjoch_test
|
||||
ADD_EXECUTABLE(CatchTest
|
||||
DiffractionExperimentTest.cpp
|
||||
RawToConvertedGeometryTest.cpp
|
||||
../common/RawToConvertedGeometry.h
|
||||
@@ -30,6 +30,7 @@ ADD_EXECUTABLE(jfjoch_test
|
||||
TIFFTest.cpp
|
||||
JFJochReceiverProcessingTest.cpp
|
||||
JPEGTest.cpp
|
||||
NeuralNetResPredictorTest.cpp
|
||||
HistogramTest.cpp
|
||||
FPGAEigerReorderTest.cpp
|
||||
ROIMapTest.cpp
|
||||
@@ -39,5 +40,7 @@ ADD_EXECUTABLE(jfjoch_test
|
||||
RegressionTest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(jfjoch_test Catch2WithMain JFJochBroker JFJochReceiver JFJochWriter JFJochImageAnalysis JFJochCommon JFJochHLSSimulation JFJochPreview)
|
||||
target_include_directories(jfjoch_test PRIVATE .)
|
||||
target_link_libraries(CatchTest Catch2WithMain JFJochBroker JFJochReceiver JFJochWriter JFJochImageAnalysis JFJochCommon JFJochHLSSimulation JFJochPreview JFJochResonet)
|
||||
target_include_directories(CatchTest PRIVATE .)
|
||||
|
||||
INSTALL(TARGETS CatchTest RUNTIME)
|
||||
|
||||
@@ -188,6 +188,73 @@ TEST_CASE("GenerateResolutionMap") {
|
||||
experiment.GetYPixelsNum(), 4);
|
||||
}
|
||||
|
||||
TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_resolution", "[JFJochReceiver]") {
|
||||
Logger logger("JFJochIntegrationTest_ZMQ_lysozyme_resolution");
|
||||
|
||||
RegisterHDF5Filter();
|
||||
|
||||
const uint16_t nthreads = 4;
|
||||
|
||||
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
|
||||
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
|
||||
.FilePrefix("lyso_test_resolution").ConversionOnFPGA(false)
|
||||
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
|
||||
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90})
|
||||
.NeuralNetModelPath("../../resonet/traced_resnet_model.pt");
|
||||
|
||||
// Load example image
|
||||
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
|
||||
HDF5DataSet dataset(data, "/entry/data/data");
|
||||
HDF5DataSpace file_space(dataset);
|
||||
|
||||
REQUIRE(file_space.GetDimensions()[2] == experiment.GetXPixelsNum());
|
||||
REQUIRE(file_space.GetDimensions()[1] == experiment.GetYPixelsNum());
|
||||
std::vector<int16_t> image_conv (file_space.GetDimensions()[1] * file_space.GetDimensions()[2]);
|
||||
|
||||
std::vector<hsize_t> start = {4,0,0};
|
||||
std::vector<hsize_t> file_size = {1, file_space.GetDimensions()[1], file_space.GetDimensions()[2]};
|
||||
dataset.ReadVector(image_conv, start, file_size);
|
||||
|
||||
std::vector<int16_t> image_raw_geom(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
ConvertedToRawGeometry(experiment, image_raw_geom.data(), image_conv.data());
|
||||
logger.Info("Loaded image");
|
||||
|
||||
// Setup acquisition device
|
||||
AcquisitionDeviceGroup aq_devices;
|
||||
std::unique_ptr<HLSSimulatedDevice> test = std::make_unique<HLSSimulatedDevice>(0, 64);
|
||||
for (int m = 0; m < experiment.GetModulesNum(); m++)
|
||||
test->SetInternalGeneratorFrame((uint16_t *) image_raw_geom.data() + m * RAW_MODULE_SIZE, m);
|
||||
aq_devices.Add(std::move(test));
|
||||
|
||||
ZMQContext context;
|
||||
ZMQStream2Pusher pusher(context, "ipc://*");
|
||||
StreamWriter writer(context, logger, pusher.GetAddress());
|
||||
auto writer_future = std::async(std::launch::async, &StreamWriter::Run, &writer);
|
||||
|
||||
context.NumThreads(4);
|
||||
|
||||
JFJochReceiverService service(aq_devices, logger, pusher);
|
||||
service.NumThreads(nthreads);
|
||||
|
||||
SpotFindingSettings settings = DiffractionExperiment::DefaultDataProcessingSettings();
|
||||
settings.signal_to_noise_threshold = 2.5;
|
||||
settings.photon_count_threshold = 5;
|
||||
settings.min_pix_per_spot = 1;
|
||||
settings.max_pix_per_spot = 200;
|
||||
service.SetSpotFindingSettings(settings);
|
||||
|
||||
service.Start(experiment, nullptr);
|
||||
auto receiver_out = service.Stop();
|
||||
|
||||
CHECK(receiver_out.efficiency == 1.0);
|
||||
CHECK(receiver_out.status.indexing_rate == 1.0);
|
||||
CHECK(receiver_out.status.images_sent == experiment.GetImageNum());
|
||||
CHECK(!receiver_out.status.cancelled);
|
||||
|
||||
REQUIRE_NOTHROW(writer_future.get());
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("JFJochIntegrationTest_ZMQ_ROI", "[JFJochReceiver]") {
|
||||
Logger logger("JFJochIntegrationTest_ZMQ_ROI");
|
||||
|
||||
|
||||
51
tests/NeuralNetResPredictorTest.cpp
Normal file
51
tests/NeuralNetResPredictorTest.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include "../writer/HDF5Objects.h"
|
||||
#include "../resonet/NeuralNetResPredictor.h"
|
||||
|
||||
TEST_CASE("NeuralNetResPredictor_Prepare", "[LinearAlgebra][Coord]") {
|
||||
DiffractionExperiment experiment(DetectorGeometry(8, 2, 8, 36));
|
||||
experiment.DetectorDistance_mm(75).PhotonEnergy_keV(12.4).BeamX_pxl(1000).BeamY_pxl(1000);
|
||||
|
||||
std::vector<int16_t> v(experiment.GetPixelsNum(),0);
|
||||
v[1000 * experiment.GetXPixelsNum() + 1000] = 100;
|
||||
v[1000 * experiment.GetXPixelsNum() + 1001] = 20;
|
||||
v[1001 * experiment.GetXPixelsNum() + 1000] = 30;
|
||||
v[1001 * experiment.GetXPixelsNum() + 1001] = INT16_MIN;
|
||||
|
||||
v[1050 * experiment.GetXPixelsNum() + 1050] = 52;
|
||||
v[2000 * experiment.GetXPixelsNum() + 1500] = 160;
|
||||
NeuralNetResPredictor predictor("../../resonet/traced_resnet_model.pt");
|
||||
|
||||
REQUIRE(predictor.GetMaxPoolFactor(experiment) == 2);
|
||||
|
||||
predictor.Prepare(experiment, v.data());
|
||||
auto nn_input = predictor.GetModelInput();
|
||||
REQUIRE(nn_input[0] == 10);
|
||||
REQUIRE(nn_input[25 * 512 + 25] == 7);
|
||||
REQUIRE(nn_input[500 * 512 + 250] == 12);
|
||||
}
|
||||
|
||||
TEST_CASE("NeuralNetResPredictor_Inference", "[LinearAlgebra][Coord]") {
|
||||
DiffractionExperiment experiment(DetectorGeometry(8, 2, 8, 36));
|
||||
experiment.DetectorDistance_mm(75).PhotonEnergy_keV(12.4).BeamY_pxl(1136).BeamX_pxl(1090);
|
||||
|
||||
NeuralNetResPredictor predictor("../../resonet/traced_resnet_model.pt");
|
||||
|
||||
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
|
||||
HDF5DataSet dataset(data, "/entry/data/data");
|
||||
HDF5DataSpace file_space(dataset);
|
||||
|
||||
std::vector<int16_t> image_conv (file_space.GetDimensions()[1] * file_space.GetDimensions()[2]);
|
||||
|
||||
std::vector<hsize_t> start = {4,0,0};
|
||||
std::vector<hsize_t> file_size = {1, file_space.GetDimensions()[1], file_space.GetDimensions()[2]};
|
||||
dataset.ReadVector(image_conv, start, file_size);
|
||||
|
||||
auto res = predictor.Inference(experiment, image_conv.data());
|
||||
REQUIRE(res);
|
||||
REQUIRE(res.value() < 1.5);
|
||||
REQUIRE(res.value() > 1.4);
|
||||
}
|
||||
@@ -21,7 +21,8 @@ ADD_LIBRARY(JFJochWriter STATIC
|
||||
HDF5DataFilePluginAzInt.h
|
||||
HDF5DataFilePluginJUNGFRAU.cpp
|
||||
HDF5DataFilePluginJUNGFRAU.h
|
||||
)
|
||||
HDF5DataFilePluginResEstimation.cpp
|
||||
HDF5DataFilePluginResEstimation.h)
|
||||
|
||||
TARGET_LINK_LIBRARIES(JFJochWriter JFJochHDF5Wrappers JFJochCommon CBORStream2FrameSerialize)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "HDF5DataFilePluginMX.h"
|
||||
#include "HDF5DataFilePluginXFEL.h"
|
||||
#include "HDF5DataFilePluginJUNGFRAU.h"
|
||||
#include "HDF5DataFilePluginResEstimation.h"
|
||||
#include "../include/spdlog/fmt/fmt.h"
|
||||
|
||||
HDF5DataFile::HDF5DataFile(const std::string &in_filename,
|
||||
@@ -26,6 +27,7 @@ HDF5DataFile::HDF5DataFile(const std::string &in_filename,
|
||||
plugins.emplace_back(std::make_unique<HDF5DataFilePluginJUNGFRAU>());
|
||||
plugins.emplace_back(std::make_unique<HDF5DataFilePluginAzInt>(in_rad_int_bin_to_q));
|
||||
plugins.emplace_back(std::make_unique<HDF5DataFilePluginXFEL>());
|
||||
plugins.emplace_back(std::make_unique<HDF5DataFilePluginResEstimation>());
|
||||
plugins.emplace_back(std::make_unique<HDF5DataFilePluginMX>(in_max_spots));
|
||||
}
|
||||
|
||||
|
||||
20
writer/HDF5DataFilePluginResEstimation.cpp
Normal file
20
writer/HDF5DataFilePluginResEstimation.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include "HDF5DataFilePluginResEstimation.h"
|
||||
|
||||
void HDF5DataFilePluginResEstimation::OpenFile(HDF5File &data_file, const DataMessage &msg) {}
|
||||
|
||||
void HDF5DataFilePluginResEstimation::Write(const DataMessage &msg, uint64_t image_number) {
|
||||
if (!msg.resolution_estimation)
|
||||
return;
|
||||
|
||||
if (image_number >= res_estimation.size())
|
||||
res_estimation.resize(image_number + 1);
|
||||
|
||||
res_estimation[image_number] = msg.resolution_estimation.value();
|
||||
}
|
||||
|
||||
void HDF5DataFilePluginResEstimation::WriteFinal(HDF5File &data_file) {
|
||||
if (!res_estimation.empty())
|
||||
data_file.SaveVector("/entry/res_estimation", res_estimation);
|
||||
}
|
||||
17
writer/HDF5DataFilePluginResEstimation.h
Normal file
17
writer/HDF5DataFilePluginResEstimation.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#ifndef JUNGFRAUJOCH_HDF5DATAFILEPLUGINRESESTIMATION_H
|
||||
#define JUNGFRAUJOCH_HDF5DATAFILEPLUGINRESESTIMATION_H
|
||||
|
||||
#include "HDF5DataFilePlugin.h"
|
||||
|
||||
class HDF5DataFilePluginResEstimation : public HDF5DataFilePlugin {
|
||||
std::vector<float> res_estimation;
|
||||
public:
|
||||
void OpenFile(HDF5File &data_file, const DataMessage &msg) override;
|
||||
void Write(const DataMessage &msg, uint64_t image_number) override;
|
||||
void WriteFinal(HDF5File &data_file) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_HDF5DATAFILEPLUGINRESESTIMATION_H
|
||||
Reference in New Issue
Block a user