Line data Source code
1 : #include "Reaction.hpp" 2 : #include "ExceptionWithLocation.hpp" 3 : #include "Hashing.hpp" 4 : 5 : namespace cell 6 : { 7 : 8 : namespace 9 : { 10 0 : std::string toString(Reaction::Type type) 11 : { 12 0 : switch (type) 13 : { 14 0 : case Reaction::Type::Decomposition: return "Decomposition"; 15 0 : case Reaction::Type::Transformation: return "Transformation"; 16 0 : case Reaction::Type::Combination: return "Combination"; 17 0 : case Reaction::Type::Exchange: return "Exchange"; 18 0 : case Reaction::Type::None: return "None"; 19 0 : default: throw ExceptionWithLocation("Invalid reaction type"); 20 : } 21 : } 22 : } // namespace 23 : 24 4 : bool operator==(const Reaction& a, const Reaction& b) 25 : { 26 8 : return a.getEduct1() == b.getEduct1() && a.hasEduct2() == b.hasEduct2() && 27 8 : (!a.hasEduct2() || a.getEduct2() == b.getEduct2()) && a.getProduct1() == b.getProduct1() && 28 12 : a.hasProduct2() == b.hasProduct2() && (!a.hasProduct2() || a.getProduct2() == b.getProduct2()); 29 : } 30 : 31 4 : std::string toString(const Reaction& reaction, const DiscTypeRegistry& discTypeRegistry) 32 : { 33 4 : std::string result = discTypeRegistry.getByID(reaction.getEduct1()).getName(); 34 : 35 4 : if (reaction.hasEduct2()) 36 2 : result += " + " + discTypeRegistry.getByID(reaction.getEduct2()).getName(); 37 : 38 4 : result += " -> " + discTypeRegistry.getByID(reaction.getProduct1()).getName(); 39 : 40 4 : if (reaction.hasProduct2()) 41 2 : result += " + " + discTypeRegistry.getByID(reaction.getProduct2()).getName(); 42 : 43 4 : return result; 44 0 : } 45 : 46 0 : bool contains(const Reaction& reaction, DiscTypeID discType) 47 : { 48 0 : return reaction.getEduct1() == discType || reaction.getEduct2() == discType || reaction.getProduct1() == discType || 49 0 : reaction.getProduct2() == discType; 50 : } 51 : 52 10 : Reaction::Type inferReactionType(bool educt2, bool product2) 53 : { 54 10 : if (!educt2 && !product2) 55 2 : return Reaction::Type::Transformation; 56 8 : else if (educt2 && !product2) 57 4 : return Reaction::Type::Combination; 58 4 : else if (!educt2 && product2) 59 2 : return Reaction::Type::Decomposition; 60 : else 61 2 : return Reaction::Type::Exchange; 62 : } 63 : 64 0 : size_t ReactionHash::operator()(const Reaction& reaction) const 65 : { 66 0 : auto hash = calculateHash(reaction.getEduct1(), reaction.getProduct1()); 67 : 68 0 : if (reaction.hasEduct2()) 69 0 : hashCombine(hash, reaction.getEduct2()); 70 : 71 0 : if (reaction.hasProduct2()) 72 0 : hashCombine(hash, reaction.getProduct2()); 73 : 74 0 : return hash; 75 : } 76 : 77 10 : Reaction::Reaction(DiscTypeID educt1, const std::optional<DiscTypeID>& educt2, DiscTypeID product1, 78 10 : const std::optional<DiscTypeID>& product2, double probability) 79 10 : : educt1_(educt1) 80 10 : , educt2_(educt2) 81 10 : , product1_(product1) 82 10 : , product2_(product2) 83 10 : , type_(inferReactionType(educt2.has_value(), product2.has_value())) 84 : { 85 10 : setProbability(probability); 86 10 : } 87 : 88 56 : DiscTypeID Reaction::getEduct1() const 89 : { 90 56 : return educt1_; 91 : } 92 : 93 0 : void Reaction::setEduct1(DiscTypeID educt1) 94 : { 95 0 : educt1_ = educt1; 96 0 : } 97 : 98 37 : DiscTypeID Reaction::getEduct2() const 99 : { 100 37 : if (!educt2_) 101 0 : throw ExceptionWithLocation("Can't get educt2 for reaction of type" + toString(type_)); 102 : 103 37 : return *educt2_; 104 : } 105 : 106 16 : bool Reaction::hasEduct2() const 107 : { 108 16 : return type_ == Type::Combination || type_ == Type::Exchange; 109 : } 110 : 111 0 : void Reaction::setEduct2(DiscTypeID educt2) 112 : { 113 0 : if (!educt2_) 114 0 : throw ExceptionWithLocation("Can't set educt2 for reaction of type" + toString(type_)); 115 0 : educt2_ = educt2; 116 0 : } 117 : 118 22 : DiscTypeID Reaction::getProduct1() const 119 : { 120 22 : return product1_; 121 : } 122 : 123 0 : void Reaction::setProduct1(DiscTypeID product1) 124 : { 125 0 : product1_ = product1; 126 0 : } 127 : 128 9 : DiscTypeID Reaction::getProduct2() const 129 : { 130 9 : if (!product2_) 131 0 : throw ExceptionWithLocation("Can't get product2 for reaction of type" + toString(type_)); 132 : 133 9 : return *product2_; 134 : } 135 : 136 16 : bool Reaction::hasProduct2() const 137 : { 138 16 : return type_ == Type::Decomposition || type_ == Type::Exchange; 139 : } 140 : 141 0 : void Reaction::setProduct2(DiscTypeID product2) 142 : { 143 0 : if (!product2_) 144 0 : throw ExceptionWithLocation("Can't set product2 for reaction of type" + toString(type_)); 145 : 146 0 : product2_ = product2; 147 0 : } 148 : 149 6 : double Reaction::getProbability() const 150 : { 151 6 : return probability_; 152 : } 153 : 154 10 : void Reaction::setProbability(double probability) 155 : { 156 10 : if (probability < 0 || probability > 1) 157 0 : throw ExceptionWithLocation("Probability must be between 0 and 1"); 158 : 159 10 : probability_ = probability; 160 10 : } 161 : 162 72 : const Reaction::Type& Reaction::getType() const 163 : { 164 72 : return type_; 165 : } 166 : 167 14 : void Reaction::validate(const DiscTypeRegistry& discTypeRegistry) const 168 : { 169 42 : auto getMass = [&](DiscTypeID discTypeID) { return discTypeRegistry.getByID(discTypeID).getMass(); }; 170 : 171 14 : const auto eductMassSum = getMass(educt1_) + (educt2_ ? getMass(*educt2_) : 0); 172 14 : const auto productMassSum = getMass(product1_) + (product2_ ? getMass(*product2_) : 0); 173 : 174 14 : if (eductMassSum != productMassSum) 175 0 : throw ExceptionWithLocation(toString(*this, discTypeRegistry) + 176 0 : ": Product- and educt masses need to be identical"); 177 : 178 14 : if (type_ == Type::Transformation && educt1_ == product1_) 179 0 : throw ExceptionWithLocation(toString(*this, discTypeRegistry) + ": Educt 1 and product 1 are identical"); 180 14 : } 181 : 182 : } // namespace cell