1"""Functions for computing harvested and ingested biomass
2
3References
4----------
5.. [#Jouven] Jouven, M., Carrère, P., and Baumont, R. (2006). ‘Model
6 predicting dynamics of biomass, structure and digestibility of herbage in
7 managed permanent pastures. 1. Model description’, Grass and Forage
8 Science, 61(2), pp. 112–124.
9 https://doi.org/10.1111/j.1365-2494.2006.00515.x.
10.. [#Kavanagh] Kavanagh, S. (2016). ‘Feeding the Dairy Cow’, in Teagasc Dairy
11 Manual. Teagasc, pp. 201–212. Available at:
12 https://www.teagasc.ie/publications/2016/teagasc-dairy-manual.php
13 (Accessed: 2 November 2022).
14.. [#Broad] Broad, H. J. and Hough, M. N. (1993). ‘The growing and grazing
15 season in the United Kingdom’, Grass and Forage Science, 48(1), pp. 26–37.
16 https://doi.org/10.1111/j.1365-2494.1993.tb01833.x.
17.. [#Eurostat] Eurostat (2022). Glossary:Livestock unit (LSU). Available at:
18 https://ec.europa.eu/eurostat/statistics-explained/index.php?title=Glossary:Livestock_unit_(LSU)
19 (Accessed: 2 November 2022).
20"""
21
22import numpy as np
23
24np.seterr("raise")
25
26
[docs]
27def organic_matter_digestibility(
28 ts_vals: dict[str, float], params: dict[str, float]
29):
30 """Organic matter digestibility
31
32 Parameters
33 ----------
34 ts_vals : dict
35 Dictionary with intermediate time series values
36 params : dict
37 Dictionary of model parameters
38
39 Returns
40 -------
41 dict
42 Updated `ts_vals` dictionary
43
44 Notes
45 -----
46 Organic matter digestibility of the green vegetative (GV) and green
47 reproductive (GR) compartments. See Equations (9) and (10) in [#Jouven]_.
48
49 Digestibility varies among plant parts, with leaves usually being more
50 digestible than stems. The differing digestibility of plant parts may
51 explain why they are grazed selectively. Digestibility of green
52 compartments decreases linearly with compartment age.
53
54 This function returns an updated `ts_vals` dictionary with:
55
56 - `omd_gv`: Organic matter digestibility of the GV compartment
57 [dimensionless]
58 - `omd_gr`: Organic matter digestibility of the GR compartment
59 [dimensionless]
60 """
61 ts_vals["omd_gv"] = max(
62 params["max_omd_gv"]
63 - (ts_vals["age_gv"] * (params["max_omd_gv"] - params["min_omd_gv"]))
64 / params["lls"],
65 params["min_omd_gv"],
66 )
67
68 ts_vals["omd_gr"] = max(
69 params["max_omd_gr"]
70 - (ts_vals["age_gr"] * (params["max_omd_gr"] - params["min_omd_gr"]))
71 / (params["st_2"] - params["st_1"]),
72 params["min_omd_gr"],
73 )
74
75
[docs]
76def biomass_ingestion(ts_vals: dict[str, float], params: dict[str, float]):
77 """Biomass ingestion through grazing
78
79 Parameters
80 ----------
81 ts_vals : dict
82 Dictionary of intermediate time series values
83 params : dict
84 Dictionary containing of model parameters
85
86 Returns
87 -------
88 dict
89 Updated `ts_vals` dictionary
90
91 Notes
92 -----
93 The maximum amount of biomass available for grazing and/or harvesting
94 for each structural compartment.
95
96 - Maintain the height of the residual biomass after harvest to the
97 minimum residual grass height
98 - The bulk density is used to convert this height to the equivalent
99 biomass amount. See [#Jouven]_, sec. "Harvested biomass",
100 Equation (19)
101
102 The maximum amount of biomass ingestible based on the stocking rate
103
104 - Ingestion takes precedence over harvesting
105 - The amount of biomass ingested per livestock unit per day is
106 13 kg DM LU⁻¹ based on average consumption data for dairy cows from
107 [#Kavanagh]_ and the value used by [#Broad]_
108 - One livestock unit (LU) is equivalent to one dairy cow, based on
109 [#Eurostat]_
110
111 The amount of biomass ingested in total for each structural compartment
112
113 - This is weighted according to the organic matter digestibility.
114 - Digestibility varies among plant parts, with leaves usually being
115 more digestible than stems
116 - The differing digestibility of plant parts may explain why they are
117 grazed selectively
118
119 **Assumption**: when grazed/harvested, 10% of the available biomass in each
120 structural component is lost.
121
122 This function returns an updated `ts_vals` dictionary with:
123
124 - `i_bm`: The total ingested biomass amount [kg DM ha⁻¹]
125 - `bm_gv`: Updated standing biomass of the green vegetative
126 compartment [kg DM ha⁻¹]
127 - `bm_gr`: Updated standing biomass of the green reproductive
128 compartment [kg DM ha⁻¹]
129 - `bm_dv`: Updated standing biomass of the dead vegetative
130 compartment [kg DM ha⁻¹]
131 - `bm_dr`: Updated standing biomass of the dead reproductive
132 compartment [kg DM ha⁻¹]
133 """
134 if (
135 params["sr"] > 0.0
136 and params["h_grass"] >= 0.0
137 and params["st_g1"] <= ts_vals["st"] <= params["st_g2"]
138 ):
139 # max available compartmental biomass
140 available_biomass = {}
141 for key in ["gv", "gr", "dv", "dr"]:
142 residual_biomass = params["h_grass"] * params[f"bd_{key}"] * 10.0
143 if residual_biomass < ts_vals[f"bm_{key}"]:
144 available_biomass[f"bm_{key}"] = (
145 ts_vals[f"bm_{key}"] - residual_biomass
146 ) * 0.9
147 else:
148 available_biomass[f"bm_{key}"] = 0.0
149
150 # max ingested biomass
151 max_ingested_biomass = params["sr"] * params["i_bm_lu"]
152
153 # ingested compartmental biomass
154 weights = {}
155 ingested = {}
156
157 weights_total = (
158 ts_vals["omd_gv"]
159 + ts_vals["omd_gr"]
160 + params["omd_dv"]
161 + params["omd_dr"]
162 )
163
164 weights["bm_gv"] = ts_vals["omd_gv"] / weights_total
165 weights["bm_gr"] = ts_vals["omd_gr"] / weights_total
166 weights["bm_dv"] = params["omd_dv"] / weights_total
167 weights["bm_dr"] = params["omd_dr"] / weights_total
168
169 # sort by weight
170 weights = dict(sorted(weights.items(), key=lambda item: item[1]))
171
172 needed = 0.0
173
174 for key in weights:
175 ingested[key] = max_ingested_biomass * weights[key]
176 ingested[key] += needed
177 if available_biomass[key] < ingested[key]:
178 needed = ingested[key] - available_biomass[key]
179 ingested[key] = available_biomass[key]
180 else:
181 needed = 0.0
182 # update biomass compartments
183 # 10% of biomass is lost during ingestion
184 ts_vals[key] -= ingested[key] / 0.9
185
186 # total ingestion
187 ts_vals["i_bm"] += (
188 ingested["bm_gv"]
189 + ingested["bm_gr"]
190 + ingested["bm_dv"]
191 + ingested["bm_dr"]
192 )
193
194 # daily values
195 ts_vals["c_bm"] = (
196 ingested["bm_gv"]
197 + ingested["bm_gr"]
198 + ingested["bm_dv"]
199 + ingested["bm_dr"]
200 )
201
202 else:
203 ts_vals["c_bm"] = 0.0
204
205
[docs]
206def biomass_harvest(ts_vals: dict[str, float], params: dict[str, float]):
207 """Harvest biomass through a cutting event
208
209 Parameters
210 ----------
211 ts_vals : dict
212 A dictionary with intermediate time series values.
213 params : dict
214 A dictionary containing of model parameters
215
216 Returns
217 -------
218 dict
219 An updated `ts_vals` dictionary
220
221 Notes
222 -----
223 Harvest biomass through a cutting event at the end of the reproductive
224 period.
225 Maintain the height of the residual biomass after harvest to the minimum
226 cut height. This height is calculated by using the bulk density.
227 See [#Jouven]_, sec. "Harvested biomass", Equation (19).
228
229 **Assumption**: during harvest, 10% of the harvestable biomass in each
230 structural component is lost.
231
232 This function updated `ts_vals` dictionary with:
233
234 - `h_bm`: The total harvested biomass amount [kg DM ha⁻¹]
235 - `bm_gv`: Updated standing biomass of the green vegetative
236 compartment [kg DM ha⁻¹]
237 - `bm_gr`: Updated standing biomass of the green reproductive
238 compartment [kg DM ha⁻¹]
239 - `bm_dv`: Updated standing biomass of the dead vegetative
240 compartment [kg DM ha⁻¹]
241 - `bm_dr`: Updated standing biomass of the dead reproductive
242 compartment [kg DM ha⁻¹]
243 """
244 if (
245 params["h_grass"] >= 0.0
246 and params["st_h1"] <= ts_vals["st"] <= params["st_g2"]
247 ):
248 harvested_biomass = {}
249 for key in ["gv", "gr", "dv", "dr"]:
250 # harvested biomass for each compartment
251 residual_biomass = params["h_grass"] * params[f"bd_{key}"] * 10.0
252 harvested_biomass[f"bm_{key}"] = 0.0
253 if residual_biomass < ts_vals[f"bm_{key}"]:
254 harvested_biomass[f"bm_{key}"] += (
255 ts_vals[f"bm_{key}"] - residual_biomass
256 ) * 0.9
257 # update biomass compartments
258 # 10% of biomass is lost during harvest
259 ts_vals[f"bm_{key}"] -= harvested_biomass[f"bm_{key}"] / 0.9
260
261 # total harvested biomass
262 ts_vals["h_bm"] += (
263 harvested_biomass["bm_gv"]
264 + harvested_biomass["bm_gr"]
265 + harvested_biomass["bm_dv"]
266 + harvested_biomass["bm_dr"]
267 )