Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some example models #1

Merged
merged 27 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
74f6f4d
First attempt at dx2 models
jbeilstenedmands Nov 6, 2024
61809e6
Add set of beam classes to replicate dxtbx models for mono, poly beam…
jbeilstenedmands Nov 7, 2024
3ef2b04
Add reading into simple detector from json
jbeilstenedmands Nov 7, 2024
d1a2d21
Make single-panel detector model
jbeilstenedmands Nov 8, 2024
028d9f2
Start adding a crystal model, using gemmi for unit cell
jbeilstenedmands Nov 13, 2024
febe78f
Tidying
jbeilstenedmands Nov 13, 2024
4b73746
Add space group to the crystal, to/from json methods
jbeilstenedmands Nov 26, 2024
f249f35
Add ray intersection
jbeilstenedmands Nov 27, 2024
b9241ae
Add gemmi submodule
jbeilstenedmands Dec 3, 2024
3ac36ff
Add crystal test
jbeilstenedmands Dec 3, 2024
6c57a52
Add gemmi as a softlink and submodule
jbeilstenedmands Dec 3, 2024
8c30136
Add a requirements.txt
jbeilstenedmands Dec 3, 2024
de9fca0
Be more specific
jbeilstenedmands Dec 3, 2024
5904f1b
Just add gemmi as dependency
jbeilstenedmands Dec 3, 2024
f29ecbf
Typo
jbeilstenedmands Dec 3, 2024
d8da1ed
Add github actions
jbeilstenedmands Dec 4, 2024
072c7cb
Add mac tests as well
jbeilstenedmands Dec 4, 2024
7f11773
Remove unneeded env
jbeilstenedmands Dec 4, 2024
02cc4ea
Add function to read data from h5 processing file and test
jbeilstenedmands Dec 5, 2024
1c1de81
Run in correct dir
jbeilstenedmands Dec 5, 2024
ec8fcab
Don't pick up other tests
jbeilstenedmands Dec 5, 2024
190f148
Try something
jbeilstenedmands Dec 5, 2024
972fa9f
Working example with add_test
jbeilstenedmands Dec 5, 2024
f6d8095
Add test labels
jbeilstenedmands Dec 5, 2024
4318508
Correctly link to hdf5
jbeilstenedmands Dec 10, 2024
3898dc0
Add a basic experiment class
jbeilstenedmands Jan 17, 2025
de68a87
Update some comments
jbeilstenedmands Jan 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/build_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
on: [push, pull_request]

jobs:
build_test:
name: Build/Test
strategy:
matrix:
os: [ubuntu, macOS]
defaults:
run:
shell: bash -l {0}
runs-on: ${{ matrix.os }}-latest
steps:
- uses: actions/checkout@v4
- uses: conda-incubator/setup-miniconda@v3
with:
channels: conda-forge
- name: Install dependencies
run: |
conda env update --file requirements.txt --name test
conda list
- name: Build
run: mkdir build && cd build && cmake .. && make
- name: Run tests
run: cd build && ctest -L dx2tests --output-on-failure
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
# #######################################################################
# External Dependencies
find_package(HDF5 REQUIRED)
find_package(gemmi CONFIG REQUIRED)

# #######################################################################
# Automatic Dependencies
Expand Down Expand Up @@ -107,4 +108,4 @@ install(
)
export(EXPORT DX2Targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/DX2Targets.cmake"
)
)
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

## Quick Start

