Coverage for pybeepop/beepop/mite.py: 56%

84 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-30 13:34 +0000

1""" 

2Mite Population Module for BeePop+ Varroa Mite Simulation 

3 

4This module models Varroa destructor mite populations with resistance genetics 

5for BeePop+ honey bee colony simulation. It tracks resistant and non-resistant 

6mite subpopulations, supporting treatment efficacy modeling and resistance 

7evolution studies. 

8 

9Classes: 

10 Mite: Varroa mite population with resistance genetics tracking 

11""" 

12 

13 

14class Mite: 

15 """ 

16 Varroa destructor mite population model with resistance genetics for BeePop+ simulation. 

17 

18 Attributes: 

19 resistant (float): Number of treatment-resistant mites in population 

20 non_resistant (float): Number of treatment-susceptible mites in population 

21 

22 Note: 

23 Arithmetic operations include C++ compatibility features like integer 

24 truncation in addition operations to maintain exact simulation reproducibility. 

25 """ 

26 

27 def __init__(self, resistant=0.0, non_resistant=0.0): 

28 self.resistant = resistant 

29 self.non_resistant = non_resistant 

30 

31 def zero(self): 

32 self.resistant = 0.0 

33 self.non_resistant = 0.0 

34 

35 def get_resistant(self): 

36 return self.resistant 

37 

38 def get_non_resistant(self): 

39 return self.non_resistant 

40 

41 def set_resistant(self, num): 

42 self.resistant = num 

43 

44 def set_non_resistant(self, num): 

45 self.non_resistant = num 

46 

47 def get_total(self): 

48 return self.resistant + self.non_resistant 

49 

50 def get_pct_resistant(self): 

51 total = self.get_total() 

52 return (100.0 * self.resistant / total) if total > 0 else 0.0 

53 

54 def set_pct_resistant(self, pct): 

55 total = self.get_total() 

56 self.resistant = total * pct / 100.0 

57 self.non_resistant = total - self.resistant 

58 

59 def __iadd__(self, other): 

60 if isinstance(other, Mite): 

61 self.resistant += other.resistant 

62 self.non_resistant += other.non_resistant 

63 elif isinstance(other, (int, float)): 

64 total = self.get_total() 

65 pctres = self.resistant / total if total > 0 else 0.0 

66 addtores = other * pctres 

67 self.resistant += addtores 

68 self.non_resistant += other - addtores 

69 return self 

70 

71 def __isub__(self, other): 

72 if isinstance(other, Mite): 

73 self.resistant -= other.resistant 

74 self.non_resistant -= other.non_resistant 

75 elif isinstance(other, (int, float)): 

76 total = self.get_total() 

77 pctres = self.resistant * 100 / total if total > 0 else 0.0 

78 subfromres = other * pctres / 100.0 

79 self.resistant -= subfromres 

80 self.non_resistant -= other - subfromres 

81 self.resistant = max(0.0, self.resistant) 

82 self.non_resistant = max(0.0, self.non_resistant) 

83 return self 

84 

85 def __add__(self, other): 

86 if isinstance(other, Mite): 

87 # Match C++ behavior: truncate to int like CMite::operator+(CMite theMite) 

88 res = self.resistant + other.resistant 

89 nres = self.non_resistant + other.non_resistant 

90 return Mite(int(res), int(nres)) 

91 elif isinstance(other, (int, float)): 

92 return Mite(self.resistant, self.non_resistant + other) 

93 return NotImplemented 

94 

95 def __radd__(self, other): 

96 # Handle cases like 0 + Mite or int + Mite 

97 if isinstance(other, (int, float)): 

98 return Mite(self.resistant, self.non_resistant + other) 

99 return NotImplemented 

100 

101 def __sub__(self, other): 

102 if isinstance(other, Mite): 

103 res = self.resistant - other.resistant 

104 nres = self.non_resistant - other.non_resistant 

105 return Mite(max(0.0, res), max(0.0, nres)) 

106 elif isinstance(other, (int, float)): 

107 return Mite(self.resistant, max(0.0, self.non_resistant - other)) 

108 return NotImplemented 

109 

110 def __mul__(self, value): 

111 if isinstance(value, (int, float)): 

112 return Mite(self.resistant * value, self.non_resistant * value) 

113 return NotImplemented 

114 

115 def __int__(self): 

116 return int(self.get_total()) 

117 

118 def __eq__(self, other): 

119 if not isinstance(other, Mite): 

120 return False 

121 return ( 

122 self.resistant == other.resistant 

123 and self.non_resistant == other.non_resistant 

124 ) 

125 

126 def assign_value(self, value): 

127 """ 

128 Matches C++ CMite::operator=(double value) behavior. 

129 Sets resistant = 0 and non_resistant = value. 

130 """ 

131 self.resistant = 0.0 

132 self.non_resistant = float(value) 

133 return self 

134 

135 def __str__(self): 

136 return f"Mite(resistant={self.resistant}, non_resistant={self.non_resistant})"