LCOV - code coverage report
Current view: top level - cell - Reaction.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 49 66 74.2 %
Date: 2026-06-20 21:11:56 Functions: 9 11 81.8 %

          Line data    Source code
       1             : #include "Reaction.hpp"
       2             : #include "ExceptionWithLocation.hpp"
       3             : #include "Hashing.hpp"
       4             : 
       5             : #include <numbers>
       6             : 
       7             : namespace cell
       8             : {
       9             : 
      10           4 : bool operator==(const Reaction& a, const Reaction& b)
      11             : {
      12           8 :     return a.getEduct1() == b.getEduct1() && a.hasEduct2() == b.hasEduct2() &&
      13           8 :            (!a.hasEduct2() || a.getEduct2() == b.getEduct2()) && a.getProduct1() == b.getProduct1() &&
      14          12 :            a.hasProduct2() == b.hasProduct2() && (!a.hasProduct2() || a.getProduct2() == b.getProduct2());
      15             : }
      16             : 
      17           5 : std::string toString(const Reaction& reaction, const DiscTypeRegistry& discTypeRegistry)
      18             : {
      19           5 :     std::string result = discTypeRegistry.getByID(reaction.getEduct1()).getName();
      20             : 
      21           5 :     if (reaction.hasEduct2())
      22           2 :         result += " + " + discTypeRegistry.getByID(reaction.getEduct2()).getName();
      23             : 
      24           5 :     result += " -> " + discTypeRegistry.getByID(reaction.getProduct1()).getName();
      25             : 
      26           5 :     if (reaction.hasProduct2())
      27           3 :         result += " + " + discTypeRegistry.getByID(reaction.getProduct2()).getName();
      28             : 
      29           5 :     return result;
      30           0 : }
      31             : 
      32           0 : bool contains(const Reaction& reaction, DiscTypeID discType)
      33             : {
      34           0 :     return reaction.getEduct1() == discType || reaction.getEduct2() == discType || reaction.getProduct1() == discType ||
      35           0 :            reaction.getProduct2() == discType;
      36             : }
      37             : 
      38          13 : Reaction::Type inferReactionType(bool educt2, bool product2)
      39             : {
      40          13 :     if (!educt2 && !product2)
      41           2 :         return Reaction::Type::Transformation;
      42          11 :     else if (educt2 && !product2)
      43           5 :         return Reaction::Type::Combination;
      44           6 :     else if (!educt2 && product2)
      45           4 :         return Reaction::Type::Decomposition;
      46             :     else
      47           2 :         return Reaction::Type::Exchange;
      48             : }
      49             : 
      50          13 : Reaction::Reaction(DiscTypeID educt1, const std::optional<DiscTypeID>& educt2, DiscTypeID product1,
      51          13 :                    const std::optional<DiscTypeID>& product2, double probability)
      52          13 :     : educt1_(educt1)
      53          13 :     , educt2_(educt2)
      54          13 :     , product1_(product1)
      55          13 :     , product2_(product2)
      56          13 :     , type_(inferReactionType(educt2.has_value(), product2.has_value()))
      57             : {
      58          13 :     setProbability(probability);
      59          13 : }
      60             : 
      61          13 : void Reaction::setProbability(double probability)
      62             : {
      63          13 :     if (probability < 0 || probability > 1)
      64           0 :         throw ExceptionWithLocation("Probability must be between 0 and 1");
      65             : 
      66          13 :     probability_ = probability;
      67          13 : }
      68             : 
      69          16 : void Reaction::validate(const DiscTypeRegistry& discTypeRegistry) const
      70             : {
      71          48 :     const auto getMass = [&](DiscTypeID discTypeID) { return discTypeRegistry.getByID(discTypeID).getMass(); };
      72             : 
      73          16 :     const auto eductMassSum = getMass(educt1_) + (educt2_ ? getMass(*educt2_) : 0);
      74          16 :     const auto productMassSum = getMass(product1_) + (product2_ ? getMass(*product2_) : 0);
      75             : 
      76          16 :     if (std::abs(eductMassSum - productMassSum) > 1e-6)
      77           0 :         throw ExceptionWithLocation(toString(*this, discTypeRegistry) +
      78           0 :                                     ": Product- and educt masses need to be identical");
      79             : 
      80          16 :     if (type_ == Type::Transformation && educt1_ == product1_)
      81           0 :         throw ExceptionWithLocation(toString(*this, discTypeRegistry) + ": Educt 1 and product 1 are identical");
      82          16 : }
      83             : 
      84           1 : void Reaction::validateAreaConservation(const DiscTypeRegistry& discTypeRegistry) const
      85             : {
      86           3 :     const auto getArea = [&](DiscTypeID discTypeID)
      87             :     {
      88           3 :         const auto R = discTypeRegistry.getByID(discTypeID).getRadius();
      89           3 :         return std::numbers::pi * R * R;
      90           1 :     };
      91           1 :     const auto eductAreaSum = getArea(educt1_) + (educt2_ ? getArea(*educt2_) : 0);
      92           1 :     const auto productAreaSum = getArea(product1_) + (product2_ ? getArea(*product2_) : 0);
      93             : 
      94           1 :     if (std::abs(eductAreaSum - productAreaSum) > 1e-6)
      95           1 :         throw ExceptionWithLocation(toString(*this, discTypeRegistry) + ": Area not conserved");
      96           0 : }
      97             : 
      98           0 : std::string Reaction::getTypeString() const
      99             : {
     100           0 :     switch (type_)
     101             :     {
     102           0 :     case Reaction::Type::Decomposition: return "Decomposition";
     103           0 :     case Reaction::Type::Transformation: return "Transformation";
     104           0 :     case Reaction::Type::Combination: return "Combination";
     105           0 :     case Reaction::Type::Exchange: return "Exchange";
     106           0 :     case Reaction::Type::None: return "None";
     107           0 :     default: throw ExceptionWithLocation("Invalid reaction type");
     108             :     }
     109             : }
     110             : 
     111             : } // namespace cell

Generated by: LCOV version 1.14