diff --git a/catima.cpp b/catima.cpp index a971619..95ce76d 100644 --- a/catima.cpp +++ b/catima.cpp @@ -26,47 +26,41 @@ bool operator==(const Config &a, const Config&b){ double dedx(Projectile &p, double T, const Material &mat, const Config &c){ double sum = 0; - Target t; double w=0; if(T<=0)return 0.0; for(int i=0;irange = calculate_range(p,t,c); - index->range_straggling = calculate_range_straggling(p,t,c); - index->angular_variance = calculate_angular_variance(p,t,c); + index->range_straggling = calculate_range_straggling(p,t,c); + index->angular_variance = calculate_angular_variance(p,t,c); #else *index = calculate_DataPoint(p,t,c); #endif diff --git a/structures.cpp b/structures.cpp index ce8b7cb..d013243 100644 --- a/structures.cpp +++ b/structures.cpp @@ -1,5 +1,6 @@ #include "structures.h" #include "catima/nucdata.h" +#include namespace catima{ @@ -17,7 +18,7 @@ bool operator==(const Material &a, const Material&b){ if(a.density() != b.density())return false; if(a.ncomponents() != b.ncomponents())return false; for(int i=0;i &list){ */ Material::Material(std::initializer_list>list,double _density):rho(_density){ std::initializer_list>::iterator it; + atoms.reserve(list.size()); for ( it=list.begin(); it!=list.end(); ++it){ - add_element( (*it)[0],(*it)[1],(*it)[2]); + add_element((*it)[0],(*it)[1],(*it)[2]); } + + calculate(); // calculate if needed, ie average molar mass + } Material::Material(double _a, int _z, double _rho, double _th):rho(_rho),th(_th){ @@ -42,24 +47,20 @@ Material::Material(double _a, int _z, double _rho, double _th):rho(_rho),th(_th) void Material::add_element(double _a, int _z, double _stn){ double a = (_a>0)?_a:element_atomic_weight(_z); - stn.push_back(_stn); - atoms.push_back({a,_z}); + atoms.push_back({a,_z,_stn}); molar_mass += _stn*a; } - -std::pair Material::get_element(int i) const{ - return std::pair(atoms[i],stn[i]); -} - -/* void Material::calculate(){ - molar_mass = 0; - for(int i=0;iatoms; - std::vectorstn; public: Material(){}; @@ -91,6 +91,9 @@ namespace catima{ Material(std::initializer_list>list,double _density=0.0); //Material(const std::array &list); + /** + * calculates internal variables if needed + */ void calculate(); /** @@ -104,14 +107,20 @@ namespace catima{ /** * returns i-th element of the Material as a std::pair of Target and corresponding stoichiometric number * @param i - index of element to return - * @return std::pair of Target and corresponding stoichiometric number + * @return Target class */ - std::pair get_element(int i) const; + Target get_element(int i) const {return atoms[i];}; + + /** + * return weight fraction of i-th element + * @return weight fraction + */ + double weight_fraction(int i) const {return (atoms[i].stn<1.0)?atoms[i].stn:atoms[i].stn*atoms[i].A/M();}; /** * @return number of components in Material */ - int ncomponents() const {return stn.size();} + int ncomponents() const {return atoms.size();} /** * @return returns Molar Mass of the Material diff --git a/tests/test_calculations.cpp b/tests/test_calculations.cpp index 868834a..51ca74a 100644 --- a/tests/test_calculations.cpp +++ b/tests/test_calculations.cpp @@ -1,9 +1,9 @@ #include "lest.hpp" +#include "testutils.h" #include using namespace std; - +using catima::approx; #include "catima/catima.h" -using lest::approx; bool rcompare(double a, double b,double eps){ if(fabs((a-b)/fabs(b)) using namespace std; +using catima::approx; #include "catima/catima.h" #include "catima/material_database.h" @@ -57,28 +59,25 @@ const lest::test specification[] = CASE("Material automatic atomic weight"){ catima::Material water({{0,1,2},{0,8,1}}); catima::Material graphite(0,6); - EXPECT(water.get_element(0).first.A == 1.00794); - EXPECT(water.get_element(1).first.A == 15.9994); - EXPECT(graphite.get_element(0).first.A == 12.0107); + EXPECT(water.get_element(0).A == 1.00794); + EXPECT(water.get_element(1).A == 15.9994); + EXPECT(graphite.get_element(0).A == 12.0107); EXPECT(water.M()>16); EXPECT(graphite.M()>12); }, CASE("default materials"){ catima::Material m = catima::get_material(6); - EXPECT(m.get_element(0).first.A == 12.0107); - EXPECT(m.get_element(0).first.Z == 6); + EXPECT(m.get_element(0).A == 12.0107); + EXPECT(m.get_element(0).Z == 6); EXPECT(m.density() == 2.0); EXPECT(m.M() == 12.0107); m = catima::get_material(catima::material::Water); - EXPECT(m.get_element(0).first.A == 1.00794); - EXPECT(m.get_element(0).first.Z == 1); - EXPECT(m.get_element(1).first.A == 15.9994); - EXPECT(m.get_element(1).first.Z == 8); + EXPECT(m.get_element(0).A == 1.00794); + EXPECT(m.get_element(0).Z == 1); + EXPECT(m.get_element(1).A == 15.9994); + EXPECT(m.get_element(1).Z == 8); EXPECT(m.density() == 1.0); - EXPECT(m.M() > 16.0); - - }, CASE("Layers"){ catima::Material water2; @@ -102,10 +101,10 @@ const lest::test specification[] = EXPECT(detector1[0].thickness()==1.0); EXPECT(detector1[1].density()==1.0); EXPECT(detector1[1].thickness()==2.0); - EXPECT(detector1[0].get_element(0).first.Z == 6); - EXPECT(detector1[0].get_element(0).first.A == 12); - EXPECT(detector1[1].get_element(0).first.A == 1); - EXPECT(detector1[1].get_element(0).first.Z == 1); + EXPECT(detector1[0].get_element(0).Z == 6); + EXPECT(detector1[0].get_element(0).A == 12); + EXPECT(detector1[1].get_element(0).A == 1); + EXPECT(detector1[1].get_element(0).Z == 1); EXPECT(detector1[2].density() == 1.8); detector1[1].density(1.2); EXPECT(detector1[1].density()==1.2); @@ -163,43 +162,62 @@ const lest::test specification[] = }); EXPECT(mat2.ncomponents()==1); EXPECT(mat3.ncomponents()==1); - EXPECT(mat3.get_element(0).first.A==12.01); + EXPECT(mat3.get_element(0).A==12.01); EXPECT(mat4.ncomponents()==1); - EXPECT(mat4.get_element(0).first.A==12.01); + EXPECT(mat4.get_element(0).A==12.01); EXPECT(mat5.ncomponents()==2); - EXPECT(mat5.get_element(0).first.A==12.01); - EXPECT(mat5.get_element(0).first.Z==6); - EXPECT(mat5.get_element(1).first.A==16.0); - EXPECT(mat5.get_element(1).second==2); + EXPECT(mat5.get_element(0).A==12.01); + EXPECT(mat5.get_element(0).Z==6); + EXPECT(mat5.get_element(1).A==16.0); + EXPECT(mat5.get_element(1).stn==2); catima::Material mat6; mat6 = mat5; EXPECT(mat5==mat6); EXPECT(mat5.ncomponents()==mat6.ncomponents()); - EXPECT(mat5.get_element(0).first.A==mat6.get_element(0).first.A); - EXPECT(mat5.get_element(1).first.A==mat6.get_element(1).first.A); + EXPECT(mat5.get_element(0).A==mat6.get_element(0).A); + EXPECT(mat5.get_element(1).A==mat6.get_element(1).A); mat5.add_element(12,6,1); EXPECT(mat5.ncomponents()==mat6.ncomponents()+1); }, - CASE("int and double stn check"){ - catima::Projectile p{1,1,1,1000}; - catima::Material mat1({ - {12.01, 6, 1}, - {16.00, 8, 1} + CASE("fraction vs stn init"){ + catima::Projectile p{12,6}; + catima::Material water1({ + {0, 1, 0.111894}, + {0, 8, 0.888106} }); - catima::Material mat2({ - {12.01, 6, 0.5}, - {16.00, 8, 0.5} + catima::Material water2({ + {0, 1, 2}, + {0, 8, 1} }); - mat1.thickness(1.0); - mat2.thickness(1.0); - auto res1 = catima::calculate(p(1000),mat1); - auto res2 = catima::calculate(p(1000),mat2); - EXPECT(res1.dEdxi == res2.dEdxi); - EXPECT(res1.range == res2.range); - EXPECT(res1.sigma_a == res2.sigma_a); - EXPECT(res1.sigma_r == res2.sigma_r); + water1.thickness(1.0); + water2.thickness(1.0); + auto res1 = catima::calculate(p(600),water1); + auto res2 = catima::calculate(p(600),water2); + EXPECT(res1.dEdxi == approx(res2.dEdxi,0.001)); + EXPECT(res1.range == approx(res2.range).R(1e-2)); + EXPECT(res1.sigma_a == approx(res2.sigma_a).R(1e-2)); + EXPECT(res1.sigma_r == approx(res2.sigma_r).R(1e-2)); + }, + CASE("fraction calculation"){ + catima::Material water1({ + {0, 1, 0.111894}, + {0, 8, 0.888106} + }); + catima::Material water2({ + {0, 1, 2}, + {0, 8, 1} + }); + + EXPECT(water1.weight_fraction(0)==0.111894); + EXPECT(water2.weight_fraction(0)==approx(water1.weight_fraction(0)).R(1e-3)); + EXPECT(water1.weight_fraction(1)==0.888106); + EXPECT(water2.weight_fraction(1)==approx(water1.weight_fraction(1)).R(1e-3)); + EXPECT(water2.M()==approx(18).epsilon(0.1)); + + EXPECT(water1.M()==approx(6.0,0.1)); + EXPECT(water2.M()==approx(18,0.1)); } }; diff --git a/tests/testutils.h b/tests/testutils.h new file mode 100644 index 0000000..975fbcb --- /dev/null +++ b/tests/testutils.h @@ -0,0 +1,71 @@ +namespace catima{ + +class approx +{ +public: + explicit approx ( double magnitude, double eps=1.0) + : epsilon_ { eps } + , magnitude_{ magnitude } {} + + approx( approx const & other ) = default; + + approx operator()( double magnitude, double eps = 1.0 ) + { + approx approx ( magnitude, eps); + return approx; + } + + double magnitude() const { return magnitude_; } + + approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; } + approx & R( double relative ) { epsilon_ = relative*magnitude_; return *this; } + + + friend bool operator == ( double lhs, approx const & rhs ) + { + return std::abs( lhs - rhs.magnitude_) < rhs.epsilon_; + } + + friend bool operator == ( approx const & lhs, double rhs ) { return operator==( rhs, lhs ); } + friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); } + friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); } + + friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; } + friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; } + friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; } + friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; } + +//private: + double epsilon_; + double magnitude_; +}; + +std::ostream & operator<<(std::ostream &os, approx const &a){ + using lest::to_string; + return os<