Skip to content
Snippets Groups Projects
Commit 5bd310ee authored by Tor-Einar Skog's avatar Tor-Einar Skog
Browse files

First working version with separate cutoffs per phase

parent 8ab813b2
Branches
No related tags found
No related merge requests found
/*
* Copyright (c) 2016 NIBIO <http://www.nibio.no/>.
* Copyright (c) 2021 NIBIO <http://www.nibio.no/>.
*
* This file is part of PsilaRosaeTempModel.
* PsilaRosaeTempModel is free software: you can redistribute it and/or modify
......@@ -22,7 +22,7 @@ package no.nibio.vips.model.yellowstemborertempmodel;
import no.nibio.vips.util.DateMap;
/**
* @copyright 2016 <a href="http://www.nibio.no/">NIBIO</a>
* @copyright 2021 <a href="http://www.nibio.no/">NIBIO</a>
* @author Tor-Einar Skog <tor-einar.skog@nibio.no>
*/
public class DataMatrix extends DateMap{
......@@ -31,4 +31,8 @@ public class DataMatrix extends DateMap{
public final static String DAILY_CONTRIB = "DC";
public final static String HEAT_SUM = "HEAT_SUM";
public final static String PHASE = "PHASE";
public final static String PHASE_EGG ="EGG";
public final static String PHASE_LARVAE ="LARVAE";
public final static String PHASE_PUPA ="PUPA";
public final static String PHASE_ADULT ="ADULT";
}
......@@ -26,6 +26,7 @@ import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
......@@ -58,17 +59,21 @@ public class YellowStemborerTempModel extends I18nImpl implements Model {
private TimeZone timeZone;
private DataMatrix dataMatrix;
private Date biofixDate;
private String[] phases = {"EGG","LARVAE","PUPA","ADULT"};
private final String[] phases = {
DataMatrix.PHASE_EGG,
DataMatrix.PHASE_LARVAE,
DataMatrix.PHASE_PUPA,
DataMatrix.PHASE_ADULT
};
// DD aggregation cutoff values for each phase
private Double[] dd_lower = {13.0,12.0,15.0,13.0};
private Double[] dd_upper = {36.0, 34.0,36.0,36.0};
// Threshold values
// TODO: ADJUST with actual biologically sensible defaults
private Double dd_lower = 0.0;
private Double dd_upper = 40.0;
// Heat sum thresholds for each phase
private Double[] heatRequirements = {113.0, 370.0, 188.0, 25.0};
private Double THRESHOLD_1 = 260.0; // Egg to larvae
private Double THRESHOLD_2 = 360.0; // Larvae to pupa
private Double THRESHOLD_3 = 560.0; // Pupa to adult
private String observedPhase = DataMatrix.PHASE_EGG;
public YellowStemborerTempModel()
{
......@@ -79,7 +84,7 @@ public class YellowStemborerTempModel extends I18nImpl implements Model {
@Override
public List<Result> getResult() throws ModelExcecutionException {
this.calculateTemperatureSum();
//System.out.println(this.dataMatrix.toCSV());
List<Result> retVal = new ArrayList<>();
Date currentDate = this.dataMatrix.getFirstDateWithParameterValue(DataMatrix.HEAT_SUM);
Calendar cal = Calendar.getInstance(this.timeZone);
......@@ -94,35 +99,35 @@ public class YellowStemborerTempModel extends I18nImpl implements Model {
Double DAILY_CONTRIB = this.dataMatrix.getParamDoubleValueForDate(currentDate, DataMatrix.DAILY_CONTRIB);
Double HEAT_SUM = this.dataMatrix.getParamDoubleValueForDate(currentDate, DataMatrix.HEAT_SUM);
String PHASE = this.dataMatrix.getParamStringValueForDate(currentDate, DataMatrix.PHASE);
result.setValue(CommonNamespaces.NS_WEATHER, DataMatrix.TND, dFormat.format(TNDCurrentDate));
result.setValue(CommonNamespaces.NS_WEATHER, DataMatrix.TXD, dFormat.format(TXDCurrentDate));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.DAILY_CONTRIB, dFormat.format(DAILY_CONTRIB));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.HEAT_SUM, dFormat.format(HEAT_SUM));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), "THRESHOLD_1", dFormat.format(this.THRESHOLD_1));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), "THRESHOLD_2", dFormat.format(this.THRESHOLD_2));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), "THRESHOLD_3", dFormat.format(this.THRESHOLD_3));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.PHASE, PHASE);
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), "thresholdLarvae", dFormat.format(this.heatRequirements[0]));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), "thresholdPupa", dFormat.format(this.heatRequirements[0] + this.heatRequirements[1]));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), "thresholdAdult", dFormat.format(this.heatRequirements[0] + this.heatRequirements[1] + this.heatRequirements[2]));
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.PHASE, PHASE);
Integer warningStatus = Result.WARNING_STATUS_NO_WARNING;
Integer warningStatus = Result.WARNING_STATUS_NO_RISK;
if(HEAT_SUM < this.THRESHOLD_1)
if(PHASE.equals(DataMatrix.PHASE_EGG))
{
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.PHASE, this.phases[0]);
warningStatus = Result.WARNING_STATUS_NO_RISK;
}
if(HEAT_SUM >= this.THRESHOLD_1)
if(PHASE.equals(DataMatrix.PHASE_LARVAE))
{
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.PHASE, this.phases[1]);
warningStatus = Result.WARNING_STATUS_MINOR_RISK;
}
if(HEAT_SUM >= this.THRESHOLD_2)
if(PHASE.equals(DataMatrix.PHASE_PUPA))
{
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.PHASE, this.phases[2]);
warningStatus = Result.WARNING_STATUS_HIGH_RISK;
warningStatus = Result.WARNING_STATUS_MINOR_RISK;
}
if(HEAT_SUM >= this.THRESHOLD_3)
if(PHASE.equals(DataMatrix.PHASE_ADULT))
{
result.setValue(YellowStemborerTempModel.MODEL_ID.toString(), DataMatrix.PHASE, this.phases[3]);
warningStatus = Result.WARNING_STATUS_NO_WARNING;
warningStatus = Result.WARNING_STATUS_HIGH_RISK;
}
result.setWarningStatus(warningStatus);
retVal.add(result);
......@@ -236,6 +241,8 @@ public class YellowStemborerTempModel extends I18nImpl implements Model {
"\t\t}\n" +
"]}\n";
}
@Override
public void setConfiguration(ModelConfiguration config) throws ConfigValidationException {
......@@ -243,26 +250,47 @@ public class YellowStemborerTempModel extends I18nImpl implements Model {
// Adjusting defaults
try
{
if(config.getConfigParameter("THRESHOLD_1") != null)
if(config.getConfigParameter("heatRequirements") != null)
{
this.THRESHOLD_1 = this.modelUtil.getDouble(config.getConfigParameter("THRESHOLD_1"));
}
if(config.getConfigParameter("THRESHOLD_2") != null)
{
this.THRESHOLD_2 = this.modelUtil.getDouble(config.getConfigParameter("THRESHOLD_2"));
}
if(config.getConfigParameter("THRESHOLD_3") != null)
{
this.THRESHOLD_3 = this.modelUtil.getDouble(config.getConfigParameter("THRESHOLD_3"));
this.heatRequirements = this.modelUtil.getDoubleArray(config.getConfigParameter("heatRequirements"));
if(this.heatRequirements.length != 4)
{
throw new ConfigValidationException("ERROR: Array of heat requirements has " + this.dd_lower.length + " elements, should be 4");
}
}
if(config.getConfigParameter("dd_lower") != null)
{
this.dd_lower = this.modelUtil.getDouble(config.getConfigParameter("dd_lower"));
this.dd_lower = this.modelUtil.getDoubleArray(config.getConfigParameter("dd_lower"));
if(this.dd_lower.length != 4)
{
throw new ConfigValidationException("ERROR: Array of lower cutoffs has " + this.dd_lower.length + " elements, should be 4");
}
}
if(config.getConfigParameter("dd_upper") != null)
{
this.dd_upper = this.modelUtil.getDouble(config.getConfigParameter("dd_upper"));
this.dd_upper = this.modelUtil.getDoubleArray(config.getConfigParameter("dd_upper"));
if(this.dd_upper.length != 4)
{
throw new ConfigValidationException("ERROR: Array of upper cutoffs has " + this.dd_upper.length + " elements, should be 4");
}
}
if(config.getConfigParameter("observedPhase") != null)
{
if(!(config.getConfigParameter("observedPhase") instanceof String))
{
throw new ConfigValidationException("ERROR: Input parameter \"configParameter\" is " + config.getConfigParameter("observedPhase").getClass().getName() + ", must be a String");
}
String inputObservedPhase = (String) config.getConfigParameter("observedPhase");
if(Arrays.asList(phases).contains(inputObservedPhase))
{
this.observedPhase = inputObservedPhase;
}
else
{
throw new ConfigValidationException("ERROR: observedPhase \"" + inputObservedPhase + "\" is not among the valid phases (" + String.join(",", phases) + ")");
}
}
}
catch(ClassCastException ex)
{
......@@ -338,7 +366,15 @@ public class YellowStemborerTempModel extends I18nImpl implements Model {
Date lastDate = this.dataMatrix.getLastDateWithParameterValue(DataMatrix.TND);
Calendar cal = Calendar.getInstance(this.timeZone);
Double sum = 0.0;
String currentPhase = this.observedPhase;
Double sum = currentPhase.equals(DataMatrix.PHASE_EGG) ? 0.0
: currentPhase.equals(DataMatrix.PHASE_LARVAE) ? this.heatRequirements[0]
: currentPhase.equals(DataMatrix.PHASE_PUPA) ? this.heatRequirements[0] + this.heatRequirements[1]
: this.heatRequirements[0] + this.heatRequirements[1] + this.heatRequirements[2];
List<String> phasesList = Arrays.asList(this.phases);
while(today.compareTo(lastDate) <= 0)
{
WeatherObservation todayMinTemp = (WeatherObservation)this.dataMatrix.getParamValueForDate(today, DataMatrix.TND);
......@@ -348,10 +384,30 @@ public class YellowStemborerTempModel extends I18nImpl implements Model {
throw new ModelExcecutionException("Missing weather data at " + today + ": " + (todayMinTemp == null ? "TND ": "") + (todayMaxTemp == null ? "TXD ": ""));
}
//System.out.println("today=" + today + ",todayTemp=" + todayTemp);
Double dailyContribution = nlc.calculateSingleSineWaveWithCutoff(todayMinTemp.getValue(), todayMaxTemp.getValue(), this.dd_lower, this.dd_upper);
Double current_dd_lower = this.dd_lower[phasesList.indexOf(currentPhase)];
Double current_dd_upper = this.dd_upper[phasesList.indexOf(currentPhase)];
Double dailyContribution = nlc.calculateSingleSineWaveWithCutoff(todayMinTemp.getValue(), todayMaxTemp.getValue(), current_dd_lower, current_dd_upper);
this.dataMatrix.setParamDoubleValueForDate(today, DataMatrix.DAILY_CONTRIB, dailyContribution);
sum += dailyContribution;
this.dataMatrix.setParamDoubleValueForDate(today, DataMatrix.HEAT_SUM, sum);
// Check phase transition
if(sum >= this.heatRequirements[0] + this.heatRequirements[1] + this.heatRequirements[2])
{
currentPhase = DataMatrix.PHASE_ADULT;
}
else if(sum >= this.heatRequirements[0] + this.heatRequirements[1])
{
currentPhase = DataMatrix.PHASE_PUPA;
}
else if(sum >= this.heatRequirements[0])
{
currentPhase = DataMatrix.PHASE_LARVAE;
}
else
{
currentPhase = DataMatrix.PHASE_EGG;
}
this.dataMatrix.setParamStringValueForDate(today, DataMatrix.PHASE, currentPhase);
cal.setTime(today);
cal.add(Calendar.DATE, 1);
today = cal.getTime();
......
......@@ -50,26 +50,48 @@ public class YellowStemborerTempModelTest {
@Test
public void testGetResult() throws Exception {
System.out.println("getResult");
// REQUIRED
WeatherDataFileReader wfr = new WeatherDataFileReader();
// Weather data files can be placed in ("src/test/resources")
ModelConfiguration config = wfr.getModelConfigurationWithWeatherData("/test_data_2019.json", YellowStemborerTempModel.MODEL_ID.toString());
// The timezone is used to set daily temperatures and biofix date correctly
config.setConfigParameter("timeZone", "GMT+05:30");
// The date for when to start calculating heat sums
config.setConfigParameter("biofixDate", "2019-01-05");
config.setConfigParameter("THRESHOLD_1", 290.0);
config.setConfigParameter("THRESHOLD_2", 390.0);
config.setConfigParameter("THRESHOLD_3", 590);
config.setConfigParameter("dd_lower", 15);
config.setConfigParameter("dd_upper", 31.1);
// OPTIONAL
// The observed phase at biofix date. Default is EGG
config.setConfigParameter("observedPhase", DataMatrix.PHASE_EGG);
// Heat requirement for transitioning out of [EGG,LARVAE,PUPA,ADULT] phases
Double[] heatRequirements = {113.0, 370.0, 188.0, 25.0};
// Lower and upper cutoffs for [EGG,LARVAE,PUPA,ADULT] phases
Double[] dd_lower = {13.0,12.0,15.0,13.0};
Double[] dd_upper = {36.0, 34.0,36.0,36.0};
config.setConfigParameter("heatRequirements", heatRequirements);
config.setConfigParameter("dd_lower", dd_lower);
config.setConfigParameter("dd_upper", dd_upper);
//config.setConfigParameter("observedPhase", "Donald Duck");
// Print the config
System.out.println(config.toJSON());
//System.out.println(config.toJSON());
// Run the test
YellowStemborerTempModel instance = new YellowStemborerTempModel();
instance.setConfiguration(config);
List<Result> result = instance.getResult();
assertNotNull(result);
// Print the result
//ObjectMapper mapper = new ObjectMapper();
//System.out.println(mapper.writeValueAsString(result));
//result.forEach(r->System.out.println(r));
/*ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(result));
result.forEach(r->System.out.println(r));*/
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment