From 651552b4656c200ea76a0710ad7a5a1082b7c89b Mon Sep 17 00:00:00 2001 From: James Beilsten-Edmands <30625594+jbeilstenedmands@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:09:05 +0100 Subject: [PATCH 1/7] Use shared_ptr of the beam base class to enable polymorphism, rather than templates --- include/dx2/beam.h | 28 +++++++++++++++++++++++++++- include/dx2/experiment.h | 31 +++++++++++++++---------------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/include/dx2/beam.h b/include/dx2/beam.h index c855176..c4494b5 100644 --- a/include/dx2/beam.h +++ b/include/dx2/beam.h @@ -2,7 +2,7 @@ #define DX2_MODEL_BEAM_H #include #include - +#include using Eigen::Vector3d; using json = nlohmann::json; @@ -22,6 +22,7 @@ when serializing/deserializing to/from json. class BeamBase { // A base class for beam objects public: + virtual ~BeamBase() = default; BeamBase() = default; BeamBase(Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, @@ -301,4 +302,29 @@ json MonoElectronBeam::to_json() const { return MonochromaticBeam::to_json("electron"); } +std::shared_ptr make_beam(json beam_data){ + if (beam_data.find("wavelength") != beam_data.end()){ + return std::make_shared(beam_data); + } + else if (beam_data.find("wavelength_range") != beam_data.end()){ + return std::make_shared(beam_data); + } + else { + throw std::invalid_argument("Unknown beam type"); + } +} + +json make_beam_json(std::shared_ptr beamptr){ + // first try to cast to mono + MonochromaticBeam* monobeam = dynamic_cast(beamptr.get()); + if (monobeam != nullptr){ + return (*monobeam).to_json(); + } + PolychromaticBeam* polybeam = dynamic_cast(beamptr.get()); + if (polybeam != nullptr){ + return (*polybeam).to_json(); + } + throw std::runtime_error("Unable to cast base beam pointer for json creation"); +} + #endif // DX2_MODEL_BEAM_H diff --git a/include/dx2/experiment.h b/include/dx2/experiment.h index ceb70db..7cd2fd5 100644 --- a/include/dx2/experiment.h +++ b/include/dx2/experiment.h @@ -7,34 +7,34 @@ #include #include #include - +#include using Eigen::Vector3d; using json = nlohmann::json; -template class Experiment { +class Experiment { public: Experiment() = default; Experiment(json experiment_data); json to_json() const; Goniometer goniometer() const; - BeamType beam() const; + std::shared_ptr beam() const; Scan scan() const; Detector detector() const; Crystal crystal() const; void set_crystal(Crystal crystal); protected: - BeamType _beam{}; + std::shared_ptr _beam{}; Scan _scan{}; Goniometer _goniometer{}; Detector _detector{}; Crystal _crystal{}; }; -template -Experiment::Experiment(json experiment_data) { + +Experiment::Experiment(json experiment_data) { json beam_data = experiment_data["beam"][0]; - BeamType beam = BeamType(beam_data); + std::shared_ptr beam = make_beam(beam_data); json scan_data = experiment_data["scan"][0]; Scan scan(scan_data); json gonio_data = experiment_data["goniometer"][0]; @@ -54,7 +54,7 @@ Experiment::Experiment(json experiment_data) { } } -template json Experiment::to_json() const { +json Experiment::to_json() const { // save this experiment as an example experiment list json elist_out; // a list of potentially multiple experiments elist_out["__id__"] = "ExperimentList"; @@ -71,7 +71,7 @@ template json Experiment::to_json() const { // add the the actual models elist_out["scan"] = std::array{_scan.to_json()}; elist_out["goniometer"] = std::array{_goniometer.to_json()}; - elist_out["beam"] = std::array{_beam.to_json()}; + elist_out["beam"] = std::array{make_beam_json(_beam)}; elist_out["detector"] = std::array{_detector.to_json()}; if (_crystal.get_U_matrix().determinant()) { @@ -85,28 +85,27 @@ template json Experiment::to_json() const { return elist_out; } -template Scan Experiment::scan() const { +Scan Experiment::scan() const { return _scan; } -template Goniometer Experiment::goniometer() const { +Goniometer Experiment::goniometer() const { return _goniometer; } -template Detector Experiment::detector() const { +Detector Experiment::detector() const { return _detector; } -template Crystal Experiment::crystal() const { +Crystal Experiment::crystal() const { return _crystal; } -template -void Experiment::set_crystal(Crystal crystal) { +void Experiment::set_crystal(Crystal crystal) { _crystal = crystal; } -template BeamType Experiment::beam() const { +std::shared_ptr Experiment::beam() const { return _beam; } From c70623425bc78372dd3619ed66e012210f278850 Mon Sep 17 00:00:00 2001 From: James Beilsten-Edmands <30625594+jbeilstenedmands@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:47:00 +0100 Subject: [PATCH 2/7] Remove additional Xray/Electron subclasses, use probe enum instead --- include/dx2/beam.h | 102 ++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/include/dx2/beam.h b/include/dx2/beam.h index c4494b5..1b6ffd7 100644 --- a/include/dx2/beam.h +++ b/include/dx2/beam.h @@ -14,11 +14,10 @@ BeamBase defines most of the class attributes except the wavelength. The two subclasses are MonochromaticBeam and PolychromaticBeam, which define a single wavelength or wavelength range respectively. -MonochromaticBeam is subclassed further into MonoXrayBeam and -MonoElectronBeam, these simply set the correct probe name -when serializing/deserializing to/from json. */ +enum Probe { xray = 1, electron = 2, neutron = 3 }; + class BeamBase { // A base class for beam objects public: @@ -26,11 +25,13 @@ class BeamBase { BeamBase() = default; BeamBase(Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, - double flux, double transmission, double sample_to_source_distance); + double flux, double transmission, Probe probe, double sample_to_source_distance); protected: void init_from_json(json beam_data); - void add_to_json(json beam_data) const; + void add_to_json(json &beam_data) const; + Probe get_probe() const; + std::string get_probe_name() const; Vector3d sample_to_source_direction_{0.0, 0.0, 1.0}; // called direction_ in dxtbx double divergence_{0.0}; // "beam divergence - be more specific with name?" @@ -39,6 +40,7 @@ class BeamBase { double polarization_fraction_{0.999}; double flux_{0.0}; double transmission_{1.0}; + Probe probe_{Probe::xray}; double sample_to_source_distance_{0.0}; // FIXME is this really needed? }; @@ -51,9 +53,9 @@ class MonochromaticBeam : public BeamBase { MonochromaticBeam(double wavelength, Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, double flux, - double transmission, double sample_to_source_distance); + double transmission, Probe probe, double sample_to_source_distance); MonochromaticBeam(json beam_data); - json to_json(std::string probe) const; + json to_json() const; double get_wavelength() const; void set_wavelength(double wavelength); Vector3d get_s0() const; @@ -63,23 +65,6 @@ class MonochromaticBeam : public BeamBase { double wavelength_{0.0}; }; -class MonoXrayBeam : public MonochromaticBeam { - // Same as the parent class, except explicitly set a probe type when calling - // to_json - using MonochromaticBeam::MonochromaticBeam; - -public: - json to_json() const; -}; - -class MonoElectronBeam : public MonochromaticBeam { - // Same as the parent class, except explicitly set a probe type when calling - // to_json - using MonochromaticBeam::MonochromaticBeam; - -public: - json to_json() const; -}; class PolychromaticBeam : public BeamBase { // A polychromatic beam (i.e. a wavelength range) @@ -90,10 +75,10 @@ class PolychromaticBeam : public BeamBase { PolychromaticBeam(std::array wavelength_range, Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, - double flux, double transmission, + double flux, double transmission, Probe probe, double sample_to_source_distance); PolychromaticBeam(json beam_data); - json to_json(std::string probe) const; + json to_json() const; std::array get_wavelength_range() const; void set_wavelength_range(std::array wavelength_range); @@ -107,12 +92,12 @@ class PolychromaticBeam : public BeamBase { BeamBase::BeamBase(Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, double flux, - double transmission, double sample_to_source_distance) + double transmission, Probe probe, double sample_to_source_distance) : sample_to_source_direction_{direction}, divergence_{divergence}, sigma_divergence_{sigma_divergence}, polarization_normal_{polarization_normal}, polarization_fraction_{polarization_fraction}, flux_{flux}, - transmission_{transmission}, + transmission_{transmission}, probe_{probe}, sample_to_source_distance_{sample_to_source_distance} {} void BeamBase::init_from_json(json beam_data) { @@ -145,12 +130,24 @@ void BeamBase::init_from_json(json beam_data) { if (beam_data.find("transmission") != beam_data.end()) { transmission_ = beam_data["transmission"]; } + if (beam_data.find("probe") != beam_data.end()) { + std::string probe = beam_data["probe"]; + if (probe == "x-ray"){ + probe_ = Probe::xray; + } + else if (probe == "neutron"){ + probe_ = Probe::neutron; + } + else if (probe == "electron"){ + probe_ = Probe::electron; + } + } if (beam_data.find("sample_to_source_distance") != beam_data.end()) { sample_to_source_distance_ = beam_data["sample_to_source_distance"]; } } -void BeamBase::add_to_json(json beam_data) const { +void BeamBase::add_to_json(json &beam_data) const { // Add the members to the json object to prepare for serialization. beam_data["direction"] = sample_to_source_direction_; beam_data["divergence"] = divergence_; @@ -159,9 +156,29 @@ void BeamBase::add_to_json(json beam_data) const { beam_data["polarization_fraction"] = polarization_fraction_; beam_data["flux"] = flux_; beam_data["transmission"] = transmission_; + beam_data["probe"] = get_probe_name(); beam_data["sample_to_source_distance"] = sample_to_source_distance_; } +Probe BeamBase::get_probe() const { + return probe_; +} + +std::string BeamBase::get_probe_name() const { + // Return a name that matches NeXus definitions from + // https://manual.nexusformat.org/classes/base_classes/NXsource.html + switch (probe_) { + case xray: + return std::string("x-ray"); + case electron: + return std::string("electron"); + case neutron: + return std::string("neutron"); + default: + throw std::runtime_error("Unknown probe type"); + } +} + // MonochromaticBeam definitions // full constructor @@ -169,7 +186,7 @@ MonochromaticBeam::MonochromaticBeam(double wavelength, Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, double flux, - double transmission, + double transmission, Probe probe, double sample_to_source_distance) : BeamBase{direction, divergence, @@ -178,6 +195,7 @@ MonochromaticBeam::MonochromaticBeam(double wavelength, Vector3d direction, polarization_fraction, flux, transmission, + probe, sample_to_source_distance}, wavelength_{wavelength} {} @@ -205,9 +223,9 @@ MonochromaticBeam::MonochromaticBeam(json beam_data) { } // serialize to json format -json MonochromaticBeam::to_json(std::string probe = "x-ray") const { +json MonochromaticBeam::to_json() const { // create a json object that conforms to a dials model serialization. - json beam_data = {{"__id__", "monochromatic"}, {"probe", probe}}; + json beam_data = {{"__id__", "monochromatic"}}; beam_data["wavelength"] = wavelength_; add_to_json(beam_data); return beam_data; @@ -236,6 +254,7 @@ PolychromaticBeam::PolychromaticBeam(std::array wavelength_range, Vector3d polarization_normal, double polarization_fraction, double flux, double transmission, + Probe probe, double sample_to_source_distance) : BeamBase{direction, divergence, @@ -244,6 +263,7 @@ PolychromaticBeam::PolychromaticBeam(std::array wavelength_range, polarization_fraction, flux, transmission, + probe, sample_to_source_distance}, wavelength_range_{wavelength_range} {} @@ -271,9 +291,9 @@ PolychromaticBeam::PolychromaticBeam(json beam_data) { } // serialize to json format -json PolychromaticBeam::to_json(std::string probe = "x-ray") const { +json PolychromaticBeam::to_json() const { // create a json object that conforms to a dials model serialization. - json beam_data = {{"__id__", "polychromatic"}, {"probe", probe}}; + json beam_data = {{"__id__", "polychromatic"}}; beam_data["wavelength_range"] = wavelength_range_; add_to_json(beam_data); return beam_data; @@ -287,20 +307,8 @@ void PolychromaticBeam::set_wavelength_range( wavelength_range_ = wavelength_range; } -// MonoXrayBeam definitions - -json MonoXrayBeam::to_json() const { - // call the parent function with the correct probe name (which is the same as - // the default in this case) - return MonochromaticBeam::to_json("x-ray"); -} - -// MonoElectronBeam definitions -json MonoElectronBeam::to_json() const { - // call the parent function with the correct probe name - return MonochromaticBeam::to_json("electron"); -} +// Classes to handle run-time polymorphism for json serialization/deserialization. std::shared_ptr make_beam(json beam_data){ if (beam_data.find("wavelength") != beam_data.end()){ From 153be8fc95725b1d38fb1f71c212cf8e6f876381 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:37:32 +0000 Subject: [PATCH 3/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- include/dx2/beam.h | 56 +++++++++++++++++++--------------------- include/dx2/experiment.h | 27 +++++-------------- 2 files changed, 34 insertions(+), 49 deletions(-) diff --git a/include/dx2/beam.h b/include/dx2/beam.h index 1b6ffd7..e5f2dba 100644 --- a/include/dx2/beam.h +++ b/include/dx2/beam.h @@ -1,8 +1,8 @@ #ifndef DX2_MODEL_BEAM_H #define DX2_MODEL_BEAM_H #include -#include #include +#include using Eigen::Vector3d; using json = nlohmann::json; @@ -25,7 +25,8 @@ class BeamBase { BeamBase() = default; BeamBase(Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, - double flux, double transmission, Probe probe, double sample_to_source_distance); + double flux, double transmission, Probe probe, + double sample_to_source_distance); protected: void init_from_json(json beam_data); @@ -53,7 +54,8 @@ class MonochromaticBeam : public BeamBase { MonochromaticBeam(double wavelength, Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, double flux, - double transmission, Probe probe, double sample_to_source_distance); + double transmission, Probe probe, + double sample_to_source_distance); MonochromaticBeam(json beam_data); json to_json() const; double get_wavelength() const; @@ -65,7 +67,6 @@ class MonochromaticBeam : public BeamBase { double wavelength_{0.0}; }; - class PolychromaticBeam : public BeamBase { // A polychromatic beam (i.e. a wavelength range) public: @@ -92,7 +93,8 @@ class PolychromaticBeam : public BeamBase { BeamBase::BeamBase(Vector3d direction, double divergence, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, double flux, - double transmission, Probe probe, double sample_to_source_distance) + double transmission, Probe probe, + double sample_to_source_distance) : sample_to_source_direction_{direction}, divergence_{divergence}, sigma_divergence_{sigma_divergence}, polarization_normal_{polarization_normal}, @@ -132,13 +134,11 @@ void BeamBase::init_from_json(json beam_data) { } if (beam_data.find("probe") != beam_data.end()) { std::string probe = beam_data["probe"]; - if (probe == "x-ray"){ + if (probe == "x-ray") { probe_ = Probe::xray; - } - else if (probe == "neutron"){ + } else if (probe == "neutron") { probe_ = Probe::neutron; - } - else if (probe == "electron"){ + } else if (probe == "electron") { probe_ = Probe::electron; } } @@ -160,9 +160,7 @@ void BeamBase::add_to_json(json &beam_data) const { beam_data["sample_to_source_distance"] = sample_to_source_distance_; } -Probe BeamBase::get_probe() const { - return probe_; -} +Probe BeamBase::get_probe() const { return probe_; } std::string BeamBase::get_probe_name() const { // Return a name that matches NeXus definitions from @@ -253,8 +251,7 @@ PolychromaticBeam::PolychromaticBeam(std::array wavelength_range, double sigma_divergence, Vector3d polarization_normal, double polarization_fraction, double flux, - double transmission, - Probe probe, + double transmission, Probe probe, double sample_to_source_distance) : BeamBase{direction, divergence, @@ -307,32 +304,33 @@ void PolychromaticBeam::set_wavelength_range( wavelength_range_ = wavelength_range; } +// Classes to handle run-time polymorphism for json +// serialization/deserialization. -// Classes to handle run-time polymorphism for json serialization/deserialization. - -std::shared_ptr make_beam(json beam_data){ - if (beam_data.find("wavelength") != beam_data.end()){ +std::shared_ptr make_beam(json beam_data) { + if (beam_data.find("wavelength") != beam_data.end()) { return std::make_shared(beam_data); - } - else if (beam_data.find("wavelength_range") != beam_data.end()){ + } else if (beam_data.find("wavelength_range") != beam_data.end()) { return std::make_shared(beam_data); - } - else { + } else { throw std::invalid_argument("Unknown beam type"); } } -json make_beam_json(std::shared_ptr beamptr){ +json make_beam_json(std::shared_ptr beamptr) { // first try to cast to mono - MonochromaticBeam* monobeam = dynamic_cast(beamptr.get()); - if (monobeam != nullptr){ + MonochromaticBeam *monobeam = + dynamic_cast(beamptr.get()); + if (monobeam != nullptr) { return (*monobeam).to_json(); } - PolychromaticBeam* polybeam = dynamic_cast(beamptr.get()); - if (polybeam != nullptr){ + PolychromaticBeam *polybeam = + dynamic_cast(beamptr.get()); + if (polybeam != nullptr) { return (*polybeam).to_json(); } - throw std::runtime_error("Unable to cast base beam pointer for json creation"); + throw std::runtime_error( + "Unable to cast base beam pointer for json creation"); } #endif // DX2_MODEL_BEAM_H diff --git a/include/dx2/experiment.h b/include/dx2/experiment.h index 7cd2fd5..6129762 100644 --- a/include/dx2/experiment.h +++ b/include/dx2/experiment.h @@ -6,8 +6,8 @@ #include #include #include -#include #include +#include using Eigen::Vector3d; using json = nlohmann::json; @@ -31,7 +31,6 @@ class Experiment { Crystal _crystal{}; }; - Experiment::Experiment(json experiment_data) { json beam_data = experiment_data["beam"][0]; std::shared_ptr beam = make_beam(beam_data); @@ -85,28 +84,16 @@ json Experiment::to_json() const { return elist_out; } -Scan Experiment::scan() const { - return _scan; -} +Scan Experiment::scan() const { return _scan; } -Goniometer Experiment::goniometer() const { - return _goniometer; -} +Goniometer Experiment::goniometer() const { return _goniometer; } -Detector Experiment::detector() const { - return _detector; -} +Detector Experiment::detector() const { return _detector; } -Crystal Experiment::crystal() const { - return _crystal; -} +Crystal Experiment::crystal() const { return _crystal; } -void Experiment::set_crystal(Crystal crystal) { - _crystal = crystal; -} +void Experiment::set_crystal(Crystal crystal) { _crystal = crystal; } -std::shared_ptr Experiment::beam() const { - return _beam; -} +std::shared_ptr Experiment::beam() const { return _beam; } #endif // DX2_MODEL_EXPERIMENT_H \ No newline at end of file From 9a6a571e6456c36c4f1ce63d497de400457c43f3 Mon Sep 17 00:00:00 2001 From: James Beilsten-Edmands <30625594+jbeilstenedmands@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:44:41 +0100 Subject: [PATCH 4/7] Template get_beam method, return reference not pointer. --- include/dx2/experiment.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/dx2/experiment.h b/include/dx2/experiment.h index 6129762..3b7a01e 100644 --- a/include/dx2/experiment.h +++ b/include/dx2/experiment.h @@ -17,7 +17,8 @@ class Experiment { Experiment(json experiment_data); json to_json() const; Goniometer goniometer() const; - std::shared_ptr beam() const; + template + BeamType& get_beam() const; Scan scan() const; Detector detector() const; Crystal crystal() const; @@ -94,6 +95,14 @@ Crystal Experiment::crystal() const { return _crystal; } void Experiment::set_crystal(Crystal crystal) { _crystal = crystal; } -std::shared_ptr Experiment::beam() const { return _beam; } +template +BeamType& Experiment::get_beam() const { + BeamType* beam = dynamic_cast(_beam.get()); + if (beam == nullptr){ + std::cerr << "Unable to return beam type " + std::string(typeid(BeamType).name()) + " (beam type is " + std::string(typeid(*_beam.get()).name())+ ")"< Date: Wed, 2 Apr 2025 14:45:39 +0000 Subject: [PATCH 5/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- include/dx2/experiment.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/include/dx2/experiment.h b/include/dx2/experiment.h index 3b7a01e..ed25695 100644 --- a/include/dx2/experiment.h +++ b/include/dx2/experiment.h @@ -17,8 +17,7 @@ class Experiment { Experiment(json experiment_data); json to_json() const; Goniometer goniometer() const; - template - BeamType& get_beam() const; + template BeamType &get_beam() const; Scan scan() const; Detector detector() const; Crystal crystal() const; @@ -95,11 +94,13 @@ Crystal Experiment::crystal() const { return _crystal; } void Experiment::set_crystal(Crystal crystal) { _crystal = crystal; } -template -BeamType& Experiment::get_beam() const { - BeamType* beam = dynamic_cast(_beam.get()); - if (beam == nullptr){ - std::cerr << "Unable to return beam type " + std::string(typeid(BeamType).name()) + " (beam type is " + std::string(typeid(*_beam.get()).name())+ ")"< BeamType &Experiment::get_beam() const { + BeamType *beam = dynamic_cast(_beam.get()); + if (beam == nullptr) { + std::cerr << "Unable to return beam type " + + std::string(typeid(BeamType).name()) + " (beam type is " + + std::string(typeid(*_beam.get()).name()) + ")" + << std::endl; std::exit(1); } return *beam; From ec7c67086f2b5b4ee8c0d3a3371b4e25b59db2b7 Mon Sep 17 00:00:00 2001 From: James Beilsten-Edmands <30625594+jbeilstenedmands@users.noreply.github.com> Date: Thu, 3 Apr 2025 14:28:38 +0100 Subject: [PATCH 6/7] Add a typed experiment class --- include/dx2/beam.h | 12 ++-- include/dx2/experiment.h | 118 +++++++++++++++++++++++++++++++++++---- 2 files changed, 114 insertions(+), 16 deletions(-) diff --git a/include/dx2/beam.h b/include/dx2/beam.h index e5f2dba..5b8588f 100644 --- a/include/dx2/beam.h +++ b/include/dx2/beam.h @@ -27,12 +27,12 @@ class BeamBase { Vector3d polarization_normal, double polarization_fraction, double flux, double transmission, Probe probe, double sample_to_source_distance); + Probe get_probe() const; + std::string get_probe_name() const; protected: void init_from_json(json beam_data); void add_to_json(json &beam_data) const; - Probe get_probe() const; - std::string get_probe_name() const; Vector3d sample_to_source_direction_{0.0, 0.0, 1.0}; // called direction_ in dxtbx double divergence_{0.0}; // "beam divergence - be more specific with name?" @@ -41,7 +41,7 @@ class BeamBase { double polarization_fraction_{0.999}; double flux_{0.0}; double transmission_{1.0}; - Probe probe_{Probe::xray}; + Probe probe_{xray}; double sample_to_source_distance_{0.0}; // FIXME is this really needed? }; @@ -156,7 +156,7 @@ void BeamBase::add_to_json(json &beam_data) const { beam_data["polarization_fraction"] = polarization_fraction_; beam_data["flux"] = flux_; beam_data["transmission"] = transmission_; - beam_data["probe"] = get_probe_name(); + beam_data["probe"] = "x-ray"; //FIXME get_probe_name() not working? beam_data["sample_to_source_distance"] = sample_to_source_distance_; } @@ -304,7 +304,7 @@ void PolychromaticBeam::set_wavelength_range( wavelength_range_ = wavelength_range; } -// Classes to handle run-time polymorphism for json +// Functions to handle run-time polymorphism for json // serialization/deserialization. std::shared_ptr make_beam(json beam_data) { @@ -313,7 +313,7 @@ std::shared_ptr make_beam(json beam_data) { } else if (beam_data.find("wavelength_range") != beam_data.end()) { return std::make_shared(beam_data); } else { - throw std::invalid_argument("Unknown beam type"); + throw std::runtime_error("Unrecognised beam type in json data"); } } diff --git a/include/dx2/experiment.h b/include/dx2/experiment.h index ed25695..46c7414 100644 --- a/include/dx2/experiment.h +++ b/include/dx2/experiment.h @@ -15,9 +15,10 @@ class Experiment { public: Experiment() = default; Experiment(json experiment_data); + Experiment(std::shared_ptr beam, Scan scan, Goniometer gonio, Detector detector); json to_json() const; Goniometer goniometer() const; - template BeamType &get_beam() const; + std::shared_ptr beam() const; Scan scan() const; Detector detector() const; Crystal crystal() const; @@ -31,6 +32,16 @@ class Experiment { Crystal _crystal{}; }; +Experiment::Experiment(std::shared_ptr beam, + Scan scan, + Goniometer gonio, + Detector detector){ + this->_beam = beam; + this->_scan = scan; + this->_goniometer = gonio; + this->_detector = detector; + } + Experiment::Experiment(json experiment_data) { json beam_data = experiment_data["beam"][0]; std::shared_ptr beam = make_beam(beam_data); @@ -84,6 +95,8 @@ json Experiment::to_json() const { return elist_out; } +std::shared_ptr Experiment::beam() const { return _beam; } + Scan Experiment::scan() const { return _scan; } Goniometer Experiment::goniometer() const { return _goniometer; } @@ -94,16 +107,101 @@ Crystal Experiment::crystal() const { return _crystal; } void Experiment::set_crystal(Crystal crystal) { _crystal = crystal; } -template BeamType &Experiment::get_beam() const { - BeamType *beam = dynamic_cast(_beam.get()); - if (beam == nullptr) { - std::cerr << "Unable to return beam type " + - std::string(typeid(BeamType).name()) + " (beam type is " + - std::string(typeid(*_beam.get()).name()) + ")" - << std::endl; - std::exit(1); + +template class TypedExperiment { + public: + TypedExperiment(BeamType& beam, Scan scan, Goniometer gonio, Detector detector); + BeamType& get_beam() const; + Scan scan() const; + Detector detector() const; + Goniometer goniometer() const; + Crystal crystal() const; + void set_crystal(Crystal crystal); + json to_json() const; + + private: + BeamType& _beam; + Scan _scan; + Goniometer _goniometer; + Detector _detector; + Crystal _crystal; +}; + +template +TypedExperiment::TypedExperiment(BeamType& beam, Scan scan, Goniometer gonio, Detector detector) : _beam(beam), _scan(scan), _goniometer(gonio), _detector(detector){} + +template +BeamType& TypedExperiment::get_beam() const { + return _beam; +} + +template +Scan TypedExperiment::scan() const { return _scan; } + +template +Goniometer TypedExperiment::goniometer() const { return _goniometer; } + +template +Detector TypedExperiment::detector() const { return _detector; } + +template +Crystal TypedExperiment::crystal() const { return _crystal; } + +template +void TypedExperiment::set_crystal(Crystal crystal) { _crystal = crystal; } + +template +json TypedExperiment::to_json() const { + // save this experiment as an example experiment list + json elist_out; // a list of potentially multiple experiments + elist_out["__id__"] = "ExperimentList"; + json expt_out; // our single experiment + // no imageset (for now?). + expt_out["__id__"] = "Experiment"; + expt_out["identifier"] = "test"; + expt_out["beam"] = + 0; // the indices of the models that will correspond to our experiment + expt_out["detector"] = 0; + expt_out["goniometer"] = 0; + expt_out["scan"] = 0; + + // add the the actual models + elist_out["scan"] = std::array{_scan.to_json()}; + elist_out["goniometer"] = std::array{_goniometer.to_json()}; + elist_out["beam"] = std::array{_beam.to_json()}; + elist_out["detector"] = std::array{_detector.to_json()}; + + if (_crystal.get_U_matrix().determinant()) { + expt_out["crystal"] = 0; + elist_out["crystal"] = std::array{_crystal.to_json()}; + } else { + elist_out["crystal"] = std::array{}; } - return *beam; + + elist_out["experiment"] = std::array{expt_out}; + return elist_out; +} + + + +// factory function to make a typed experiment +template +TypedExperiment make_typed_experiment(json experiment_data){ // can add other types e.g. detector, scan + json beam_data = experiment_data["beam"][0]; + std::shared_ptr beamptr = make_beam(beam_data); // may raise std::runtime_error + + // Make sure we can cast to the requested types. + BeamType* converted = dynamic_cast(beamptr.get()); + if (converted == nullptr){ + throw std::runtime_error("Unable to make requested beam type"); + } + json scan_data = experiment_data["scan"][0]; + Scan scan(scan_data); + json gonio_data = experiment_data["goniometer"][0]; + Goniometer gonio(gonio_data); + json detector_data = experiment_data["detector"][0]; + Detector detector(detector_data); + return TypedExperiment(*converted, scan, gonio, detector); } #endif // DX2_MODEL_EXPERIMENT_H \ No newline at end of file From 7a9d08b936eebdbc555b159c2b9ba220c5034692 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 13:32:17 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- include/dx2/beam.h | 2 +- include/dx2/experiment.h | 109 +++++++++++++++++++++------------------ 2 files changed, 60 insertions(+), 51 deletions(-) diff --git a/include/dx2/beam.h b/include/dx2/beam.h index 5b8588f..e088a1b 100644 --- a/include/dx2/beam.h +++ b/include/dx2/beam.h @@ -156,7 +156,7 @@ void BeamBase::add_to_json(json &beam_data) const { beam_data["polarization_fraction"] = polarization_fraction_; beam_data["flux"] = flux_; beam_data["transmission"] = transmission_; - beam_data["probe"] = "x-ray"; //FIXME get_probe_name() not working? + beam_data["probe"] = "x-ray"; // FIXME get_probe_name() not working? beam_data["sample_to_source_distance"] = sample_to_source_distance_; } diff --git a/include/dx2/experiment.h b/include/dx2/experiment.h index 46c7414..b9a76da 100644 --- a/include/dx2/experiment.h +++ b/include/dx2/experiment.h @@ -15,7 +15,8 @@ class Experiment { public: Experiment() = default; Experiment(json experiment_data); - Experiment(std::shared_ptr beam, Scan scan, Goniometer gonio, Detector detector); + Experiment(std::shared_ptr beam, Scan scan, Goniometer gonio, + Detector detector); json to_json() const; Goniometer goniometer() const; std::shared_ptr beam() const; @@ -32,15 +33,13 @@ class Experiment { Crystal _crystal{}; }; -Experiment::Experiment(std::shared_ptr beam, - Scan scan, - Goniometer gonio, - Detector detector){ - this->_beam = beam; - this->_scan = scan; - this->_goniometer = gonio; - this->_detector = detector; - } +Experiment::Experiment(std::shared_ptr beam, Scan scan, + Goniometer gonio, Detector detector) { + this->_beam = beam; + this->_scan = scan; + this->_goniometer = gonio; + this->_detector = detector; +} Experiment::Experiment(json experiment_data) { json beam_data = experiment_data["beam"][0]; @@ -107,51 +106,61 @@ Crystal Experiment::crystal() const { return _crystal; } void Experiment::set_crystal(Crystal crystal) { _crystal = crystal; } +template class TypedExperiment { +public: + TypedExperiment(BeamType &beam, Scan scan, Goniometer gonio, + Detector detector); + BeamType &get_beam() const; + Scan scan() const; + Detector detector() const; + Goniometer goniometer() const; + Crystal crystal() const; + void set_crystal(Crystal crystal); + json to_json() const; -template class TypedExperiment { - public: - TypedExperiment(BeamType& beam, Scan scan, Goniometer gonio, Detector detector); - BeamType& get_beam() const; - Scan scan() const; - Detector detector() const; - Goniometer goniometer() const; - Crystal crystal() const; - void set_crystal(Crystal crystal); - json to_json() const; - - private: - BeamType& _beam; - Scan _scan; - Goniometer _goniometer; - Detector _detector; - Crystal _crystal; +private: + BeamType &_beam; + Scan _scan; + Goniometer _goniometer; + Detector _detector; + Crystal _crystal; }; -template -TypedExperiment::TypedExperiment(BeamType& beam, Scan scan, Goniometer gonio, Detector detector) : _beam(beam), _scan(scan), _goniometer(gonio), _detector(detector){} +template +TypedExperiment::TypedExperiment(BeamType &beam, Scan scan, + Goniometer gonio, Detector detector) + : _beam(beam), _scan(scan), _goniometer(gonio), _detector(detector) {} -template -BeamType& TypedExperiment::get_beam() const { +template +BeamType &TypedExperiment::get_beam() const { return _beam; } -template -Scan TypedExperiment::scan() const { return _scan; } +template Scan TypedExperiment::scan() const { + return _scan; +} -template -Goniometer TypedExperiment::goniometer() const { return _goniometer; } +template +Goniometer TypedExperiment::goniometer() const { + return _goniometer; +} -template -Detector TypedExperiment::detector() const { return _detector; } +template +Detector TypedExperiment::detector() const { + return _detector; +} -template -Crystal TypedExperiment::crystal() const { return _crystal; } +template +Crystal TypedExperiment::crystal() const { + return _crystal; +} -template -void TypedExperiment::set_crystal(Crystal crystal) { _crystal = crystal; } +template +void TypedExperiment::set_crystal(Crystal crystal) { + _crystal = crystal; +} -template -json TypedExperiment::to_json() const { +template json TypedExperiment::to_json() const { // save this experiment as an example experiment list json elist_out; // a list of potentially multiple experiments elist_out["__id__"] = "ExperimentList"; @@ -182,17 +191,17 @@ json TypedExperiment::to_json() const { return elist_out; } - - // factory function to make a typed experiment -template -TypedExperiment make_typed_experiment(json experiment_data){ // can add other types e.g. detector, scan +template +TypedExperiment make_typed_experiment( + json experiment_data) { // can add other types e.g. detector, scan json beam_data = experiment_data["beam"][0]; - std::shared_ptr beamptr = make_beam(beam_data); // may raise std::runtime_error + std::shared_ptr beamptr = + make_beam(beam_data); // may raise std::runtime_error // Make sure we can cast to the requested types. - BeamType* converted = dynamic_cast(beamptr.get()); - if (converted == nullptr){ + BeamType *converted = dynamic_cast(beamptr.get()); + if (converted == nullptr) { throw std::runtime_error("Unable to make requested beam type"); } json scan_data = experiment_data["scan"][0];