- Ensure you have an environment with CMake and compilers available
- Ensure you have an environment with CMake and compilers available (see requirements.txt)
- Dependencies:
- HDF5 (Required)
- gemmi (Required)
- [Eigen](https://eigen.tuxfamily.org/) (Optional, downloaded if missing)
- [nlohmann/json](https://github.com/nlohmann/json) (Optional, downloaded if missing)
- [{fmt}](https://github.com/fmtlib/fmt) (Optional, downloaded if missing)
Expand All @@ -15,11 +16,11 @@ mkdir build
cd build
cmake ..
```
You can then build with `make`, and run the tests with `make test`.
You can then build with `make`, and run the test with `./tests/test_make_models`.

## Writing And Running Tests

See https://google.github.io/googletest/primer.html. The `tests/` subdirectory
See https://google.github.io/googletest/primer.html. The `tests` subdirectory
has an example test.

You can run test executables directly, run `ctest`, or run `make test` (or
Expand Down
2 changes: 2 additions & 0 deletions dx2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ target_link_libraries(
Eigen3::Eigen
nlohmann_json::nlohmann_json
hdf5::hdf5
gemmi::gemmi_cpp
)

target_include_directories(
dx2 PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(dx2 PUBLIC $<TARGET_NAME_IF_EXISTS:hdf5::hdf5>)
288 changes: 288 additions & 0 deletions include/dx2/beam.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
#ifndef DX2_MODEL_BEAM_H
#define DX2_MODEL_BEAM_H
#include <Eigen/Dense>
#include <nlohmann/json.hpp>

using Eigen::Vector3d;
using json = nlohmann::json;

/*
This file defines a set of Beam classes.

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.
*/

class BeamBase {
// A base class for beam objects
public:
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);

protected:
void init_from_json(json beam_data);
void add_to_json(json beam_data) 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?"
double sigma_divergence_{0.0}; // standard deviation of the beam divergence
Vector3d polarization_normal_{0.0,1.0,0.0};
double polarization_fraction_{0.999};
double flux_{0.0};
double transmission_{1.0};
double sample_to_source_distance_{0.0}; // FIXME is this really needed?
};

class MonochromaticBeam: public BeamBase {
// A monochromatic beam (i.e. single wavelength value)
public:
MonochromaticBeam()=default;
MonochromaticBeam(double wavelength);
MonochromaticBeam(Vector3d s0);
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);
MonochromaticBeam(json beam_data);
json to_json(std::string probe) const;
double get_wavelength() const;
void set_wavelength(double wavelength);
Vector3d get_s0() const;
void set_s0(Vector3d s0);

protected:
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)
public:
PolychromaticBeam()=default;
PolychromaticBeam(std::array<double, 2> wavelength_range);
PolychromaticBeam(std::array<double, 2> wavelength_range, Vector3d direction);
PolychromaticBeam(std::array<double, 2> wavelength_range, Vector3d direction, double divergence,
double sigma_divergence, Vector3d polarization_normal,
double polarization_fraction, double flux,
double transmission, double sample_to_source_distance);
PolychromaticBeam(json beam_data);
json to_json(std::string probe) const;
std::array<double, 2> get_wavelength_range() const;
void set_wavelength_range(std::array<double, 2> wavelength_range);

protected:
std::array<double, 2> wavelength_range_{{0.0, 0.0}};
};


// BeamBase definitions

// full constructor
BeamBase::BeamBase(
Vector3d direction, double divergence,
double sigma_divergence, Vector3d polarization_normal,
double polarization_fraction, double flux,
double transmission, 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},
sample_to_source_distance_{sample_to_source_distance} {}

void BeamBase::init_from_json(json beam_data){
// Load values from a json object.
// Allow these to not be present so we can load a simple json dict without all these items.
if (beam_data.find("direction") != beam_data.end()){
Vector3d direction{{beam_data["direction"][0], beam_data["direction"][1], beam_data["direction"][2]}};
sample_to_source_direction_ = direction;
}
if (beam_data.find("divergence") != beam_data.end()){
divergence_ = beam_data["divergence"];
}
if (beam_data.find("sigma_divergence") != beam_data.end()){
sigma_divergence_ = beam_data["sigma_divergence"];
}
if (beam_data.find("polarization_normal") != beam_data.end()){
Vector3d pn{
{beam_data["polarization_normal"][0],
beam_data["polarization_normal"][1],
beam_data["polarization_normal"][2]}};
polarization_normal_ = pn;
}
if (beam_data.find("polarization_fraction") != beam_data.end()){
polarization_fraction_ = beam_data["polarization_fraction"];
}
if (beam_data.find("flux") != beam_data.end()){
flux_ = beam_data["flux"];
}
if (beam_data.find("transmission") != beam_data.end()){
transmission_ = beam_data["transmission"];
}
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 {
// Add the members to the json object to prepare for serialization.
beam_data["direction"] = sample_to_source_direction_;
beam_data["divergence"] = divergence_;
beam_data["sigma_divergence"] = sigma_divergence_;
beam_data["polarization_normal"] = polarization_normal_;
beam_data["polarization_fraction"] = polarization_fraction_;
beam_data["flux"] = flux_;
beam_data["transmission"] = transmission_;
beam_data["sample_to_source_distance"] = sample_to_source_distance_;
}


// MonochromaticBeam definitions

// full constructor
MonochromaticBeam::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)
: BeamBase{direction, divergence, sigma_divergence,
polarization_normal, polarization_fraction,
flux, transmission, sample_to_source_distance}, wavelength_{wavelength} {}

MonochromaticBeam::MonochromaticBeam(double wavelength) : wavelength_{wavelength} {}

MonochromaticBeam::MonochromaticBeam(Vector3d s0){
double len = s0.norm();
wavelength_ = 1.0 / len;
sample_to_source_direction_ = -1.0 * s0 / len;
}

// constructor from json data
MonochromaticBeam::MonochromaticBeam(json beam_data) {
// minimal required keys
std::vector<std::string> required_keys = {"wavelength"};
for (const auto &key : required_keys) {
if (beam_data.find(key) == beam_data.end()) {
throw std::invalid_argument(
"Key " + key + " is missing from the input beam JSON");
}
}
wavelength_ = beam_data["wavelength"];
init_from_json(beam_data);
}

// serialize to json format
json MonochromaticBeam::to_json(std::string probe="x-ray") const {
// create a json object that conforms to a dials model serialization.
json beam_data = {{"__id__", "monochromatic"}, {"probe", probe}};
beam_data["wavelength"] = wavelength_;
add_to_json(beam_data);
return beam_data;
}

double MonochromaticBeam::get_wavelength() const {
return wavelength_;
}
void MonochromaticBeam::set_wavelength(double wavelength){
wavelength_ = wavelength;
}

Vector3d MonochromaticBeam::get_s0() const {
return -sample_to_source_direction_ / wavelength_;
}
void MonochromaticBeam::set_s0(Vector3d s0){
double len = s0.norm();
wavelength_ = 1.0 / len;
sample_to_source_direction_ = -1.0 * s0 / len;
}


// PolychromaticBeam definitions

// full constructor
PolychromaticBeam::PolychromaticBeam(
std::array<double,2> wavelength_range, Vector3d direction, double divergence,
double sigma_divergence, Vector3d polarization_normal,
double polarization_fraction, double flux,
double transmission, double sample_to_source_distance)
: BeamBase{direction, divergence, sigma_divergence,
polarization_normal, polarization_fraction,
flux, transmission, sample_to_source_distance}, wavelength_range_{wavelength_range} {}

PolychromaticBeam::PolychromaticBeam(std::array<double, 2> wavelength_range) : wavelength_range_{wavelength_range} {}

PolychromaticBeam::PolychromaticBeam(std::array<double, 2> wavelength_range, Vector3d direction)
: wavelength_range_{wavelength_range} {
sample_to_source_direction_ = direction / direction.norm();
}

// constructor from json data
PolychromaticBeam::PolychromaticBeam(json beam_data) {
// minimal required keys
std::vector<std::string> required_keys = {"wavelength_range"};
for (const auto &key : required_keys) {
if (beam_data.find(key) == beam_data.end()) {
throw std::invalid_argument(
"Key " + key + " is missing from the input beam JSON");
}
}
wavelength_range_ = beam_data["wavelength_range"];
init_from_json(beam_data);
}

// serialize to json format
json PolychromaticBeam::to_json(std::string probe="x-ray") const {
// create a json object that conforms to a dials model serialization.
json beam_data = {{"__id__", "polychromatic"}, {"probe", probe}};
beam_data["wavelength_range"] = wavelength_range_;
add_to_json(beam_data);
return beam_data;
}

std::array<double, 2> PolychromaticBeam::get_wavelength_range() const {
return wavelength_range_;
}
void PolychromaticBeam::set_wavelength_range(std::array<double, 2> 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");
}

#endif //DX2_MODEL_BEAM_H
Loading
Loading