1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package fr.inrae.agroclim.indicators.model.indicator;
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Objects;
25 import java.util.Optional;
26 import java.util.Set;
27
28 import fr.inrae.agroclim.indicators.exception.IndicatorsException;
29 import fr.inrae.agroclim.indicators.model.Knowledge;
30 import fr.inrae.agroclim.indicators.model.Parameter;
31 import fr.inrae.agroclim.indicators.model.data.DailyData;
32 import fr.inrae.agroclim.indicators.model.data.Resource;
33 import fr.inrae.agroclim.indicators.model.data.Variable;
34 import fr.inrae.agroclim.indicators.model.data.climate.ClimaticDailyData;
35 import fr.inrae.agroclim.indicators.model.data.climate.ClimaticResource;
36 import fr.inrae.agroclim.indicators.util.Doublet;
37 import jakarta.xml.bind.annotation.XmlType;
38 import lombok.Getter;
39 import lombok.Setter;
40 import lombok.extern.log4j.Log4j2;
41
42
43
44
45
46
47
48
49
50
51
52 @XmlType(propOrder = {"nbDays", "rainThreshold",
53 "soilWaterContentAtFieldCapacity", "soilWaterContentThreshold",
54 "tmeanThreshold", "tminThreshold"})
55 @Log4j2
56 public final class PotentialSowingDaysFrequency extends SimpleIndicator implements Detailable {
57
58
59
60
61 private static final long serialVersionUID = 6030595237342422013L;
62
63
64
65
66
67 @Getter
68 @Setter
69 private int nbDays;
70
71
72
73
74 @Getter
75 @Setter
76 private double rainThreshold;
77
78
79
80
81 @Getter
82 @Setter
83 private double soilWaterContentAtFieldCapacity;
84
85
86
87
88 @Getter
89 @Setter
90 private double soilWaterContentThreshold;
91
92
93
94
95 @Getter
96 @Setter
97 private double tmeanThreshold;
98
99
100
101
102 @Getter
103 @Setter
104 private double tminThreshold;
105
106
107
108
109 public PotentialSowingDaysFrequency() {
110 super();
111 }
112
113
114
115
116
117
118 private boolean checkSumOfRain(final int index,
119 final ClimaticResource res) {
120 final List<ClimaticDailyData> data = res.getData();
121 double sum = 0;
122 for (int i = index; i <= index + nbDays; i++) {
123 sum += data.get(i).getValue(Variable.RAIN);
124 }
125 return sum >= rainThreshold;
126 }
127
128
129
130
131
132
133 private boolean checkTminTmoy(final int index, final ClimaticResource res) {
134 final List<ClimaticDailyData> data = res.getData();
135 for (int i = index; i <= index + nbDays; i++) {
136 final double tmin = data.get(i).getValue(Variable.TMIN);
137 final double tmean = data.get(i).getValue(Variable.TMEAN);
138 if (!(tmin > tminThreshold && tmean > tmeanThreshold)) {
139 return false;
140 }
141 }
142 return true;
143 }
144
145 @Override
146 public PotentialSowingDaysFrequency clone()
147 throws CloneNotSupportedException {
148 PotentialSowingDaysFrequency clone;
149 clone = (PotentialSowingDaysFrequency) super.clone();
150 clone.soilWaterContentThreshold = soilWaterContentThreshold;
151 clone.soilWaterContentAtFieldCapacity = soilWaterContentAtFieldCapacity;
152 clone.nbDays = nbDays;
153 clone.tmeanThreshold = tmeanThreshold;
154 clone.tminThreshold = tminThreshold;
155 clone.rainThreshold = rainThreshold;
156 return clone;
157 }
158
159 @Override
160 public double computeSingleValue(final Resource<? extends DailyData> resource) throws IndicatorsException {
161 Objects.requireNonNull(resource, "Resource must not be null!");
162 if (!(resource instanceof ClimaticResource)) {
163 throw new IllegalArgumentException(
164 getClass().getName()
165 + ".computeSingleValue() handles "
166 + "only ClimaticResource, not "
167 + resource.getClass().getName());
168 }
169 final ClimaticResource res = (ClimaticResource) resource;
170 int cpt = 0;
171 final List<ClimaticDailyData> data = res.getData();
172 if (data.size() >= nbDays) {
173 for (int index = 0; index < res.getData().size(); index++) {
174 final double soilWaterContent = data.get(index)
175 .getValue(Variable.SOILWATERCONTENT);
176 if (soilWaterContent >= soilWaterContentThreshold
177 && soilWaterContent < soilWaterContentAtFieldCapacity
178 && index + nbDays < data.size()
179 && checkTminTmoy(index, res)
180 && checkSumOfRain(index, res)) {
181 cpt += 1;
182 }
183 }
184 }
185 final double percent = 100.;
186 return cpt * percent / data.size();
187 }
188
189 private Map<String, Number> getAttributeValues() {
190 return Map.of(
191 "soilWaterContentThreshold", soilWaterContentThreshold,
192 "nbDays", nbDays,
193 "rainThreshold", rainThreshold,
194 "soilWaterContentAtFieldCapacity", soilWaterContentAtFieldCapacity,
195 "tmeanThreshold", tmeanThreshold,
196 "tminThreshold", tminThreshold
197 );
198 }
199
200 @Override
201 public List<Doublet<Parameter, Number>> getParameterDefaults() {
202 if (getParameters() == null) {
203 return List.of();
204 }
205 final List<Doublet<Parameter, Number>> val = new ArrayList<>();
206 getAttributeValues().forEach((k, v) -> {
207 final Optional<Parameter> found = getParameters().stream()
208 .filter(a -> k.equals(a.getAttribute()))
209 .findFirst();
210 if (found.isPresent()) {
211 val.add(Doublet.of(found.get(), v));
212 }
213 });
214 return val;
215 }
216
217
218
219
220 @Override
221 public List<Parameter> getParameters() {
222 if (super.getParameters() != null) {
223 return super.getParameters();
224 } else {
225 return List.of();
226 }
227 }
228
229 @Override
230 public Map<String, Double> getParametersValues() {
231 final Map<String, Double> val = new HashMap<>();
232
233 if (getParameters() == null) {
234 return val;
235 }
236 final Map<String, Number> attributeValues = getAttributeValues();
237 for (final Parameter param : getParameters()) {
238 if (param == null) {
239 continue;
240 }
241 final Number value = attributeValues.get(param.getId());
242 if (value != null) {
243 val.put(param.getId(), value.doubleValue());
244 }
245 }
246 return val;
247 }
248
249 @Override
250 public Set<Variable> getVariables() {
251 final Set<Variable> variables = new HashSet<>();
252 variables.add(Variable.RAIN);
253 variables.add(Variable.SOILWATERCONTENT);
254 variables.add(Variable.TMEAN);
255 variables.add(Variable.TMIN);
256 return variables;
257 }
258
259 @Override
260 public boolean isComputable(final Resource<? extends DailyData> resource) {
261 if (resource == null) {
262 throw new IllegalArgumentException("resource must no be null!");
263 }
264 if (!(resource instanceof ClimaticResource)) {
265 throw new IllegalArgumentException(getClass().getName()
266 + " handles only ClimaticResource, not "
267 + resource.getClass().getName());
268 }
269 return true;
270 }
271
272 @Override
273 public void setParametersFromKnowledge(final Knowledge knowledge) {
274 final Indicator indicator = knowledge.getIndicator(getId());
275 setParameters(indicator.getParameters());
276 }
277
278 @Override
279 public void setParametersValues(final Map<String, Double> values) {
280
281 if (getParameters() == null) {
282 return;
283 }
284 getParameters().forEach(param -> {
285 final String id = param.getId();
286 if (values.containsKey(id)) {
287 switch (param.getAttribute()) {
288 case "soilWaterContentThreshold":
289 soilWaterContentThreshold = values.get(id);
290 break;
291 case "soilWaterContentAtFieldCapacity":
292 soilWaterContentAtFieldCapacity = values.get(id);
293 break;
294 case "nbDays":
295 if (values.get(id) != null) {
296 nbDays = values.get(id).intValue();
297 }
298 break;
299 case "tmeanThreshold":
300 tmeanThreshold = values.get(id);
301 break;
302 case "tminThreshold":
303 tminThreshold = values.get(id);
304 break;
305 case "rainThreshold":
306 rainThreshold = values.get(id);
307 break;
308 default:
309 break;
310 }
311 }
312 });
313 }
314
315 @Override
316 public String toStringTree(final String indent) {
317 return toStringTreeBase(indent);
318 }
319
320 @Override
321 public void removeParameter(final Parameter param) {
322 if (getParameters() != null) {
323 getParameters().remove(param);
324 }
325 }
326
327 }