// Copyright (2019-2024) Paul Scherrer Institute #include #include "DatasetSettings.h" #include "Definitions.h" #include "JFJochException.h" #define check_max(param, val, max) if ((val) > (max)) throw JFJochException(JFJochExceptionCategory::InputParameterAboveMax, param) #define check_min(param, val, min) if ((val) < (min)) throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin, param) #define check_finite(param, val) if (!std::isfinite(val)) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, param) DatasetSettings::DatasetSettings() { photon_energy_keV = WVL_1A_IN_KEV; detector_distance_mm = 100; beam_x_pxl = 0.0; beam_y_pxl = 0.0; file_prefix = "test"; ntrigger = 1; images_per_trigger = 1; fpga_pixel_output = FPGAPixelOutput::Auto; space_group_number = 0; // not set compression = CompressionAlgorithm::BSHUF_LZ4; photon_energy_multiplier = 1.0f; images_per_file = 1000; data_reduction_factor_serialmx = 1.0; } DatasetSettings &DatasetSettings::ImagesPerTrigger(int64_t input) { check_max("Total number of images", input, 10*1000*1000); check_min("Total number of images", input, 0); images_per_trigger = input; return *this; } DatasetSettings &DatasetSettings::NumTriggers(int64_t input) { check_max("Total number of triggers", input, 10*1000*1000); check_min("Total number of triggers", input, 1); ntrigger = input; return *this; } DatasetSettings &DatasetSettings::PhotonEnergy_keV(float input) { check_finite("Energy (keV)", input); check_min("Energy (keV)", input, MIN_ENERGY); check_max("Energy (keV)", input, MAX_ENERGY); photon_energy_keV = input; return *this; } DatasetSettings &DatasetSettings::BeamX_pxl(float input) { check_finite("Beam center x", input); beam_x_pxl = input; return *this; } DatasetSettings &DatasetSettings::BeamY_pxl(float input) { check_finite("Beam center y", input); beam_y_pxl = input; return *this; } DatasetSettings &DatasetSettings::DetectorDistance_mm(float input) { check_finite("Detector distance (mm)", input); check_min("Detector distance (mm)", input, 1); detector_distance_mm = input; return *this; } DatasetSettings &DatasetSettings::FilePrefix(std::string input) { // File prefix with front slash is not allowed for security reasons if (input.front() == '/') throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Path cannot start with slash"); if (input.substr(0,3) == "../") throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Path cannot start with ../"); if (input.find("/../") != std::string::npos) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Path cannot contain /../"); if ((input.find("_master.h5") == input.length() - 10) && (input.length() > 10)) file_prefix = input.substr(0, input.length() - 10); else file_prefix = input; return *this; } DatasetSettings &DatasetSettings::Compression(CompressionAlgorithm input) { switch (input) { case CompressionAlgorithm::NO_COMPRESSION: case CompressionAlgorithm::BSHUF_LZ4: case CompressionAlgorithm::BSHUF_ZSTD: case CompressionAlgorithm::BSHUF_ZSTD_RLE: compression = input; break; default: throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Invalid value for enum parameter"); } return *this; } DatasetSettings &DatasetSettings::SetUnitCell(const std::optional &cell) { if (cell && (cell->a * cell->b * cell->c * cell->alpha * cell->beta * cell->gamma != 0.0)) { check_min("Angle alpha", cell->alpha, 0); check_min("Angle beta", cell->beta, 0); check_min("Angle gamma", cell->gamma, 0); check_max("Angle alpha", cell->alpha, 360); check_max("Angle beta", cell->beta, 360); check_max("Angle gamma", cell->gamma, 360); unit_cell = cell; } else unit_cell.reset(); return *this; } DatasetSettings &DatasetSettings::SpaceGroupNumber(int64_t input) { check_min("Space group number", input, 0); check_max("Space group number", input, 230); space_group_number = input; return *this; } DatasetSettings &DatasetSettings::SampleName(std::string input) { sample_name = input; return *this; } DatasetSettings &DatasetSettings::AttenuatorTransmission(const std::optional &input) { if (input) { check_finite("Attenuator transmission", input.value()); check_max("Attenuator transmission", input.value(), 1.0); check_min("Attenuator transmission", input.value(), 0.0); } attenuator_transmission = input; return *this; } DatasetSettings &DatasetSettings::TotalFlux(const std::optional &input) { if (input) { check_finite("Total flux", input.value()); check_max("Total flux", input.value(), 1.0); check_min("Total flux", input.value(), 0.0); } total_flux = input; return *this; } DatasetSettings &DatasetSettings::Goniometer(const std::optional &input) { if (input) { check_finite("Rotation angle increment", input->increment); if (input->increment == 0.0f) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Angle increment cannot be zero"); check_finite("Rotation angle start", input->start); goniometer = input; } return *this; } DatasetSettings &DatasetSettings::RotationAxis(const std::optional &c) { if (c) { if (c->Length() == 0.0) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Cannot use empty vector for omega"); rotation_axis = c->Normalize(); } else rotation_axis = c; return *this; } DatasetSettings &DatasetSettings::HeaderAppendix(const nlohmann::json &input) { header_appendix = input; return *this; } DatasetSettings &DatasetSettings::ImageAppendix(const nlohmann::json &input) { image_appendix = input; return *this; } DatasetSettings &DatasetSettings::PhotonEnergyMultiplayer(float input) { check_finite("Photon energy multiplier", input); check_min("Photon energy multiplier", input, 1/64.f); check_max("Photon energy multiplier", input, 4.f); photon_energy_multiplier = input; return *this; } DatasetSettings &DatasetSettings::FPGAOutputMode(FPGAPixelOutput input) { switch (input) { case FPGAPixelOutput::Auto: case FPGAPixelOutput::Int16: case FPGAPixelOutput::Uint16: case FPGAPixelOutput::Int32: case FPGAPixelOutput::Uint32: fpga_pixel_output = input; break; default: throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Invalid value for enum parameter"); } return *this; } std::optional DatasetSettings::GetAttenuatorTransmission() const { return attenuator_transmission; } std::optional DatasetSettings::GetTotalFlux() const { return total_flux; } std::optional DatasetSettings::GetGoniometer() const { return goniometer; } std::optional DatasetSettings::GetRotationAxis() const { return rotation_axis; } const nlohmann::json& DatasetSettings::GetHeaderAppendix() const { return header_appendix; } const nlohmann::json& DatasetSettings::GetImageAppendix() const { return image_appendix; } float DatasetSettings::GetPhotonEnergyMultiplier() const { return photon_energy_multiplier; } std::optional DatasetSettings::GetUnitCell() const { return unit_cell; } int64_t DatasetSettings::GetSpaceGroupNumber() const { return space_group_number; } FPGAPixelOutput DatasetSettings::GetFPGAOutputMode() const { return fpga_pixel_output; } std::string DatasetSettings::GetSampleName() const { return sample_name; } float DatasetSettings::GetPhotonEnergy_keV() const { return photon_energy_keV; } float DatasetSettings::GetWavelength_A() const { return WVL_1A_IN_KEV / photon_energy_keV; } float DatasetSettings::GetBeamX_pxl() const { return beam_x_pxl; } float DatasetSettings::GetBeamY_pxl() const { return beam_y_pxl; } float DatasetSettings::GetDetectorDistance_mm() const { return detector_distance_mm; } Coord DatasetSettings::GetScatteringVector() const { return {0, 0, photon_energy_keV / WVL_1A_IN_KEV}; } std::string DatasetSettings::GetFilePrefix() const { return file_prefix; } CompressionAlgorithm DatasetSettings::GetCompressionAlgorithm() const { return compression; } int64_t DatasetSettings::GetNumTriggers() const { return ntrigger; } int64_t DatasetSettings::GetImageNumPerTrigger() const { return images_per_trigger; } DatasetSettings &DatasetSettings::ImagesPerFile(int64_t input) { check_min("Images per file", input, 0); images_per_file = input; return *this; } int64_t DatasetSettings::GetImagesPerFile() const { return images_per_file; } DatasetSettings &DatasetSettings::LossyCompressionSerialMX(float input) { check_min("Data reduction factor for serial MX", input, 0.0); check_max("Data reduction factor for serial MX", input, 1.0); data_reduction_factor_serialmx = input; return *this; } float DatasetSettings::GetLossyCompressionSerialMX() const { return data_reduction_factor_serialmx; } DatasetSettings &DatasetSettings::RunNumber(const std::optional &input) { if (input) { check_min("Run number", input, 0); check_max("Run number", input, INT64_MAX); } run_number = input; return *this; } DatasetSettings & DatasetSettings::RunName(const std::optional &input) { if (input && input.value().empty()) run_name = {}; else run_name = input; return *this; } DatasetSettings &DatasetSettings::ExperimentGroup(const std::string &input) { group = input; return *this; } std::optional DatasetSettings::GetRunNumber() const { return run_number; } std::optional DatasetSettings::GetRunName() const { return run_name; } std::string DatasetSettings::GetExperimentGroup() const { return group; } std::optional DatasetSettings::GetImageTime() const { return image_time; } DatasetSettings &DatasetSettings::ImageTime(const std::optional input) { if (input && (input.value().count() == 0)) image_time = {}; else image_time = input; return *this; } DatasetSettings &DatasetSettings::LossyCompressionPoisson(std::optional input) { if (!input || (input == 0)) compression_poisson_factor = {}; else { check_min("Poisson compression factor", input.value(), 1); check_max("Poisson compression factor", input.value(), 16); compression_poisson_factor = input; } return *this; } std::optional DatasetSettings::GetLossyCompressionPoisson() const { return compression_poisson_factor; }