diff --git a/src/main/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModel.java b/src/main/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModel.java index 5856e184d3f5667818c2dd728ec5aba8ce802f57..6624ebb97fd5202581dad1ba87e0b2d4b73ce4cc 100644 --- a/src/main/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModel.java +++ b/src/main/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModel.java @@ -66,6 +66,16 @@ public class GrassDryingModel extends I18nImpl implements Model{ private Integer conditioning; private Double stringWidth; private Double harvestWidth; + // Either this is set... + private Double initialDryMatterFraction; + // ... or the three values below has to be set for estimates of dry matter fraction + private Integer seasonHarvestNumber; // First, second or [X]th harvest this season? + private Integer cloverFraction; + private Double precipitationLastDayBeforeHarvest; + + // Constants for cloverFraction + private final Integer CLOVER_FRACTION_SMALL = 1; + private final Integer CLOVER_FRACTION_LARGE = 2; public GrassDryingModel() { @@ -92,6 +102,7 @@ public class GrassDryingModel extends I18nImpl implements Model{ Double dryMatter = this.calculateTS( this.calculateMCWB( this.calculateM( + this.getMCWBInit(), this.calculateMe(this.UM.get(i).getValue()), sigma_E, this.calculateR_a(this.stringWidth, this.RR.get(i).getValue()), @@ -167,6 +178,10 @@ public class GrassDryingModel extends I18nImpl implements Model{ "\t\t\"conditioning\":2,\n" + "\t\t\"stringWidth\":3.0,\n" + "\t\t\"harvestWidth\":3.0,\n" + + "\t\t\"initialDryMatterFraction\":17.7,\n" + + "\t\t\"seasonHarvestNumber\":1,\n" + + "\t\t\"cloverFraction\":1,\n" + + "\t\t\"precipitationLastDayBeforeHarvest\":2.453987,\n" + "\t\t\"observations\":[\n" + "\t\t{\n" + "\t\t\t\t\"timeMeasured\": \"2014-06-25T00:00:00+02:00\",\n" + @@ -203,6 +218,11 @@ public class GrassDryingModel extends I18nImpl implements Model{ "}\n"; } + /** + * TODO: Finish validation + * @param config + * @throws ConfigValidationException + */ @Override public void setConfiguration(ModelConfiguration config) throws ConfigValidationException { this.initCollections(); @@ -212,6 +232,29 @@ public class GrassDryingModel extends I18nImpl implements Model{ this.timeZone = TimeZone.getTimeZone((String) config.getConfigParameter("timeZone")); this.yield = (Double) config.getConfigParameter("yield"); + this.initialDryMatterFraction = (Double) config.getConfigParameter("initialDryMatterFraction"); + if(this.initialDryMatterFraction == null || this.initialDryMatterFraction > 100) + { + this.seasonHarvestNumber = (Integer) config.getConfigParameter("seasonHarvestNumber"); + if(this.seasonHarvestNumber == null || this.seasonHarvestNumber < 1 || this.seasonHarvestNumber > 2) + { + throw new ConfigValidationException("Acceptable values for seasonHarvestNumber are [1,2]"); + } + this.precipitationLastDayBeforeHarvest = (Double) config.getConfigParameter("precipitationLastDayBeforeHarvest"); + if(this.precipitationLastDayBeforeHarvest == null) + { + throw new ConfigValidationException("Missing value for precipitation last day before harvest"); + } + this.cloverFraction = (Integer) config.getConfigParameter("cloverFraction"); + if( + this.cloverFraction == null + || (!this.cloverFraction.equals(this.CLOVER_FRACTION_SMALL) + && !this.cloverFraction.equals(this.CLOVER_FRACTION_LARGE)) + ) + { + throw new ConfigValidationException("Missing or invalid value for clover fraction"); + } + } // 1 = weak, 2 = strong, 3 = crushed this.conditioning = (Integer) config.getConfigParameter("conditioning"); if(!this.conditioning.equals(1) && !this.conditioning.equals(2) && !this.conditioning.equals(3)) @@ -220,7 +263,8 @@ public class GrassDryingModel extends I18nImpl implements Model{ } this.stringWidth = (Double) config.getConfigParameter("stringWidth"); this.harvestWidth = (Double) config.getConfigParameter("harvestWidth"); - // private Double yield; // Kgs? Tons?? Per hectar? + + // ############ Weather data ############## @@ -343,11 +387,15 @@ public class GrassDryingModel extends I18nImpl implements Model{ return M / (1 + M); } - public Double calculateM(Double Me, Double sigma_E, Double R_a, Double Korr_DRC, Double rewet) + public Double getMCWBInit() throws ModelExcecutionException + { + return 1.0 - this.getInitialDryMatterFraction() / 100; + } + + public Double calculateM(Double MCWBInit, Double Me, Double sigma_E, Double R_a, Double Korr_DRC, Double rewet) throws ModelExcecutionException { // Constants - Double MCWBinit = 0.823; - Double M_0 = MCWBinit/(1-MCWBinit); + Double M_0 = MCWBInit/(1-MCWBInit); Double rainWaterEpoFactor = 0.0; return (M_0 - Me) * Math.exp(-Korr_DRC * (sigma_E - rainWaterEpoFactor * R_a)) + Me + rewet; } @@ -392,4 +440,66 @@ public class GrassDryingModel extends I18nImpl implements Model{ return dryingRateCoefficient * correctionFactor; } + public Double getInitialDryMatterFraction() throws ModelExcecutionException { + if(this.initialDryMatterFraction != null) + { + return this.initialDryMatterFraction; + } + // If not set by user, estimate from other input data + if(this.seasonHarvestNumber == 1) + { + if(this.precipitationLastDayBeforeHarvest <= 1) + { + if(this.cloverFraction.equals(this.CLOVER_FRACTION_SMALL)) + { + return this.yield * 0.0040 + 17.62; + } + else + { + return this.yield * 0.0012 + 18.03; + } + } + else + { + if(this.cloverFraction.equals(this.CLOVER_FRACTION_SMALL)) + { + return (this.yield * 0.0040) - (this.precipitationLastDayBeforeHarvest * 0.4727) + 15.86; + } + else + { + return (this.yield * 0.0038) - (this.precipitationLastDayBeforeHarvest * 0.0520) + 15.97; + } + } + } + else if(this.seasonHarvestNumber == 2) + { + if(this.precipitationLastDayBeforeHarvest <= 1) + { + if(this.cloverFraction.equals(this.CLOVER_FRACTION_SMALL)) + { + return this.yield * 0.0033 + 20.04; + } + else + { + return this.yield * 0.0133 + 16.31; + } + } + else + { + if(this.cloverFraction.equals(this.CLOVER_FRACTION_SMALL)) + { + return (this.yield * 0.0152) - (this.precipitationLastDayBeforeHarvest * 0.0300) + 10.86; + } + else + { + return (this.yield * 0.0122) - (this.precipitationLastDayBeforeHarvest * 0.1964) + 14.22; + } + } + } + else + { + throw new ModelExcecutionException("Only able to estimate initial dry matter fraction from 1st and 2nd harvest."); + } + } + } diff --git a/src/test/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModelTest.java b/src/test/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModelTest.java index 8eabb8dfbf769c49830fe923fe41a9161b07d58a..a1608d9fe58c250fa506ef2f47819b590cb4f1a4 100644 --- a/src/test/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModelTest.java +++ b/src/test/java/no/bioforsk/vips/model/grassdryingmodel/GrassDryingModelTest.java @@ -28,6 +28,8 @@ import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; +import java.util.logging.Level; +import java.util.logging.Logger; import static junit.framework.Assert.fail; import no.bioforsk.vips.entity.ModelConfiguration; import no.bioforsk.vips.entity.Result; @@ -79,31 +81,7 @@ public class GrassDryingModelTest { // @Test // public void hello() {} - @Test - public void testAcceptance() - { - System.out.println("testAcceptance"); - try - { - GrassDryingModel instance = new GrassDryingModel(); - ModelConfiguration config = this.getConfiguration("/weatherData.json"); - config.setConfigParameter("yield", 750.0); - config.setConfigParameter("conditioning", 2); - config.setConfigParameter("stringWidth", 3.0); - config.setConfigParameter("harvestWidth", 3.0); - - instance.setConfiguration(config); - - List<Result> results = instance.getResult(); - - assertEquals(18.01757902,Double.valueOf(results.get(0).getValue(GrassDryingModel.MODEL_ID.toString(), "DRY_MATTER")), 0.0001); - assertEquals(19.16504279,Double.valueOf(results.get(16).getValue(GrassDryingModel.MODEL_ID.toString(), "DRY_MATTER")), 0.0001); - } - catch(ConfigValidationException | ModelExcecutionException ex) - { - fail("Exception: " + ex.getMessage()); - } - } + @Test public void testCalculateE() @@ -180,7 +158,13 @@ public class GrassDryingModelTest { System.out.println("testCalculateM"); GrassDryingModel instance = new GrassDryingModel(); Double sigma_E = instance.calculateE(15.1, 57.9, 91.3, 5.8); - Double result = instance.calculateM(instance.calculateMe(57.9), sigma_E, instance.calculateR_a(3.0, 0.0), 0.141288948, 0.0); + Double result = 0.0; + + try { + result = instance.calculateM(0.823, instance.calculateMe(57.9), sigma_E, instance.calculateR_a(3.0, 0.0), 0.141288948, 0.0); + } catch (ModelExcecutionException ex) { + fail(ex.getMessage()); + } assertEquals(4.550135226, result,0.00001); } @@ -193,6 +177,46 @@ public class GrassDryingModelTest { assertEquals(18.28797909, result, 0.0001); } + @Test + public void testAcceptance() + { + System.out.println("testAcceptance"); + try + { + GrassDryingModel instance = new GrassDryingModel(); + ModelConfiguration config = this.getConfiguration("/weatherData.json"); + config.setConfigParameter("yield", 750.0); + config.setConfigParameter("conditioning", 2); + config.setConfigParameter("stringWidth", 3.0); + config.setConfigParameter("harvestWidth", 3.0); + config.setConfigParameter("seasonHarvestNumber", 1); + config.setConfigParameter("cloverFraction", 1); + // Optimized to give same MCWBInit as in Excel sheet + config.setConfigParameter("precipitationLastDayBeforeHarvest", 2.453987); + + instance.setConfiguration(config); + + List<Result> results = instance.getResult(); + + assertEquals(18.01757902,Double.valueOf(results.get(0).getValue(GrassDryingModel.MODEL_ID.toString(), "DRY_MATTER")), 0.0001); + assertEquals(19.16504279,Double.valueOf(results.get(16).getValue(GrassDryingModel.MODEL_ID.toString(), "DRY_MATTER")), 0.0001); + + // Testing with predefined dry matter fraction + config.setConfigParameter("initialDryMatterFraction", 18.7); + instance.setConfiguration(config); + + results = instance.getResult(); + + assertEquals(19.03080738,Double.valueOf(results.get(0).getValue(GrassDryingModel.MODEL_ID.toString(), "DRY_MATTER")), 0.0001); + assertEquals(20.22019628,Double.valueOf(results.get(16).getValue(GrassDryingModel.MODEL_ID.toString(), "DRY_MATTER")), 0.0001); + + } + catch(ConfigValidationException | ModelExcecutionException ex) + { + fail("Exception: " + ex.getMessage()); + } + } + private ModelConfiguration getConfiguration(String fileName) { try {