From dd850cdb637edd01ccb36790faead5c60dcc3df8 Mon Sep 17 00:00:00 2001 From: Tor-Einar Skog <tor-einar.skog@bioforsk.no> Date: Fri, 18 Nov 2016 16:22:51 -0800 Subject: [PATCH] Allowing for just one of height / MSC to be set of the optimization parameters Avoiding some ConcurrentModificationExceptions by copying list --- .../OptimizationObservation.java | 4 + .../RoughageNutritionModel.java | 28 +- .../RoughageNutritionModelImpl.java | 1662 ++++++++--------- .../RoughageNutritionModelTest.java | 4 + 4 files changed, 779 insertions(+), 919 deletions(-) diff --git a/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/OptimizationObservation.java b/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/OptimizationObservation.java index c3dd382..2f2596b 100644 --- a/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/OptimizationObservation.java +++ b/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/OptimizationObservation.java @@ -65,6 +65,10 @@ public class OptimizationObservation implements Comparable{ */ public Double getAvling() { + if(this.getHeight() == null) + { + return null; + } return 34.86f + 0.017f * (Double) Math.pow(this.getHeight(), 2) + 10.21f * this.getHeight(); } diff --git a/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModel.java b/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModel.java index d83a609..88edc17 100644 --- a/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModel.java +++ b/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModel.java @@ -236,8 +236,7 @@ public class RoughageNutritionModel implements Model { @Override public void setConfiguration(ModelConfiguration config) throws ConfigValidationException { - - + /* // Daily values List<WeatherObservation> luftTemperatur, TM @@ -456,27 +455,36 @@ public class RoughageNutritionModel implements Model { } } - private List<OptimizationObservation> parseOptimizationInfo(List<String> optimizationInfo, TimeZone timeZone) { + private List<OptimizationObservation> parseOptimizationInfo(List<String> optimizationInfo, TimeZone timeZone) throws ConfigValidationException { List<OptimizationObservation> retVal = new ArrayList<>(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); format.setTimeZone(timeZone); for(String line:optimizationInfo) { - String[] parts = line.split(","); + // The -1 as second parameter ensures that trailing delimiters will not be discarded, + // Thus avoiding ArrayIndexOutOfBoundsException when parsing + // E.g. "2015-06-02,2,3,,,," + String[] parts = line.split(",",-1); try { OptimizationObservation optObs = new OptimizationObservation( format.parse(parts[0]), // Date - Double.parseDouble(parts[1]), // Height - Double.parseDouble(parts[2]), // MSC - Double.parseDouble(parts[4]), // NDF - Double.parseDouble(parts[5]), // INDF - Double.parseDouble(parts[6]), // Raw protein - Double.parseDouble(parts[3]) // FEm + (! parts[1].trim().isEmpty() ? Double.parseDouble(parts[1]) : null), // Height + (! parts[2].trim().isEmpty() ? Double.parseDouble(parts[2]) : null), // MSC + (! parts[4].trim().isEmpty() ? Double.parseDouble(parts[4]) : null), // NDF + (! parts[5].trim().isEmpty() ? Double.parseDouble(parts[5]) : null), // INDF + (! parts[6].trim().isEmpty() ? Double.parseDouble(parts[6]) : null), // Raw protein + (! parts[3].trim().isEmpty() ? Double.parseDouble(parts[3]) : null) // FEm ); + if(optObs.getHeight() == null && optObs.getMSC() == null) + { + throw new ConfigValidationException("Optimization error: Height and/or MSC must be set"); + } retVal.add(optObs); } catch(ArrayIndexOutOfBoundsException | ParseException ex) { + throw new ConfigValidationException("There's something wrong with this line of optimization observations: " + + line + " . The error is: " + ex.getClass() + ":" + ex.getMessage()); } } return retVal; diff --git a/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelImpl.java b/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelImpl.java index 7452eea..c88cfc3 100644 --- a/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelImpl.java +++ b/src/main/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 NIBIO <http://www.nibio.no/>. + * Copyright (c) 2016 NIBIO <http://www.nibio.no/>. * * This file is part of RoughageNutritionModel. * RoughageNutritionModel is free software: you can redistribute it and/or modify @@ -16,10 +16,10 @@ * along with RoughageNutritionModel. If not, see <http://www.nibio.no/licenses/>. * */ - package no.bioforsk.vips.model.roughagenutritionmodel; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; @@ -44,13 +44,11 @@ import org.apache.commons.math.optimization.CostFunction; import org.apache.commons.math.optimization.NelderMead; import org.apache.commons.math.optimization.PointCostPair; - - /** - * @copyright 2015 <a href="http://www.nibio.no/">NIBIO</a> + * @copyright 2016 <a href="http://www.nibio.no/">NIBIO</a> * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ -public class RoughageNutritionModelImpl implements CostFunction{ +public class RoughageNutritionModelImpl implements CostFunction { // Globale variable som må nullstilles for hver beregning av modellen private List<WeatherObservation> nedboerVerdier; @@ -66,7 +64,7 @@ public class RoughageNutritionModelImpl implements CostFunction{ private FEmDataMatrix FEmBakgrunnsdataMatrise; private ResultMatrix resultatMatrise; - + private Date datoFoersteSlaatt; private Date datoAndreSlaatt; @@ -92,7 +90,7 @@ public class RoughageNutritionModelImpl implements CostFunction{ private List<OptimizationObservation> observasjoner; //private Float MSCVedFoersteslaatt = null; - private final static Double MSCVedFoersteslaattGrenseverdi = 2.4; + private final static Double MSC_VED_FOERSTESLAATT_GRENSEVERDI = 2.4; // "Konstante" (optimerbare) variable brukt i beregning av utviklingsstadium private Double alfa; @@ -106,18 +104,20 @@ public class RoughageNutritionModelImpl implements CostFunction{ private Double pINDF; private Double FEmJ; private Double FEmM; - + private Date calculatedDateOfGrowthStart; - + private TimeZone timeZone; - + /** * Hovedmetoden som beregner modellen + * * @param dailyMeanAirTemperature liste med døgnverdier for luftTemperatur - * @param dailyMeanSoilTemperature liste med døgnverdier for jordTemperatur (forbedrer - * beregning av vekststart, men er ikke obligatorisk) + * @param dailyMeanSoilTemperature liste med døgnverdier for jordTemperatur + * (forbedrer beregning av vekststart, men er ikke obligatorisk) * @param dailyPrecipitation liste med døgnverdier for nedbør - * @param dailyPotentialEvaporation liste med døgnverdier for potensiell fordamping + * @param dailyPotentialEvaporation liste med døgnverdier for potensiell + * fordamping * @param dailyGlobalRadiation liste med døgnverdier for globalstråling * @param firstHarvest dato for førsteslått * @param secondHarvest dato for andreslått @@ -128,19 +128,18 @@ public class RoughageNutritionModelImpl implements CostFunction{ * @throws com.ac.march.ModelExcecutionException */ public synchronized ResultMatrix beregnModell( - List<WeatherObservation> dailyMeanAirTemperature, - List<WeatherObservation> dailyMeanSoilTemperature, - List<WeatherObservation> dailyPrecipitation, - List<WeatherObservation> dailyPotentialEvaporation, - List<WeatherObservation> dailyGlobalRadiation, - Date firstHarvest, - Date secondHarvest, - int soilType, - int cloverShare, + List<WeatherObservation> dailyMeanAirTemperature, + List<WeatherObservation> dailyMeanSoilTemperature, + List<WeatherObservation> dailyPrecipitation, + List<WeatherObservation> dailyPotentialEvaporation, + List<WeatherObservation> dailyGlobalRadiation, + Date firstHarvest, + Date secondHarvest, + int soilType, + int cloverShare, List<OptimizationObservation> optimizationObservations, TimeZone timeZone - ) throws ModelExcecutionException - { + ) throws ModelExcecutionException { // Initaliserer modellen this.luftTemperaturVerdier = dailyMeanAirTemperature; this.jordTemperaturVerdier = dailyMeanSoilTemperature; @@ -157,25 +156,20 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Valider inputdata validateInputData(); - Calendar cal = Calendar.getInstance(timeZone); // Hvis datoAndreSlaatt == null, sett den til 31/12 - if(this.datoAndreSlaatt == null) - { - + if (this.datoAndreSlaatt == null) { + cal.setTime(this.datoFoersteSlaatt); cal.set(Calendar.MONTH, Calendar.DECEMBER); - cal.set(Calendar.DATE,31); + cal.set(Calendar.DATE, 31); this.datoAndreSlaatt = cal.getTime(); } - - // Vi har fått inn observasjoner som kan brukes til optimering av interne // parametre i modellen. Da gjør vi det først. this.observasjoner = optimizationObservations; - if(this.observasjoner != null) - { + if (this.observasjoner != null) { try { this.optimerParametre(); } catch (CostException | ConvergenceException ex) { @@ -202,9 +196,8 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Siste post på programmet er å justere for målt råprotein etter at alle andre // beregninger er kjørt - if(this.observasjoner != null) - { - this.kallibrerRaaprotein(); + if (this.observasjoner != null) { + this.kalibrerRaaprotein(); // Setter verdien av optimerte parametre inn this.resultatMatrise.setOptimeringForetatt(true); @@ -221,96 +214,80 @@ public class RoughageNutritionModelImpl implements CostFunction{ this.resultatMatrise.setFEmm(this.FEmM); } - - // Returnerer resultatet return this.resultatMatrise; } - private double getTTVStart(int jordtype) - { - switch(jordtype) - { - case RoughageNutritionModel.JORDTYPE_LEIR: - return 25.6; - case RoughageNutritionModel.JORDTYPE_SAND: - return 67.7; - case RoughageNutritionModel.JORDTYPE_SILT: - return 124.0; - default: - return 0; + private double getTTVStart(int jordtype) { + switch (jordtype) { + case RoughageNutritionModel.JORDTYPE_LEIR: + return 25.6; + case RoughageNutritionModel.JORDTYPE_SAND: + return 67.7; + case RoughageNutritionModel.JORDTYPE_SILT: + return 124.0; + default: + return 0; } } - private double getLTVStart(int jordtype) - { - switch(jordtype) - { - case RoughageNutritionModel.JORDTYPE_LEIR: - return 82.3; - case RoughageNutritionModel.JORDTYPE_SAND: - return 46.9; - case RoughageNutritionModel.JORDTYPE_SILT: - return 83.3; - default: - return 0; + private double getLTVStart(int jordtype) { + switch (jordtype) { + case RoughageNutritionModel.JORDTYPE_LEIR: + return 82.3; + case RoughageNutritionModel.JORDTYPE_SAND: + return 46.9; + case RoughageNutritionModel.JORDTYPE_SILT: + return 83.3; + default: + return 0; } } /** * Beregner NDF, inkl. bakgrunnsdata + * * @param kloeverandel konstant (Definert i denne klassen) * @param jordtype konstant (Definert i denne klassen) * @throws com.ac.march.ModelExcecutionException */ - private void beregnNDFBakgrunnsdata(int kloeverandel, int jordtype) throws ModelExcecutionException - { + private void beregnNDFBakgrunnsdata(int kloeverandel, int jordtype) throws ModelExcecutionException { Collections.sort((List) this.luftTemperaturVerdier); WeatherObservation temperatur; - if(this.NDFBakgrunnsdataMatrise == null) + if (this.NDFBakgrunnsdataMatrise == null) { this.NDFBakgrunnsdataMatrise = new NDFDataMatrix(); + } boolean first = true; Date forrigeDato = null; - for(Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator();tempI.hasNext();) - { + for (Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator(); tempI.hasNext();) { temperatur = tempI.next(); // Beregner TS1 double TS1; - if(first) - { + if (first) { first = false; TS1 = temperatur.getValue(); - - } - else - { - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) - TS1 = 0; - else if(this.getAvling(forrigeDato, jordtype) > 50) - TS1 = temperatur.getValue() + this.NDFBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, NDFDataMatrix.TS1); - else - TS1 = this.NDFBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, NDFDataMatrix.TS1); + } else if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) { + TS1 = 0; + } else if (this.getAvling(forrigeDato, jordtype) > 50) { + TS1 = temperatur.getValue() + this.NDFBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, NDFDataMatrix.TS1); + } else { + TS1 = this.NDFBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, NDFDataMatrix.TS1); } this.NDFBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), NDFDataMatrix.TS1, TS1); // Beregner NDF double NDF; - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) - { + if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) { NDF = 0.35; - } - else if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) < 0) - { + } else if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) < 0) { - NDF = (double) (this.getNDm(kloeverandel) / (1 + Math.exp(this.getD(temperatur.getTimeMeasured())/this.getNRm(temperatur.getTimeMeasured(), kloeverandel) - this.getC(temperatur.getTimeMeasured(), kloeverandel) * this.getMSC(temperatur.getTimeMeasured())))); + NDF = (double) (this.getNDm(kloeverandel) / (1 + Math.exp(this.getD(temperatur.getTimeMeasured()) / this.getNRm(temperatur.getTimeMeasured(), kloeverandel) - this.getC(temperatur.getTimeMeasured(), kloeverandel) * this.getMSC(temperatur.getTimeMeasured())))); //System.out.println(temperatur.getTimeMeasured().toString() + ": " + NDF + "=" + this.getNDm(kloeverandel) + "/(1+Math.exp(" + this.getD(temperatur.getTimeMeasured()) + "/" + this.getNRm(temperatur.getTimeMeasured(), kloeverandel) + "-" + this.getC(temperatur.getTimeMeasured(),kloeverandel) + "*" + this.getMSC(temperatur.getTimeMeasured())+ ")"); - } - else - { - NDF = (double) (this.getNDm(kloeverandel) / (1 + Math.exp(this.getD(temperatur.getTimeMeasured())/this.getNRm(temperatur.getTimeMeasured(), kloeverandel) - this.getC(temperatur.getTimeMeasured(), kloeverandel) * TS1))); + } else { + NDF = (double) (this.getNDm(kloeverandel) / (1 + Math.exp(this.getD(temperatur.getTimeMeasured()) / this.getNRm(temperatur.getTimeMeasured(), kloeverandel) - this.getC(temperatur.getTimeMeasured(), kloeverandel) * TS1))); } // Creating percent points @@ -318,8 +295,7 @@ public class RoughageNutritionModelImpl implements CostFunction{ this.NDFBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), NDFDataMatrix.NDF, NDF); // Lagrer NDF i resultatmatrisen - if(this.resultatMatrise != null) - { + if (this.resultatMatrise != null) { this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.NDF, NDF); } @@ -330,197 +306,179 @@ public class RoughageNutritionModelImpl implements CostFunction{ } /** - * Returnerer NDF (fiberkonsentrasjon, andel av tørrstoff) for en bestemt dato + * Returnerer NDF (fiberkonsentrasjon, andel av tørrstoff) for en bestemt + * dato + * * @param dato * @param kloeverandel konstant (Definert i denne klassen) * @param jordtype konstant (Definert i denne klassen) * @return NDF-verdi for en bestemt dato * @throws com.ac.march.ModelExcecutionException */ - protected double getNDF(Date dato, int kloeverandel, int jordtype) throws ModelExcecutionException - { - if(this.NDFBakgrunnsdataMatrise == null) + protected double getNDF(Date dato, int kloeverandel, int jordtype) throws ModelExcecutionException { + if (this.NDFBakgrunnsdataMatrise == null) { this.beregnNDFBakgrunnsdata(kloeverandel, jordtype); + } return this.NDFBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, NDFDataMatrix.NDF); } /** * Brukes i beregning av NDF + * * @param kloeverandel konstant * @return */ - private double getNDm(int kloeverandel) - { - if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN) - return 0.75; - else if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM) - return 0.7; - else if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR) - return 0.65; - else return 0; + private double getNDm(int kloeverandel) { + switch (kloeverandel) { + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN: + return 0.75; + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM: + return 0.7; + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR: + return 0.65; + default: + return 0; + } } /** - * Brukes i beregning av NDF - * 2011-03-01: Endret metoden til å bli avhengig av verdien til MSC ved førsteslått, ihht. - * dokumentasjon av 28/2-2011 fra Anne Kjersti Bakken og Anne Langerud<br/> - * 2013-11-01: Endret metoden til at MSC-verdi ved førsteslått kun har betydning mellom - * første- og andreslått, ikke etter andreslått. - * - * @param dato + * Brukes i beregning av NDF 2011-03-01: Endret metoden til å bli avhengig + * av verdien til MSC ved førsteslått, ihht. dokumentasjon av 28/2-2011 fra + * Anne Kjersti Bakken og Anne Langerud<br/> + * 2013-11-01: Endret metoden til at MSC-verdi ved førsteslått kun har + * betydning mellom første- og andreslått, ikke etter andreslått. + * + * @param dato * @param kloeverandel konstant * @param MSCVedFoersteSlaatt vedien av MSC ved førsteslått * @return */ - private double getC(Date dato, int kloeverandel) throws ModelExcecutionException - { - if(dato.compareTo(this.datoFoersteSlaatt) <= 0) - { - if(this.cFoersteslaatt != null) - { + private double getC(Date dato, int kloeverandel) throws ModelExcecutionException { + if (dato.compareTo(this.datoFoersteSlaatt) <= 0) { + if (this.cFoersteslaatt != null) { return this.cFoersteslaatt; // Optimert verdi - } - else - { + } else { //return 1; // Testreturnverdi /* Default ikke-optimerte verdier */ - if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN) - return 1.15; - else if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM) - return 1.2; - else if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR) - return 1.25; - else return 0; - } - - } - else - { + switch (kloeverandel) { + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN: + return 1.15; + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM: + return 1.2; + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR: + return 1.25; + default: + return 0; + } + } + + } else { //return 0.0028; // Default fast returnverdi (skal ikke optimeres for gjenvekst) - return this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi && dato.compareTo(this.datoAndreSlaatt) <= 0 ? 0.0019f : 0.0014; + return this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI && dato.compareTo(this.datoAndreSlaatt) <= 0 ? 0.0019f : 0.0014; } } - /** * Brukes i beregning av NDF + * * @return */ - private double getNRm(Date dato, int kloeverandel) - { - if(dato.compareTo(this.datoFoersteSlaatt) <= 0) - { + private double getNRm(Date dato, int kloeverandel) { + if (dato.compareTo(this.datoFoersteSlaatt) <= 0) { //return 0.0995839970429309; // Testreturnverdi - return this.NRmFoersteslaatt != null ? this.NRmFoersteslaatt : 0.09; - } - else - { - //return 0.45;// Testreturnverdi - if(this.NRmAndreslaatt != null) - { - return this.NRmAndreslaatt; - } - else - { - /* Ikke-optimerte default-verdier for gjenvekst*/ - if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN) + return this.NRmFoersteslaatt != null ? this.NRmFoersteslaatt : 0.09; + } else //return 0.45;// Testreturnverdi + if (this.NRmAndreslaatt != null) { + return this.NRmAndreslaatt; + } else { + /* Ikke-optimerte default-verdier for gjenvekst*/ + switch (kloeverandel) { + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN: return 0.4; - else if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM) + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM: return 0.42; - else if(kloeverandel == RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR) + case RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR: return 0.44; - else + default: return 0; } } - + } /** - * Brukes i beregning av NDF - * 2011-03-01: Endret metoden til å bli avhengig av verdien til MSC ved førsteslått, ihht. - * dokumentasjon av 28/2-2011 fra Anne Kjersti Bakken og Anne Langerud - * 2013-11-01: Endret metoden til at MSC-verdi ved førsteslått kun har betydning mellom - * første- og andreslått, ikke etter andreslått. - * Brukes i beregning av NDF + * Brukes i beregning av NDF 2011-03-01: Endret metoden til å bli avhengig + * av verdien til MSC ved førsteslått, ihht. dokumentasjon av 28/2-2011 fra + * Anne Kjersti Bakken og Anne Langerud 2013-11-01: Endret metoden til at + * MSC-verdi ved førsteslått kun har betydning mellom første- og andreslått, + * ikke etter andreslått. Brukes i beregning av NDF + * * @return */ - private double getD(Date dato) throws ModelExcecutionException - { - if(dato.compareTo(this.datoFoersteSlaatt) <= 0) - { + private double getD(Date dato) throws ModelExcecutionException { + if (dato.compareTo(this.datoFoersteSlaatt) <= 0) { return 0.15; - } - else - { - return this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi && dato.compareTo(this.datoAndreSlaatt) <= 0 ? -0.12f : -0.15; + } else { + return this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI && dato.compareTo(this.datoAndreSlaatt) <= 0 ? -0.12f : -0.15; } } /** - * + * * @param dato * @return * @throws com.ac.march.ModelExcecutionException */ - protected double getINDF(Date dato) throws ModelExcecutionException - { - if(this.INDFBakgrunnsdataMatrise == null) - this.beregnINDFBakgrunnsdata(); - return this.INDFBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, INDFDataMatrix.INDF); + protected double getINDF(Date dato) throws ModelExcecutionException { + if (this.INDFBakgrunnsdataMatrise == null) { + this.beregnINDFBakgrunnsdata(); + } + return this.INDFBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, INDFDataMatrix.INDF); } /** - * Løper gjennom eksisterende klimadatasett og beregner bakgrunnsdata for INDF, samt - * INDF pr. dato. Avhenger av temperaturdata + * Løper gjennom eksisterende klimadatasett og beregner bakgrunnsdata for + * INDF, samt INDF pr. dato. Avhenger av temperaturdata + * * @throws com.ac.march.ModelExcecutionException */ - private void beregnINDFBakgrunnsdata() throws ModelExcecutionException - { + private void beregnINDFBakgrunnsdata() throws ModelExcecutionException { Collections.sort((List) this.luftTemperaturVerdier); WeatherObservation temperatur; - if(this.INDFBakgrunnsdataMatrise == null) + if (this.INDFBakgrunnsdataMatrise == null) { this.INDFBakgrunnsdataMatrise = new INDFDataMatrix(); + } boolean first = true; Date forrigeDato = null; Integer dagNummer = 1; - for(Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator();tempI.hasNext();) - { + for (Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator(); tempI.hasNext();) { temperatur = tempI.next(); // Beregner TS1 double TS1; - if(first) - { + if (first) { first = false; // Improbable, but U never know... - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) + if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) { TS1 = 0; - else + } else { TS1 = temperatur.getValue(); - } - else - { - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) - TS1 = 0; - else - TS1=temperatur.getValue() + this.INDFBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, INDFDataMatrix.TS1); + } + } else if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) { + TS1 = 0; + } else { + TS1 = temperatur.getValue() + this.INDFBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, INDFDataMatrix.TS1); } this.INDFBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), INDFDataMatrix.TS1, TS1); // Beregner INDF double INDF; - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) - { + if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) { INDF = 0; - } - else if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) < 0) - { - INDF = this.getIm()/(1+(double)Math.exp(this.getG()/this.getF()-this.getIRm()*TS1)); - } - else - { + } else if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) < 0) { + INDF = this.getIm() / (1 + (double) Math.exp(this.getG() / this.getF() - this.getIRm() * TS1)); + } else { INDF = (this.getBeta() * TS1) + this.getP(temperatur.getTimeMeasured()); } @@ -529,8 +487,7 @@ public class RoughageNutritionModelImpl implements CostFunction{ this.INDFBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), INDFDataMatrix.INDF, INDF); // Lagrer INDF i resultatmatrisen - if(this.resultatMatrise != null) - { + if (this.resultatMatrise != null) { this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.INDF, INDF); } forrigeDato = temperatur.getTimeMeasured(); @@ -542,23 +499,21 @@ public class RoughageNutritionModelImpl implements CostFunction{ * @return faktoren IRm, som brukes til beregning av INDF * @see #beregnINDFBakgrunnsdata() */ - private double getIRm() - { - if(this.IRmFoersteslaatt != null) + private double getIRm() { + if (this.IRmFoersteslaatt != null) { return this.IRmFoersteslaatt; - else + } else { return 0.0085d; // Default-verdi (ikke-optimert) - //return 0.00831136805776142; // TEST-verdi + } //return 0.00831136805776142; // TEST-verdi } - /** * Fom. 2011: Optimeres ikke lenger. Har fast verdi + * * @return faktoren Beta * @see #beregnINDFBakgrunnsdata() */ - private double getBeta() - { + private double getBeta() { return 0.0002d; //return this.betaINDF != null ? this.betaINDF : 0.00014; //return 0.00014; // Default-verdi (ikke-optimert) @@ -567,43 +522,43 @@ public class RoughageNutritionModelImpl implements CostFunction{ /** * Innført fra sesongen 2011.<br/> - * Optimert parameter. Avhenger av MSC ved førsteslått. - * 2013-11-01: Endret metoden til at MSC-verdi ved førsteslått kun har betydning mellom - * første- og andreslått, ikke etter andreslått. + * Optimert parameter. Avhenger av MSC ved førsteslått. 2013-11-01: Endret + * metoden til at MSC-verdi ved førsteslått kun har betydning mellom første- + * og andreslått, ikke etter andreslått. + * * @return */ - private double getP(Date dato) throws ModelExcecutionException - { - return this.pINDF != null ? this.pINDF : this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi && dato.compareTo(this.datoAndreSlaatt) <= 0 ? -0.0166f : -0.0193; + private double getP(Date dato) throws ModelExcecutionException { + return this.pINDF != null ? this.pINDF : this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI && dato.compareTo(this.datoAndreSlaatt) <= 0 ? -0.0166f : -0.0193; } /** * Dette er en fast parameter (skal ikke optimeres) + * * @return faktoren f, som brukes til beregning av INDF * @see #beregnINDFBakgrunnsdata() */ - private double getF() - { + private double getF() { return 0.001; } /** * Dette er en fast parameter (skal ikke optimeres) + * * @return faktoren g, som brukes til beregning av INDF * @see #beregnINDFBakgrunnsdata() */ - private double getG() - { + private double getG() { return 0.005; } /** * Dette er en fast parameter (skal ikke optimeres) + * * @return faktoren Im, som brukes til beregning av INDF * @see #beregnINDFBakgrunnsdata() */ - private double getIm() - { + private double getIm() { return 0.17; } @@ -614,69 +569,61 @@ public class RoughageNutritionModelImpl implements CostFunction{ * @return beregnet råproteinverdi for den gitte datoen og jordtypen * @throws com.ac.march.ModelExcecutionException */ - protected double getRaaprotein(Date dato, int jordtype) throws ModelExcecutionException - { - double dagensAvlingITonnToerrstoffPrHektar = this.getAvling(dato, jordtype)/100; - if(dagensAvlingITonnToerrstoffPrHektar < 1.5) - { + protected double getRaaprotein(Date dato, int jordtype) throws ModelExcecutionException { + double dagensAvlingITonnToerrstoffPrHektar = this.getAvling(dato, jordtype) / 100; + if (dagensAvlingITonnToerrstoffPrHektar < 1.5) { return 0d; + } else { + return 6.25 * (1.33 + (double) Math.exp(1.4 - 0.26 * dagensAvlingITonnToerrstoffPrHektar)); } - else - { - return 6.25 * (1.33 + (double) Math.exp(1.4-0.26*dagensAvlingITonnToerrstoffPrHektar)); - } - + } /** - * + * * @param dato * @return beregnet FEM (fôrhenhetskonsentrasjon) for gitt dato */ - protected double getFEm(Date dato) throws ModelExcecutionException - { - if(this.FEmBakgrunnsdataMatrise == null) + protected double getFEm(Date dato) throws ModelExcecutionException { + if (this.FEmBakgrunnsdataMatrise == null) { this.beregnFEmBakgrunnsdata(); + } return this.FEmBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, FEmDataMatrix.FEM); } /** - * Beregner bakgrunnsdata for FEM-beregning, og selve FEM-verdien - * NB! Fungerer ikke med klimadata som strekker seg over nyttår. + * Beregner bakgrunnsdata for FEM-beregning, og selve FEM-verdien NB! + * Fungerer ikke med klimadata som strekker seg over nyttår. + * * @throws com.ac.march.ModelExcecutionException */ - private void beregnFEmBakgrunnsdata() throws ModelExcecutionException - { + private void beregnFEmBakgrunnsdata() throws ModelExcecutionException { // Vi trenger MSC, sjekker om utviklingsdatamatrisen er klar - if(this.AIBakgrunnsdataMatrise == null) + if (this.AIBakgrunnsdataMatrise == null) { this.beregnAIBakgrunnsdata(); + } //GrovforModellFEmBakgrunnsdataMatriseBO Collections.sort((List) this.luftTemperaturVerdier); WeatherObservation temperatur; - if(this.FEmBakgrunnsdataMatrise == null) + if (this.FEmBakgrunnsdataMatrise == null) { this.FEmBakgrunnsdataMatrise = new FEmDataMatrix(); + } boolean first = true; Date forrigeDato = null; - for(Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator();tempI.hasNext();) - { + for (Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator(); tempI.hasNext();) { temperatur = tempI.next(); //Setter Ts2 double Ts2; - if(first) - { + if (first) { first = false; Ts2 = temperatur.getValue(); - } - else - { - if(forrigeDato.compareTo(this.getDatoFoersteSlaatt()) == 0 || forrigeDato.compareTo(this.getDatoAndreSlaatt()) == 0) - Ts2 = 0; - else if(forrigeDato.compareTo(this.getDatoFoersteSlaatt()) < 0) - Ts2 = this.FEmBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, FEmDataMatrix.TS2); - else - Ts2 = temperatur.getValue() - 6 + this.FEmBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, FEmDataMatrix.TS2); - + } else if (forrigeDato.compareTo(this.getDatoFoersteSlaatt()) == 0 || forrigeDato.compareTo(this.getDatoAndreSlaatt()) == 0) { + Ts2 = 0; + } else if (forrigeDato.compareTo(this.getDatoFoersteSlaatt()) < 0) { + Ts2 = this.FEmBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, FEmDataMatrix.TS2); + } else { + Ts2 = temperatur.getValue() - 6 + this.FEmBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, FEmDataMatrix.TS2); } this.FEmBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), FEmDataMatrix.TS2, Ts2); @@ -690,37 +637,25 @@ public class RoughageNutritionModelImpl implements CostFunction{ int dagNrAndreSlaatt = cal.get(Calendar.DAY_OF_YEAR); cal.setTime(temperatur.getTimeMeasured()); int dagNrTemperaturVerdi = cal.get(Calendar.DAY_OF_YEAR); - - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) - { + + if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) { FEm = 0; - } - else - { - if(dagNrTemperaturVerdi == dagNrFoersteSlaatt + 1 || dagNrTemperaturVerdi == dagNrAndreSlaatt) - { - FEm = 1.1; - } - else - { - double MSC = this.getMSC(temperatur.getTimeMeasured()); - if((dagNrTemperaturVerdi > dagNrFoersteSlaatt + 1 || dagNrTemperaturVerdi > dagNrAndreSlaatt) && Ts2 > 100) - { - FEm = this.getK(dagNrTemperaturVerdi > dagNrAndreSlaatt)*Ts2 + this.getM(dagNrTemperaturVerdi > dagNrAndreSlaatt); - //System.out.println(temperatur.getTimeMeasured() + ": this.getK()*Ts2 + this.getM() = " + this.getK() + " * " + Ts2 + " + " + this.getM() + " = " + FEm); - } - else - { - FEm = (this.getH()*MSC*MSC)-(this.getI()*MSC)+this.getJ(); - //System.out.println(temperatur.getTimeMeasured() + ": (this.getH()*MSC*MSC)-(this.getI()*MSC)+this.getJ()) = (" + this.getH() + "*" + MSC + "*" + MSC+ ")-(" + this.getI() + "*" + MSC + ")+" + this.getJ() + ") = " + FEm); - } + } else if (dagNrTemperaturVerdi == dagNrFoersteSlaatt + 1 || dagNrTemperaturVerdi == dagNrAndreSlaatt) { + FEm = 1.1; + } else { + double MSC = this.getMSC(temperatur.getTimeMeasured()); + if ((dagNrTemperaturVerdi > dagNrFoersteSlaatt + 1 || dagNrTemperaturVerdi > dagNrAndreSlaatt) && Ts2 > 100) { + FEm = this.getK(dagNrTemperaturVerdi > dagNrAndreSlaatt) * Ts2 + this.getM(dagNrTemperaturVerdi > dagNrAndreSlaatt); + //System.out.println(temperatur.getTimeMeasured() + ": this.getK()*Ts2 + this.getM() = " + this.getK() + " * " + Ts2 + " + " + this.getM() + " = " + FEm); + } else { + FEm = (this.getH() * MSC * MSC) - (this.getI() * MSC) + this.getJ(); + //System.out.println(temperatur.getTimeMeasured() + ": (this.getH()*MSC*MSC)-(this.getI()*MSC)+this.getJ()) = (" + this.getH() + "*" + MSC + "*" + MSC+ ")-(" + this.getI() + "*" + MSC + ")+" + this.getJ() + ") = " + FEm); } } this.FEmBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), FEmDataMatrix.FEM, FEm); // Setter data inn i resultatmatrisen - if(this.resultatMatrise != null) - { + if (this.resultatMatrise != null) { this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.FEM, FEm); } @@ -732,143 +667,123 @@ public class RoughageNutritionModelImpl implements CostFunction{ /** * Fast (ikke-optimert) konstant + * * @return faktor h, som brukes i beregning av FEM i vårvekst */ - private double getH() - { + private double getH() { return -0.0251; } - + /** * Fast (ikke-optimert) konstant + * * @return faktor i, som brukes i beregning av FEM i vårvekst */ - private double getI() - { + private double getI() { //return 0.0477; return 0.06; // 2010-03-04 endret ihht. e-post fra Anne Langerud } /** - * I 2009: Fast (ikke-optimert) konstant med verdi 1.2287; - * I 2010: Optimert konstant med default-verdi 1.2287f, og beskrankning 1,1574-1,3074. + * I 2009: Fast (ikke-optimert) konstant med verdi 1.2287; I 2010: Optimert + * konstant med default-verdi 1.2287f, og beskrankning 1,1574-1,3074. + * * @return faktor j, som brukes i beregning av FEM i vårvekst */ - private double getJ() - { + private double getJ() { return this.FEmJ != null ? this.FEmJ : 1.2287d; } /** - * Fast (ikke-optimert) konstant - * 2011-03-01: Endret metoden til å bli avhengig av verdien til MSC ved førsteslått, ihht. - * dokumentasjon av 28/2-2011 fra Anne Kjersti Bakken og Anne Langerud - * 2013-11-01: Endret metoden til at MSC-verdi ved førsteslått kun har betydning mellom - * første- og andreslått, ikke etter andreslått. - * @param etterAndreSlaatt Om dette er etter andreslått eller ikke + * Fast (ikke-optimert) konstant 2011-03-01: Endret metoden til å bli + * avhengig av verdien til MSC ved førsteslått, ihht. dokumentasjon av + * 28/2-2011 fra Anne Kjersti Bakken og Anne Langerud 2013-11-01: Endret + * metoden til at MSC-verdi ved førsteslått kun har betydning mellom første- + * og andreslått, ikke etter andreslått. + * + * @param etterAndreSlaatt Om dette er etter andreslått eller ikke * @return faktor k, som brukes i beregning av FEM i vårvekst */ - private double getK(boolean etterAndreSlaatt) throws ModelExcecutionException - { + private double getK(boolean etterAndreSlaatt) throws ModelExcecutionException { //return -0.0005; - return this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi && ! etterAndreSlaatt ? -0.0008f : -0.0006; + return this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI && !etterAndreSlaatt ? -0.0008f : -0.0006; } /** - * Fast (ikke-optimert) konstant - * 2011-03-01: Endret metoden til å bli avhengig av verdien til MSC ved førsteslått, ihht. - * dokumentasjon av 28/2-2011 fra Anne Kjersti Bakken og Anne Langerud<br/> - * 2013-11-01: Endret metoden til at MSC-verdi ved førsteslått kun har betydning mellom - * første- og andreslått, ikke etter andreslått. + * Fast (ikke-optimert) konstant 2011-03-01: Endret metoden til å bli + * avhengig av verdien til MSC ved førsteslått, ihht. dokumentasjon av + * 28/2-2011 fra Anne Kjersti Bakken og Anne Langerud<br/> + * 2013-11-01: Endret metoden til at MSC-verdi ved førsteslått kun har + * betydning mellom første- og andreslått, ikke etter andreslått. + * * @param MSCVedFoersteSlaatt vedien av MSC ved førsteslått * @return faktor m, som brukes i beregning av FEM i vårvekst */ - private double getM(boolean etterAndreSlaatt) throws ModelExcecutionException - { + private double getM(boolean etterAndreSlaatt) throws ModelExcecutionException { //return 1.0186; - double m = this.FEmM != null ? this.FEmM : this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi && ! etterAndreSlaatt ? 1.1177f : 1.0843; + double m = this.FEmM != null ? this.FEmM : this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI && !etterAndreSlaatt ? 1.1177f : 1.0843; //System.out.println("m=" + m); return m; } /** - * Løper gjennom eksisterende klimadatasett og beregner utvikling (MSC_BEREGNET) - * Dette trenger man bare å gjøre en gang pr. modellberegning + * Løper gjennom eksisterende klimadatasett og beregner utvikling + * (MSC_BEREGNET) Dette trenger man bare å gjøre en gang pr. modellberegning + * * @throws com.ac.march.ModelExcecutionException */ - private void beregnAIBakgrunnsdata() throws ModelExcecutionException - { + private void beregnAIBakgrunnsdata() throws ModelExcecutionException { Collections.sort((List) this.luftTemperaturVerdier); WeatherObservation temperatur; - if(this.AIBakgrunnsdataMatrise == null) + if (this.AIBakgrunnsdataMatrise == null) { this.AIBakgrunnsdataMatrise = new AIDataMatrix(); + } boolean first = true; Date forrigeDato = null; - for(Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator();tempI.hasNext();) - { + List<WeatherObservation> tempCopy = new ArrayList<>(this.luftTemperaturVerdier); + for (Iterator<WeatherObservation> tempI = tempCopy.iterator(); tempI.hasNext();) { temperatur = tempI.next(); //Setter Ts1 - if(first) - { + if (first) { this.AIBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), AIDataMatrix.TS1, temperatur.getValue()); - } - else - { + } else { this.AIBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), AIDataMatrix.TS1, temperatur.getValue() + this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, AIDataMatrix.TS1)); } // Beregner DVR double DVR; - if(temperatur.getTimeMeasured().compareTo(this.getVekstStart()) >= 0 && this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(temperatur.getTimeMeasured(), AIDataMatrix.TS1) > 150) - { + if (temperatur.getTimeMeasured().compareTo(this.getVekstStart()) >= 0 && this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(temperatur.getTimeMeasured(), AIDataMatrix.TS1) > 150) { DVR = Math.max(0, this.getAlfa(temperatur.getTimeMeasured()) * (temperatur.getValue() - this.getTb1())); - } - else - { + } else { DVR = 0; } this.AIBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), AIDataMatrix.DVR, DVR); // Beregner MSC_BEREGNET double MSC; - if(first) - { + if (first) { first = false; MSC = 1.35; - } - else - { - // Hvis dag == dag for første slått eller dag for andre slått: 1.35 - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) - { - MSC = 1.35; - } - else - { - if(temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) > 0) - { - MSC = 1.35; - } - else - { - MSC = Math.min(DVR + this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, AIDataMatrix.MSC_BEREGNET), 3.99999f); - } - } + } else // Hvis dag == dag for første slått eller dag for andre slått: 1.35 + if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) == 0 || temperatur.getTimeMeasured().compareTo(this.getDatoAndreSlaatt()) == 0) { + MSC = 1.35; + } else if (temperatur.getTimeMeasured().compareTo(this.getDatoFoersteSlaatt()) > 0) { + MSC = 1.35; + } else { + MSC = Math.min(DVR + this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, AIDataMatrix.MSC_BEREGNET), 3.99999f); } this.AIBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), AIDataMatrix.MSC_BEREGNET, MSC); // Lagrer MSC i resultatmatrisen - if(this.resultatMatrise != null) - { + if (this.resultatMatrise != null) { //this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.DVR, DVR); this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.MSC_BEREGNET, MSC); } - //this.AIBakgrunnsdataMatrise.printValuesForDate(temperatur.getTimeMeasured()); forrigeDato = temperatur.getTimeMeasured(); } @@ -877,13 +792,12 @@ public class RoughageNutritionModelImpl implements CostFunction{ /** * Brukes i beregning av NDF. Utledet ved beregning av AI (utviklingsindeks) + * * @param dato * @return */ - protected double getMSC(Date dato) throws ModelExcecutionException - { - if(this.AIBakgrunnsdataMatrise == null) - { + protected double getMSC(Date dato) throws ModelExcecutionException { + if (this.AIBakgrunnsdataMatrise == null) { this.beregnAIBakgrunnsdata(); //System.out.println("Resultatmatrise rett etter getMSC" + this.AIBakgrunnsdataMatrise.toString()); } @@ -891,8 +805,7 @@ public class RoughageNutritionModelImpl implements CostFunction{ return this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, AIDataMatrix.MSC_BEREGNET); } - protected double getMSCVedFoersteslaatt() throws ModelExcecutionException - { + protected double getMSCVedFoersteslaatt() throws ModelExcecutionException { // Beregn MSC ved førsteslått før optimering. // Vi må bruke dagen før førsteslått, hvis ikke er MSC feil Calendar cal = Calendar.getInstance(this.timeZone); @@ -903,24 +816,25 @@ public class RoughageNutritionModelImpl implements CostFunction{ /** * Tb1 er en konstant som brukes i beregningen av utvikling (MSC_BEREGNET) + * * @return verdien av konstanten Tb1 */ - private double getTb1() - { + private double getTb1() { return 0.002; } /** * Ak er en verdi som inngår i beregningen av AI-indeksen. + * * @param dato * @return */ - private double getAk(Date dato) - { - if(dato.before(this.getDatoFoersteSlaatt())) + private double getAk(Date dato) { + if (dato.before(this.getDatoFoersteSlaatt())) { return this.getAkFoersteSlaatt(); - else + } else { return this.getAkAndreSlaatt(); + } } private double getAkFoersteSlaatt() { @@ -930,53 +844,55 @@ public class RoughageNutritionModelImpl implements CostFunction{ private double getAkAndreSlaatt() { return 0.5; } + /** - * alfa er en optimert konstant som brukes til beregning av DVR, som igjen - * brukes til beregning av utvikling (MSC_BEREGNET), som i sin tur danner grunnlag for beregning av AI + * alfa er en optimert konstant som brukes til beregning av DVR, som igjen + * brukes til beregning av utvikling (MSC_BEREGNET), som i sin tur danner + * grunnlag for beregning av AI + * * @param dato * @return */ - private double getAlfa(Date dato) - { + private double getAlfa(Date dato) { return this.alfa != null ? this.alfa : 0.0028; } - - /** - * Cm er en konstant som brukes i beregning av avling - * I henhold til e-post fra Anne Langerud pr. 2010-02-26, så skal nå denne - * parameteren være = 35 fram til førsteslått, og deretter senkes til 30. - * I henhold til e-post fra Anne Langerud pr. 2011-02-28, så skal parameteren - * være 25 etter førsteslått + * Cm er en konstant som brukes i beregning av avling I henhold til e-post + * fra Anne Langerud pr. 2010-02-26, så skal nå denne parameteren være = 35 + * fram til førsteslått, og deretter senkes til 30. I henhold til e-post fra + * Anne Langerud pr. 2011-02-28, så skal parameteren være 25 etter + * førsteslått + * * @return */ - private double getCm(Date dato) - { - if(dato.compareTo(this.datoFoersteSlaatt) < 0) + private double getCm(Date dato) { + if (dato.compareTo(this.datoFoersteSlaatt) < 0) { return 35.0; - else + } else { return 25.0; + } } /** - * Rm er en konstant som brukes i beregning av avling. Er ulik i vårvekst (1. slått) og gjenvekst (2. slått) - * Er en av de optimerbare parametrene + * Rm er en konstant som brukes i beregning av avling. Er ulik i vårvekst + * (1. slått) og gjenvekst (2. slått) Er en av de optimerbare parametrene + * * @param dato * @return */ - private double getRm(Date dato) - { - if(dato.compareTo(this.datoFoersteSlaatt) < 0) + private double getRm(Date dato) { + if (dato.compareTo(this.datoFoersteSlaatt) < 0) { return this.getRmFoersteSlaatt(); - else + } else { return this.getRmAndreSlaatt(); + } } - /** - * Rm er en konstant som brukes i beregning av avling. Er ulik i vårvekst (1. slått) og gjenvekst (2. slått) - * Er en av de optimerbare parametrene + * Rm er en konstant som brukes i beregning av avling. Er ulik i vårvekst + * (1. slått) og gjenvekst (2. slått) Er en av de optimerbare parametrene + * * @return */ private double getRmFoersteSlaatt() { @@ -985,8 +901,9 @@ public class RoughageNutritionModelImpl implements CostFunction{ } /** - * Rm er en konstant som brukes i beregning av avling. Er ulik i vårvekst (1. slått) og gjenvekst (2. slått) - * Er en av de optimerbare parametrene + * Rm er en konstant som brukes i beregning av avling. Er ulik i vårvekst + * (1. slått) og gjenvekst (2. slått) Er en av de optimerbare parametrene + * * @return */ private double getRmAndreSlaatt() { @@ -994,139 +911,129 @@ public class RoughageNutritionModelImpl implements CostFunction{ //return 0.0510631743660617; // Testverdi som samsvarer med test-regnearket } - /** - * b er en konstant som brukes i beregning av avling. Er ulik i vårvekst (1. slått) og gjenvekst (2. slått) - * Er en fast parameter (skal ikke optimeres) + * b er en konstant som brukes i beregning av avling. Er ulik i vårvekst (1. + * slått) og gjenvekst (2. slått) Er en fast parameter (skal ikke optimeres) + * * @param dato * @return */ - private double getB(Date dato) - { - if(dato.compareTo(this.datoFoersteSlaatt) < 0) + private double getB(Date dato) { + if (dato.compareTo(this.datoFoersteSlaatt) < 0) { return this.getBFoersteSlaatt(); - else if(dato.compareTo(this.datoAndreSlaatt) < 0) + } else if (dato.compareTo(this.datoAndreSlaatt) < 0) { return this.getBMellomFoersteOgAndreSlaatt(); - else + } else { return this.getBAndreSlaatt(); + } } /** - * b er en konstant som brukes i beregning av avling. Er ulik før og etter vårvekst (1. slått) og etter gjenvekst (2. slått) - * Er en fast parameter (skal ikke optimeres) + * b er en konstant som brukes i beregning av avling. Er ulik før og etter + * vårvekst (1. slått) og etter gjenvekst (2. slått) Er en fast parameter + * (skal ikke optimeres) + * * @return */ - private double getBFoersteSlaatt() - { + private double getBFoersteSlaatt() { return 1.2; } - + /** - * b er en konstant som brukes i beregning av avling. Er ulik før og etter vårvekst (1. slått) og etter gjenvekst (2. slått) - * Er en fast parameter (skal ikke optimeres) + * b er en konstant som brukes i beregning av avling. Er ulik før og etter + * vårvekst (1. slått) og etter gjenvekst (2. slått) Er en fast parameter + * (skal ikke optimeres) + * * @return */ - - private double getBMellomFoersteOgAndreSlaatt() - { + private double getBMellomFoersteOgAndreSlaatt() { return 0.8; } /** - * b er en konstant som brukes i beregning av avling. Er ulik før og etter vårvekst (1. slått) og etter gjenvekst (2. slått) - * Er en fast parameter (skal ikke optimeres) - * 2012-04-12: Endret fra 0.8f til 0.5f etter henv. fra Anne Langerud pr. 2012-03-06 + * b er en konstant som brukes i beregning av avling. Er ulik før og etter + * vårvekst (1. slått) og etter gjenvekst (2. slått) Er en fast parameter + * (skal ikke optimeres) 2012-04-12: Endret fra 0.8f til 0.5f etter henv. + * fra Anne Langerud pr. 2012-03-06 + * * @return */ - private double getBAndreSlaatt() - { + private double getBAndreSlaatt() { return 0.5; } /** - * + * * @param dato * @param jordtype * @return * @throws com.ac.march.ModelExcecutionException */ - protected double getAvling(Date dato, int jordtype) throws ModelExcecutionException - { - if(this.avlingBakgrunnsdataMatrise == null) + protected double getAvling(Date dato, int jordtype) throws ModelExcecutionException { + if (this.avlingBakgrunnsdataMatrise == null) { this.beregnAvlingBakgrunnsdata(jordtype); + } return this.avlingBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, YieldDataMatrix.AVLING); } /** - * Løper gjennom eksisterende klimadatasett og beregner bakgrunnsdata for avling + * Løper gjennom eksisterende klimadatasett og beregner bakgrunnsdata for + * avling + * * @throws com.ac.march.ModelExcecutionException */ - private void beregnAvlingBakgrunnsdata(int jordtype) throws ModelExcecutionException - { + private void beregnAvlingBakgrunnsdata(int jordtype) throws ModelExcecutionException { Collections.sort((List) this.luftTemperaturVerdier); Collections.sort((List) this.globalStraalingVerdier); WeatherObservation temperatur; WeatherObservation straaling; - if(this.avlingBakgrunnsdataMatrise == null) + if (this.avlingBakgrunnsdataMatrise == null) { this.avlingBakgrunnsdataMatrise = new YieldDataMatrix(); + } boolean first = true; Date forrigeDato = null; Integer dagNummer = 1; + // Must make a shallow copy to avoid iterator change errors (concurrentModification) + List<WeatherObservation> tempCopy = new ArrayList<>(this.luftTemperaturVerdier); Iterator<WeatherObservation> straalingI = this.globalStraalingVerdier.iterator(); - for(Iterator<WeatherObservation> tempI = this.luftTemperaturVerdier.iterator();tempI.hasNext();) - { + for (Iterator<WeatherObservation> tempI = tempCopy.iterator(); tempI.hasNext();) { temperatur = tempI.next(); straaling = straalingI.next(); // Beregner Ts1 Double Ts1; - if(first) - { + if (first) { Ts1 = temperatur.getValue(); - } - else - { - if(temperatur.getTimeMeasured().compareTo(this.datoFoersteSlaatt) < 0 || temperatur.getTimeMeasured().compareTo(this.datoAndreSlaatt) == 0) - { - Ts1 = 0.0; - } - else - Ts1 = temperatur.getValue() + this.avlingBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, YieldDataMatrix.TS1); - //else - // Ts1 = 0; - } + } else if (temperatur.getTimeMeasured().compareTo(this.datoFoersteSlaatt) < 0 || temperatur.getTimeMeasured().compareTo(this.datoAndreSlaatt) == 0) { + Ts1 = 0.0; + } else { + Ts1 = temperatur.getValue() + this.avlingBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, YieldDataMatrix.TS1); + } //else + // Ts1 = 0; this.avlingBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), YieldDataMatrix.TS1, Ts1); - + // Beregner LAI Double LAI; Double gaarsdagensAvling = 0.0; - if(first) - { + if (first) { first = false; LAI = 0.0; - - } - else - { + + } else { gaarsdagensAvling = this.avlingBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, YieldDataMatrix.AVLING); - if(temperatur.getTimeMeasured().compareTo(this.datoFoersteSlaatt) < 0) - { - if(gaarsdagensAvling <= 300) - { + if (temperatur.getTimeMeasured().compareTo(this.datoFoersteSlaatt) < 0) { + if (gaarsdagensAvling <= 300) { LAI = gaarsdagensAvling / 75; + } else { + LAI = 4 + (gaarsdagensAvling - 300) / 200; } - else - { - LAI = 4+(gaarsdagensAvling-300)/200; - } - } - else - { + } else { double gaarsdagensTs1 = this.avlingBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, YieldDataMatrix.TS1); - if(gaarsdagensTs1 <= 350) + if (gaarsdagensTs1 <= 350) { LAI = 0.0114 * gaarsdagensTs1; - else - LAI = 4+(gaarsdagensAvling-300)/200; + } else { + LAI = 4 + (gaarsdagensAvling - 300) / 200; + } } } this.avlingBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), YieldDataMatrix.LAI, LAI); @@ -1136,14 +1043,13 @@ public class RoughageNutritionModelImpl implements CostFunction{ this.avlingBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), YieldDataMatrix.CPOT, Cpot); // Beregner v og Avling - Double v = Cpot * Math.min(this.getTI(temperatur.getTimeMeasured(),temperatur.getValue()), Math.min(this.getSI(temperatur.getTimeMeasured(),straaling.getValue()), this.getVI(temperatur.getTimeMeasured(), jordtype))) * this.getAI(temperatur.getTimeMeasured()); + Double v = Cpot * Math.min(this.getTI(temperatur.getTimeMeasured(), temperatur.getValue()), Math.min(this.getSI(temperatur.getTimeMeasured(), straaling.getValue()), this.getVI(temperatur.getTimeMeasured(), jordtype))) * this.getAI(temperatur.getTimeMeasured()); Double avling = temperatur.getTimeMeasured().compareTo(this.datoFoersteSlaatt) == 0 || temperatur.getTimeMeasured().compareTo(this.datoAndreSlaatt) == 0 ? 0f : gaarsdagensAvling + v; this.avlingBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), YieldDataMatrix.V, v); this.avlingBakgrunnsdataMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), YieldDataMatrix.AVLING, avling); // Setter data inn i resultatmatrisen, inkl. råprotein - if(this.resultatMatrise != null) - { + if (this.resultatMatrise != null) { //this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.V, v); this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.AVLING, avling); this.resultatMatrise.setParamDoubleValueForDate(temperatur.getTimeMeasured(), ResultMatrix.RAAPROTEIN, this.getRaaprotein(temperatur.getTimeMeasured(), jordtype)); @@ -1159,30 +1065,28 @@ public class RoughageNutritionModelImpl implements CostFunction{ * Løper gjennom eksisterende klimadatasett og beregner aktuell fordamping * Dette trenger man bare å gjøre en gang pr. modellberegning */ - private void beregnVIBakgrunnsdata(int jordtype) throws ModelExcecutionException - { + private void beregnVIBakgrunnsdata(int jordtype) throws ModelExcecutionException { Collections.sort((List) this.nedboerVerdier); Collections.sort((List) this.potensiellFordampingVerdier); WeatherObservation nedboer; WeatherObservation potensiellFordamping; - if(this.VIBakgrunnsdataMatrise == null) + if (this.VIBakgrunnsdataMatrise == null) { this.VIBakgrunnsdataMatrise = new VIDataMatrix(); - + } + boolean first = true; Iterator<WeatherObservation> pfI = this.potensiellFordampingVerdier.iterator(); Date forrigeDato = null; - for(Iterator<WeatherObservation> nedboerI = this.nedboerVerdier.iterator();nedboerI.hasNext();) - { + for (Iterator<WeatherObservation> nedboerI = this.nedboerVerdier.iterator(); nedboerI.hasNext();) { nedboer = nedboerI.next(); potensiellFordamping = pfI.next(); // Setter potensiell fordamping i matrisen, for enkelthets skyld //System.out.println("EPoriginal=" +potensiellFordamping.getValue()); - - this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.POTENSIELL_FORDAMPING, (double)potensiellFordamping.getValue()); + + this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.POTENSIELL_FORDAMPING, (double) potensiellFordamping.getValue()); // Første verdi er samme som potensiell fordamping - if(first) - { + if (first) { first = false; // Setter aktuell fordamping this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.AKTUELL_FORDAMPING, potensiellFordamping.getValue()); @@ -1190,42 +1094,31 @@ public class RoughageNutritionModelImpl implements CostFunction{ this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.LTV, this.getLTVStart(jordtype)); // Setter TTV this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.TTV, this.getTTVStart(jordtype)); - } - else - { + } else { // Setter aktuell fordamping Double aktuellFordamping; Double gaarsdagensVann = this.VIBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, VIDataMatrix.LTV) + this.VIBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, VIDataMatrix.TTV); Double gaarsdagensTTV = this.VIBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, VIDataMatrix.TTV); - if(gaarsdagensVann <= gaarsdagensTTV) - { + if (gaarsdagensVann <= gaarsdagensTTV) { aktuellFordamping = potensiellFordamping.getValue() * (gaarsdagensTTV / this.getTTVStart(jordtype)); - } - else - { + } else { aktuellFordamping = potensiellFordamping.getValue(); } this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.AKTUELL_FORDAMPING, aktuellFordamping); // Setter LTV Double LTV; - if(gaarsdagensTTV < this.getTTVStart(jordtype)) - { + if (gaarsdagensTTV < this.getTTVStart(jordtype)) { LTV = 0.0; - } - else - { + } else { Double gaarsdagensLTV = this.VIBakgrunnsdataMatrise.getParamDoubleValueForDate(forrigeDato, VIDataMatrix.LTV); LTV = Math.max(0, Math.min(gaarsdagensLTV - aktuellFordamping + nedboer.getValue(), this.getLTVStart(jordtype))); } this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.LTV, LTV); // Setter TTV Double TTV; - if(LTV > 0) - { + if (LTV > 0) { TTV = gaarsdagensTTV; - } - else - { + } else { TTV = Math.max(0, Math.min(gaarsdagensTTV - aktuellFordamping + nedboer.getValue(), this.getTTVStart(jordtype))); } this.VIBakgrunnsdataMatrise.setParamDoubleValueForDate(nedboer.getTimeMeasured(), VIDataMatrix.TTV, TTV); @@ -1238,84 +1131,74 @@ public class RoughageNutritionModelImpl implements CostFunction{ /** * Beregner AI-indeksen + * * @param dato * @return * @throws com.ac.march.ModelExcecutionException */ - protected double getAI(Date dato) throws ModelExcecutionException - { - if(this.AIBakgrunnsdataMatrise == null) + protected double getAI(Date dato) throws ModelExcecutionException { + if (this.AIBakgrunnsdataMatrise == null) { this.beregnAIBakgrunnsdata(); + } - if(this.AIBakgrunnsdataMatrise.getParamValueForDate(dato, AIDataMatrix.MSC_REGISTRERT) != null) - { + if (this.AIBakgrunnsdataMatrise.getParamValueForDate(dato, AIDataMatrix.MSC_REGISTRERT) != null) { return 1; - } - else - { - //System.out.println("DEBUG " + dato.toString() + " " + this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, AIDataMatrix.MSC_BEREGNET)); - if(this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, AIDataMatrix.MSC_BEREGNET) < 3f) - { - //System.out.println("Returnerer 1, rett og slett"); - return 1; - } - else - { - //System.out.println("Returnerer resultatet av et regnestykke. AK=" + this.getAk(dato)); - return Math.pow(1/(this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, AIDataMatrix.MSC_BEREGNET)/4)-1, this.getAk(dato)); - } + } else //System.out.println("DEBUG " + dato.toString() + " " + this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, AIDataMatrix.MSC_BEREGNET)); + if (this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, AIDataMatrix.MSC_BEREGNET) < 3f) { + //System.out.println("Returnerer 1, rett og slett"); + return 1; + } else { + //System.out.println("Returnerer resultatet av et regnestykke. AK=" + this.getAk(dato)); + return Math.pow(1 / (this.AIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, AIDataMatrix.MSC_BEREGNET) / 4) - 1, this.getAk(dato)); } } /** - * Beregner VI-indeksen. - * Forutsetninger: Vekststart er beregnet + * Beregner VI-indeksen. Forutsetninger: Vekststart er beregnet + * * @param dato * @param jordtype * @return */ - protected double getVI(Date dato, int jordtype) throws ModelExcecutionException - { - if(dato.compareTo(this.getVekstStart()) < 0) + protected double getVI(Date dato, int jordtype) throws ModelExcecutionException { + if (dato.compareTo(this.getVekstStart()) < 0) { return 0; - - if(this.VIBakgrunnsdataMatrise == null) + } + + if (this.VIBakgrunnsdataMatrise == null) { this.beregnVIBakgrunnsdata(jordtype); + } double potensiellFordamping = this.VIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, VIDataMatrix.POTENSIELL_FORDAMPING); - - if(potensiellFordamping == 0) + + if (potensiellFordamping == 0) { return 1; - else + } else { return this.VIBakgrunnsdataMatrise.getParamDoubleValueForDate(dato, VIDataMatrix.AKTUELL_FORDAMPING) / potensiellFordamping; + } } - /** * * @param temperatur for et døgn, i grader Celcius * @return beregnet indeks TI */ - protected double getTI(Date dato, double temperatur) throws ModelExcecutionException - { - if(dato.compareTo(this.getVekstStart()) < 0) + protected double getTI(Date dato, double temperatur) throws ModelExcecutionException { + if (dato.compareTo(this.getVekstStart()) < 0) { return 0d; + } int k_t = 2; int Tm_1 = 17; double T0_1 = 0.05; - if(Math.abs(temperatur - Tm_1) >= (Tm_1 - T0_1)) + if (Math.abs(temperatur - Tm_1) >= (Tm_1 - T0_1)) { return 0; - else - { + } else { double x = Math.abs(temperatur - Tm_1) / (Tm_1 - T0_1); - if(x < 0.5) - { - return 1-(Math.pow(2*x,k_t)/2); - } - else - { - return Math.pow(2*(1-x),k_t)/2; + if (x < 0.5) { + return 1 - (Math.pow(2 * x, k_t) / 2); + } else { + return Math.pow(2 * (1 - x), k_t) / 2; } } } @@ -1325,144 +1208,130 @@ public class RoughageNutritionModelImpl implements CostFunction{ * @param globalStraaling for et døgn, angitt i MJ/kvm/dag * @return beregnet indeks SI */ - protected double getSI(Date dato, double globalStraaling) throws ModelExcecutionException - { - if(dato.compareTo(this.getVekstStart()) < 0) + protected double getSI(Date dato, double globalStraaling) throws ModelExcecutionException { + if (dato.compareTo(this.getVekstStart()) < 0) { return 0d; + } int rk = 3; int r_mxp = 25; - return (1 - Math.exp((-rk * globalStraaling)/r_mxp)/(1-Math.exp(-rk))); + return (1 - Math.exp((-rk * globalStraaling) / r_mxp) / (1 - Math.exp(-rk))); } - /** - * - * @return - * @throws com.ac.march.ModelExcecutionException + * + * @return @throws com.ac.march.ModelExcecutionException */ - protected Date getVekstStart() throws ModelExcecutionException - { - if(this.calculatedDateOfGrowthStart == null) - { + protected Date getVekstStart() throws ModelExcecutionException { + if (this.calculatedDateOfGrowthStart == null) { this.calculatedDateOfGrowthStart = this.getVekstStart(this.luftTemperaturVerdier, this.jordTemperaturVerdier); } return this.calculatedDateOfGrowthStart; } /** - * Beregner datoen hvor vekststart antas å inntreffe. Vekststart antas å inntreffe - * etter tre påfølgende femdøgnsmiddel av temperatur > 5 grader Celcius. Dvs. datoen blir - * den tredje av disse påfølgende femdøgnsmiddel. - * Endring 2013-10-06: Hvis vi har jordtemperatur tilgjengelig, så må det samtidig - * ha vært i gjennomsnitt > 1 grader Celcius disse tre femdøgnsperiodene (siste 7 dager). + * Beregner datoen hvor vekststart antas å inntreffe. Vekststart antas å + * inntreffe etter tre påfølgende femdøgnsmiddel av temperatur > 5 grader + * Celcius. Dvs. datoen blir den tredje av disse påfølgende femdøgnsmiddel. + * Endring 2013-10-06: Hvis vi har jordtemperatur tilgjengelig, så må det + * samtidig ha vært i gjennomsnitt > 1 grader Celcius disse tre + * femdøgnsperiodene (siste 7 dager). * * @param dognMiddelLuftTemperatur - * @param jordTemperatur - * @return Dato for vekststart, eller null hvis ikke betingelsene for vekstart er oppfylt i de gitte klimadata + * @param jordTemperatur + * @return Dato for vekststart, eller null hvis ikke betingelsene for + * vekstart er oppfylt i de gitte klimadata */ - protected Date getVekstStart(Collection dognMiddelLuftTemperatur, Collection jordTemperatur) throws ModelExcecutionException - { + protected Date getVekstStart(Collection dognMiddelLuftTemperatur, Collection jordTemperatur) throws ModelExcecutionException { double lufttempTerskel = 5.0; double jordtempTerskel = 1.0; Collections.sort((List) dognMiddelLuftTemperatur); LinkedBlockingQueue<WeatherObservation> siste5DoegnVerdier = new LinkedBlockingQueue(5); - LinkedBlockingQueue<WeatherObservation> siste7DoegnVerdierJordtemp = (jordTemperatur != null && ! jordTemperatur.isEmpty()) ? new LinkedBlockingQueue(7) : null; + LinkedBlockingQueue<WeatherObservation> siste7DoegnVerdierJordtemp = (jordTemperatur != null && !jordTemperatur.isEmpty()) ? new LinkedBlockingQueue(7) : null; double sumSiste5DoegnVerdier = 0; boolean erAlleredeFull; int antallPaafoelgende5DoegnsMiddelOverTerskel = 0; - WeatherObservation eldsteDoegnverdi = null; + WeatherObservation eldsteDoegnverdi; WeatherObservation forrigeDoegnverdi = null; Calendar cal = Calendar.getInstance(this.timeZone); // Må ha minst sju jordtemperaturverdier Iterator<WeatherObservation> ji = (jordTemperatur != null && jordTemperatur.size() >= 7) ? jordTemperatur.iterator() : null; - for(Iterator<WeatherObservation> i= dognMiddelLuftTemperatur.iterator();i.hasNext();) - { + for (Iterator<WeatherObservation> i = dognMiddelLuftTemperatur.iterator(); i.hasNext();) { WeatherObservation doegnVerdi = i.next(); WeatherObservation doegnVerdiJordtemp = (ji != null && ji.hasNext() ? ji.next() : null); // Validering // Verdiens parameterkode må være TM eller TMf - if(! (doegnVerdi.getElementMeasurementTypeId().equals("TM") || doegnVerdi.getElementMeasurementTypeId().equals("TMf") || doegnVerdi.getElementMeasurementTypeId().equals("TM1,60"))) + if (!(doegnVerdi.getElementMeasurementTypeId().equals("TM") || doegnVerdi.getElementMeasurementTypeId().equals("TMf") || doegnVerdi.getElementMeasurementTypeId().equals("TM1,60"))) { throw new ModelExcecutionException("Feil parameterKode. Fant " + doegnVerdi.getElementMeasurementTypeId() + ", forventet TM, TMf eller TM1,60"); + } // Verdiens parameterkode må være TJM10 - if(doegnVerdiJordtemp != null && !(doegnVerdiJordtemp.getElementMeasurementTypeId().equals("TJM10"))) - { + if (doegnVerdiJordtemp != null && !(doegnVerdiJordtemp.getElementMeasurementTypeId().equals("TJM10"))) { throw new ModelExcecutionException("Feil parameterKode. Fant " + doegnVerdi.getElementMeasurementTypeId() + ", forventet TJM10"); } // Må være akkurat 1 døgn senere enn forrige verdi - if(forrigeDoegnverdi !=null) - { + if (forrigeDoegnverdi != null) { cal.setTime(forrigeDoegnverdi.getTimeMeasured()); cal.add(Calendar.DATE, 1); // Temperatures (air and optionally soil) must have the same timestamp - if(! cal.getTime().equals(doegnVerdi.getTimeMeasured()) || (doegnVerdiJordtemp != null && ! cal.getTime().equals(doegnVerdiJordtemp.getTimeMeasured()))) + if (!cal.getTime().equals(doegnVerdi.getTimeMeasured()) || (doegnVerdiJordtemp != null && !cal.getTime().equals(doegnVerdiJordtemp.getTimeMeasured()))) { throw new ModelExcecutionException("Manglende klimadata mellom " + forrigeDoegnverdi.getTimeMeasured().toString() + " og " + doegnVerdi.getTimeMeasured().toString()); + } } - sumSiste5DoegnVerdier += doegnVerdi.getValue(); // If first time filled or already full, we may start calculating - erAlleredeFull = ! siste5DoegnVerdier.offer(doegnVerdi); + erAlleredeFull = !siste5DoegnVerdier.offer(doegnVerdi); // Makes sure the last 3 soil temps are kept - if(doegnVerdiJordtemp != null) - { - if(!siste7DoegnVerdierJordtemp.offer(doegnVerdiJordtemp)) - { + if (doegnVerdiJordtemp != null) { + if (!siste7DoegnVerdierJordtemp.offer(doegnVerdiJordtemp)) { siste7DoegnVerdierJordtemp.poll(); siste7DoegnVerdierJordtemp.offer(doegnVerdiJordtemp); } } - + //System.out.println("[RoughageNutritionModel/getVekststart] siste5DoegnVerdier.remainingCapacity() = " + siste5DoegnVerdier.remainingCapacity()); - if(erAlleredeFull || siste5DoegnVerdier.remainingCapacity() == 0) - { + if (erAlleredeFull || siste5DoegnVerdier.remainingCapacity() == 0) { // First time we calculate average - if(!erAlleredeFull) - { - if(sumSiste5DoegnVerdier / 5 > lufttempTerskel) - antallPaafoelgende5DoegnsMiddelOverTerskel++; - else + if (!erAlleredeFull) { + if (sumSiste5DoegnVerdier / 5 > lufttempTerskel) { + antallPaafoelgende5DoegnsMiddelOverTerskel++; + } else { antallPaafoelgende5DoegnsMiddelOverTerskel = 0; - } - // Average's been calculated before, so we need to extract the oldest value - else - { + } + } // Average's been calculated before, so we need to extract the oldest value + else { // We need to extract the head of the queue, and add the most current value eldsteDoegnverdi = siste5DoegnVerdier.poll(); - if(!siste5DoegnVerdier.offer(doegnVerdi)) + if (!siste5DoegnVerdier.offer(doegnVerdi)) { throw new ModelExcecutionException("Siste5DoegnVerdier er full, feil i logikken"); + } // We then need to extract the value of the head from the sum sumSiste5DoegnVerdier -= eldsteDoegnverdi.getValue(); - if(sumSiste5DoegnVerdier / 5 > lufttempTerskel) - antallPaafoelgende5DoegnsMiddelOverTerskel++; - else + if (sumSiste5DoegnVerdier / 5 > lufttempTerskel) { + antallPaafoelgende5DoegnsMiddelOverTerskel++; + } else { antallPaafoelgende5DoegnsMiddelOverTerskel = 0; + } } //System.out.println("[RoughageNutritionModel/getVekststart] dato= " + doegnVerdi.getTimeMeasured().toString() + ", sumSiste5DoegnVerdier / 5 = " + (sumSiste5DoegnVerdier / 5)); - if(antallPaafoelgende5DoegnsMiddelOverTerskel == 3) - { + if (antallPaafoelgende5DoegnsMiddelOverTerskel == 3) { // If soil temps not provided, use only air temp - if(doegnVerdiJordtemp == null) - { + if (doegnVerdiJordtemp == null) { //System.out.println("Ingen jordtemp! Vekststart=" + doegnVerdi.getTimeMeasured()); return doegnVerdi.getTimeMeasured(); - } - else - { + } else { // Calculate mean of last 8 days (3 5-day periods, right?) double soilTempSum = 0; - for(WeatherObservation soilTemp : siste7DoegnVerdierJordtemp) - { + for (WeatherObservation soilTemp : siste7DoegnVerdierJordtemp) { soilTempSum += soilTemp.getValue(); } - if(soilTempSum/siste7DoegnVerdierJordtemp.size() > jordtempTerskel) - { + if (soilTempSum / siste7DoegnVerdierJordtemp.size() > jordtempTerskel) { //System.out.println("Jordtempsnitt=" + soilTempSum/siste8DoegnVerdierJordtemp.size()); return doegnVerdi.getTimeMeasured(); } } - + } } //System.out.println("[RoughageNutritionModel/getVekststart] "); @@ -1556,151 +1425,147 @@ public class RoughageNutritionModelImpl implements CostFunction{ } /** - * Sjekker at inputdata henger på greip og kan brukes til modellberegning<br/> + * Sjekker at inputdata henger på greip og kan brukes til + * modellberegning<br/> * Krav til data: * <ul> - * <li>Alle klimadata må være satt og sammenhengende, og det må være minst 10 verdier</li> + * <li>Alle klimadata må være satt og sammenhengende, og det må være minst + * 10 verdier</li> * <li>Klimadata må ikke spenne over årsskifte</li> - * <li>Datasett for ulike klimaparametre må ha samme start- og sluttdato</li> + * <li>Datasett for ulike klimaparametre må ha samme start- og + * sluttdato</li> * <li>Jordtype må være satt, og ha en lovlig verdi</li> * <li>Kløverandel i eng må være satt, og ha en lovlig verdi</li> * </ul> + * * @throws com.ac.march.ModelExcecutionException dersom validering feiler */ - private void validateInputData() throws ModelExcecutionException - { + private void validateInputData() throws ModelExcecutionException { // Alle klimadata (unntatt jordtemperatur) må være satt og sammenhengende // Minst 10 verdier // Må ikke spenne over årsskifte // Må ha samme start- og sluttdato Date foersteDato = null; Date sisteDato = null; - - try - { + + try { // Sorterer klimadata først - Collections.sort((List)this.luftTemperaturVerdier); - Collections.sort((List)this.nedboerVerdier); - Collections.sort((List)this.potensiellFordampingVerdier); - Collections.sort((List)this.globalStraalingVerdier); - + Collections.sort((List) this.luftTemperaturVerdier); + Collections.sort((List) this.nedboerVerdier); + Collections.sort((List) this.potensiellFordampingVerdier); + Collections.sort((List) this.globalStraalingVerdier); // Itererer samtlige samtidig Iterator<WeatherObservation> luftTempI = this.luftTemperaturVerdier.iterator(); Iterator<WeatherObservation> nedboerI = this.nedboerVerdier.iterator(); Iterator<WeatherObservation> potFordI = this.potensiellFordampingVerdier.iterator(); Iterator<WeatherObservation> straalingI = this.globalStraalingVerdier.iterator(); - + Iterator<WeatherObservation> jordTempI = null; - if(this.jordTemperaturVerdier != null && ! this.jordTemperaturVerdier.isEmpty()) - { - Collections.sort((List)this.jordTemperaturVerdier); + if (this.jordTemperaturVerdier != null && !this.jordTemperaturVerdier.isEmpty()) { + Collections.sort((List) this.jordTemperaturVerdier); jordTempI = this.jordTemperaturVerdier.iterator(); } - - + Date forrigeDato = null; boolean first = true; int counter = 0; Calendar cal = Calendar.getInstance(this.timeZone); - int dagensDagIAaret = -1; - for(;luftTempI.hasNext();) - { + int dagensDagIAaret; + for (; luftTempI.hasNext();) { WeatherObservation luftTemperatur = luftTempI.next(); WeatherObservation nedboer = nedboerI.next(); WeatherObservation potFord = potFordI.next(); WeatherObservation straaling = straalingI.next(); - WeatherObservation jordTemperatur = (jordTempI != null && jordTempI.hasNext()) ? jordTempI.next():null; + WeatherObservation jordTemperatur = (jordTempI != null && jordTempI.hasNext()) ? jordTempI.next() : null; Date dagensDato = luftTemperatur.getTimeMeasured(); - if(first) - { + if (first) { first = false; foersteDato = dagensDato; - } - else - { + } else { cal.setTime(dagensDato); dagensDagIAaret = cal.get(Calendar.DAY_OF_YEAR); cal.setTime(forrigeDato); - if(dagensDagIAaret - cal.get(Calendar.DAY_OF_YEAR) != 1) + if (dagensDagIAaret - cal.get(Calendar.DAY_OF_YEAR) != 1) { throw new ModelExcecutionException("Feil ved klimadata: Temperaturparameteren har hull i datasettet rundt " + dagensDato); + } } // Datoer må stemme overens. - if(dagensDato.compareTo(nedboer.getTimeMeasured()) != 0 || dagensDato.compareTo(potFord.getTimeMeasured()) != 0 || dagensDato.compareTo(straaling.getTimeMeasured()) !=0) - throw new ModelExcecutionException("Feil ved klimadata: Minst en av parametrene har hull i datasettet. Dette har applikasjonen kommet fram til ved å " + - "gå gjennom klimaparametrene en verdi av gangen og sammenlikne datoer. Den parameteren som har avvikende dato, og da fram i tid, er den " + - "som har hull i datasettet. <br/>" + - "<ul><li>Temperaturlinjas dato er " + dagensDato.toString() + "</li><li>Nedbørlinjas dato er " + nedboer.getTimeMeasured().toString() + - "</li><li>Potensiell fordampinglinjas dato er " + potFord.getTimeMeasured().toString() + "</li><li>Globalstrålingslinjas dato er " + straaling.getTimeMeasured().toString() + "</li></ul>"); - + if (dagensDato.compareTo(nedboer.getTimeMeasured()) != 0 || dagensDato.compareTo(potFord.getTimeMeasured()) != 0 || dagensDato.compareTo(straaling.getTimeMeasured()) != 0) { + throw new ModelExcecutionException("Feil ved klimadata: Minst en av parametrene har hull i datasettet. Dette har applikasjonen kommet fram til ved å " + + "gå gjennom klimaparametrene en verdi av gangen og sammenlikne datoer. Den parameteren som har avvikende dato, og da fram i tid, er den " + + "som har hull i datasettet. <br/>" + + "<ul><li>Temperaturlinjas dato er " + dagensDato.toString() + "</li><li>Nedbørlinjas dato er " + nedboer.getTimeMeasured().toString() + + "</li><li>Potensiell fordampinglinjas dato er " + potFord.getTimeMeasured().toString() + "</li><li>Globalstrålingslinjas dato er " + straaling.getTimeMeasured().toString() + "</li></ul>"); + } - if(jordTemperatur != null && dagensDato.compareTo(jordTemperatur.getTimeMeasured()) != 0) - { + if (jordTemperatur != null && dagensDato.compareTo(jordTemperatur.getTimeMeasured()) != 0) { throw new ModelExcecutionException("Feil ved klimadata: Jordemperatur har hull i datasettet, omkring dato " + dagensDato); } - + forrigeDato = dagensDato; counter++; } sisteDato = forrigeDato; - if(counter < 10 || foersteDato == null || sisteDato == null) + if (counter < 10 || foersteDato == null || sisteDato == null) { throw new ModelExcecutionException("Feil ved klimadata: For få klimadata."); + } // Sjekker om serien spenner over et årsskifte cal.setTime(foersteDato); int startAar = cal.get(Calendar.YEAR); cal.setTime(sisteDato); int sluttAar = cal.get(Calendar.YEAR); - if(sluttAar - startAar != 0) + if (sluttAar - startAar != 0) { throw new ModelExcecutionException("Feil ved klimadata: Spenner over et årsskifte."); + } - } - catch(NoSuchElementException nee) - { - throw new ModelExcecutionException( "Feil ved klimadata: Ulikt antall observasjoner for de ulike parametrene. " + - "Antall temperaturdata: " + this.luftTemperaturVerdier.size() + - ", antall nedbørdata: " + this.nedboerVerdier.size() + - ", antall fordampingsdata: " + this.potensiellFordampingVerdier.size() + - ", antall strålingsdata: " + this.globalStraalingVerdier.size()); - } - catch(NullPointerException npe) - { - npe.printStackTrace(); + } catch (NoSuchElementException nee) { + throw new ModelExcecutionException("Feil ved klimadata: Ulikt antall observasjoner for de ulike parametrene. " + + "Antall temperaturdata: " + this.luftTemperaturVerdier.size() + + ", antall nedbørdata: " + this.nedboerVerdier.size() + + ", antall fordampingsdata: " + this.potensiellFordampingVerdier.size() + + ", antall strålingsdata: " + this.globalStraalingVerdier.size()); + } catch (NullPointerException npe) { + //npe.printStackTrace(); throw new ModelExcecutionException("Data mangler."); } - SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy"); format.setTimeZone(timeZone); // Dato for første- og andreslått må være innenfor klimadataenes spenn - if(this.datoFoersteSlaatt == null) + if (this.datoFoersteSlaatt == null) { throw new ModelExcecutionException("Feil: Mangler dato for førsteslått"); - else if(this.datoFoersteSlaatt.compareTo(foersteDato) < 0 || this.datoFoersteSlaatt.compareTo(sisteDato) > 0) + } else if (this.datoFoersteSlaatt.compareTo(foersteDato) < 0 || this.datoFoersteSlaatt.compareTo(sisteDato) > 0) { throw new ModelExcecutionException("Feil: Dato for førsteslått(" + format.format(this.datoFoersteSlaatt) + ") er utenfor tidsperioden for klimadata(" + format.format(foersteDato) + "-" + format.format(sisteDato) + ")"); + } - if(this.datoAndreSlaatt != null && (this.datoAndreSlaatt.compareTo(foersteDato) < 0 || this.datoAndreSlaatt.compareTo(sisteDato) > 0 || this.datoAndreSlaatt.compareTo(this.datoFoersteSlaatt) < 0)) + if (this.datoAndreSlaatt != null && (this.datoAndreSlaatt.compareTo(foersteDato) < 0 || this.datoAndreSlaatt.compareTo(sisteDato) > 0 || this.datoAndreSlaatt.compareTo(this.datoFoersteSlaatt) < 0)) { throw new ModelExcecutionException("Feil: Dato for andreslått(" + format.format(this.datoAndreSlaatt) + ") er enten utenfor tidsperioden for klimadata(" + format.format(foersteDato) + "-" + format.format(sisteDato) + ") ELLER før dato for førsteslått(" + format.format(this.datoFoersteSlaatt) + ")"); + } // Sjekker jordtype og engsammensetning - if(this.jordtype != RoughageNutritionModel.JORDTYPE_LEIR && this.jordtype != RoughageNutritionModel.JORDTYPE_SAND && this.jordtype != RoughageNutritionModel.JORDTYPE_SILT) + if (this.jordtype != RoughageNutritionModel.JORDTYPE_LEIR && this.jordtype != RoughageNutritionModel.JORDTYPE_SAND && this.jordtype != RoughageNutritionModel.JORDTYPE_SILT) { throw new ModelExcecutionException("Feil: Ugyldig verdi for jordtype"); - if(this.kloeverandel != RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN && this.kloeverandel != RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM && this.kloeverandel != RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR) + } + if (this.kloeverandel != RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN && this.kloeverandel != RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM && this.kloeverandel != RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_STOR) { throw new ModelExcecutionException("Feil: Ugyldig verdi for kløverandel"); + } } /** * Sjekker at inputdata for optimering av parametre henger på greip og kan * brukes til modellberegning. + * * @throws com.ac.march.ModelExcecutionException dersom validering feiler */ - private void validateOptimizationInputData() throws ModelExcecutionException - { + private void validateOptimizationInputData() throws ModelExcecutionException { } @@ -1709,8 +1574,7 @@ public class RoughageNutritionModelImpl implements CostFunction{ * @return De definerte jordtypene, med id og navn */ public HashMap getTilgjengeligeJordtyper() { - if(this.tilgjengeligeJordtyper == null) - { + if (this.tilgjengeligeJordtyper == null) { this.tilgjengeligeJordtyper = new HashMap(); this.tilgjengeligeJordtyper.put(RoughageNutritionModel.JORDTYPE_LEIR, "Leire"); this.tilgjengeligeJordtyper.put(RoughageNutritionModel.JORDTYPE_SAND, "Sand"); @@ -1721,11 +1585,11 @@ public class RoughageNutritionModelImpl implements CostFunction{ /** * De definerte kløverandelene, med id og navn + * * @return */ public HashMap getTilgjengeligeKloeverandeler() { - if(this.tilgjengeligeKloeverandeler == null) - { + if (this.tilgjengeligeKloeverandeler == null) { this.tilgjengeligeKloeverandeler = new HashMap(); this.tilgjengeligeKloeverandeler.put(RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN, "Liten kløverandel (inntil 5%)"); this.tilgjengeligeKloeverandeler.put(RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_MEDIUM, "Middels kløverandel (5%-20%)"); @@ -1734,14 +1598,14 @@ public class RoughageNutritionModelImpl implements CostFunction{ return this.tilgjengeligeKloeverandeler; } - /** * Tar hånd om all optimeringen<br/> * Bruker Nelder-Mead-metoden for ikke-lineær optimering. - * @see http://commons.apache.org/math/api-1.2/org/apache/commons/math/optimization/NelderMead.html + * + * @see + * http://commons.apache.org/math/api-1.2/org/apache/commons/math/optimization/NelderMead.html */ - private void optimerParametre() throws CostException, ConvergenceException, ModelExcecutionException - { + private void optimerParametre() throws CostException, ConvergenceException, ModelExcecutionException { boolean DEBUG = false; // Bruker Nelder-Mead-metoden for ikke-lineær optimering. // Se http://commons.apache.org/math/api-1.2/org/apache/commons/math/optimization/NelderMead.html @@ -1753,24 +1617,23 @@ public class RoughageNutritionModelImpl implements CostFunction{ this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_UTVIKLING; /** - * Denne metoden forteller NelderMead-klassen om vi er "nær nok" til - * at vi kan avslutte optimeringen + * Denne metoden forteller NelderMead-klassen om vi er "nær nok" til at + * vi kan avslutte optimeringen */ - ConvergenceChecker cChecker = new ConvergenceChecker(){ + ConvergenceChecker cChecker = new ConvergenceChecker() { @Override public synchronized boolean converged(PointCostPair[] pair) { boolean DEBUG = false; // Prøver med 10% double differencePercentage = 0.1; - double min=0; - double max=0; - for(int i=0;i<pair.length;i++) - { - if(DEBUG) + double min = 0; + double max = 0; + for (int i = 0; i < pair.length; i++) { + if (DEBUG) { System.out.println("[converged] DEBUG Cost=" + pair[i].getCost()); - if(i==0) - { + } + if (i == 0) { min = pair[i].getCost(); max = min; } @@ -1781,18 +1644,17 @@ public class RoughageNutritionModelImpl implements CostFunction{ return (max - max * differencePercentage <= min); } - }; - // Rekkefølge for elementene: {alfa} // Max og min-verdier for de ulike elementene: // alfa {0.002-0.004} - double[][] verticesUtvikling = { {0.0025}, {0.0035} }; + double[][] verticesUtvikling = {{0.0025}, {0.0035}}; - PointCostPair optimumUtvikling = nelderMead.minimize(this, 1000, cChecker , verticesUtvikling); - if(DEBUG) + PointCostPair optimumUtvikling = nelderMead.minimize(this, 1000, cChecker, verticesUtvikling); + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: alfa=" + optimumUtvikling.getPoint()[0]); + } this.alfa = ((Double) optimumUtvikling.getPoint()[0]); //this.AIbeta = ((Double) optimumUtvikling.getPoint()[1]).doubleValue(); @@ -1804,15 +1666,16 @@ public class RoughageNutritionModelImpl implements CostFunction{ boolean avlingMaaltFoerFoersteslaatt = false; boolean avlingMaaltMellomFoersteOgAndreslaatt = false; for (OptimizationObservation observasjon : this.observasjoner) { - if(observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getAvling() > 0) + if (observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getAvling() != null && observasjon.getAvling() > 0) { avlingMaaltFoerFoersteslaatt = true; - if(observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getAvling() > 0) + } + if (observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getAvling() != null && observasjon.getAvling() > 0) { avlingMaaltMellomFoersteOgAndreslaatt = true; + } } // 2.1. Optimerer parametre for avling før førsteslått - if(avlingMaaltFoerFoersteslaatt) - { + if (avlingMaaltFoerFoersteslaatt) { this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_AVLING_FOERSTESLAATT; // Bruker (inntil det ikke funker lenger) samme ConvergenceChecker som for MSC @@ -1820,17 +1683,17 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Men: Skiller mellom 1. og andreslått // Max- og min-verdier for de ulike elementene: // Rm {0.05-0.12} - double[][] verticesAvlingFoersteslaatt = {{0.07},{0.10}}; + double[][] verticesAvlingFoersteslaatt = {{0.07}, {0.10}}; PointCostPair optimumAvlingFoersteslaatt = nelderMead.minimize(this, 1000, cChecker, verticesAvlingFoersteslaatt); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: RmFoersteslaatt=" + optimumAvlingFoersteslaatt.getPoint()[0]); + } - this.RmFoersteslaatt = ((Double)optimumAvlingFoersteslaatt.getPoint()[0]).doubleValue(); + this.RmFoersteslaatt = ((Double) optimumAvlingFoersteslaatt.getPoint()[0]); } // 2.2. Optimerer parametre for avling mellom førsteslått og andreslått - if(avlingMaaltMellomFoersteOgAndreslaatt) - { + if (avlingMaaltMellomFoersteOgAndreslaatt) { this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_AVLING_ANDRESLAATT; // Bruker (inntil det ikke funker lenger) samme ConvergenceChecker som for MSC @@ -1838,13 +1701,14 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Men: Skiller mellom 1. og andreslått // Max- og min-verdier for de ulike elementene: // Rm {0.05-0.09} - double[][] verticesAvlingAndreslaatt = {{0.06},{0.08}}; + double[][] verticesAvlingAndreslaatt = {{0.06}, {0.08}}; PointCostPair optimumAvlingAndreslaatt = nelderMead.minimize(this, 1000, cChecker, verticesAvlingAndreslaatt); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: RmAndreslaatt=" + optimumAvlingAndreslaatt.getPoint()[0]); + } - this.RmAndreslaatt = ((Double)optimumAvlingAndreslaatt.getPoint()[0]); + this.RmAndreslaatt = ((Double) optimumAvlingAndreslaatt.getPoint()[0]); } // Nullstiller avlingsmatrisen, slik at andre beregninger går som planlagt @@ -1854,15 +1718,16 @@ public class RoughageNutritionModelImpl implements CostFunction{ boolean NDFMaaltFoerFoersteslaatt = false; boolean NDFMaaltMellomFoersteOgAndreslaatt = false; for (OptimizationObservation observasjon : this.observasjoner) { - if(observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getNDF() > 0) + if (observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getNDF() != null && observasjon.getNDF() > 0) { NDFMaaltFoerFoersteslaatt = true; - if(observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getNDF() > 0) + } + if (observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getNDF() != null && observasjon.getNDF() > 0) { NDFMaaltMellomFoersteOgAndreslaatt = true; + } } // 3.1 Optimerer parametre for NDF for førsteslått (vårvekst) - if(NDFMaaltFoerFoersteslaatt) - { + if (NDFMaaltFoerFoersteslaatt) { DEBUG = false; this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_NDF_FOERSTESLAATT; // Bruker (inntil det ikke funker lenger) samme ConvergenceChecker som for MSC @@ -1871,16 +1736,16 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Max- og min-verdier for de ulike elementene: // NRm {0.05-0.10}, c {1.0-1.4} - double[][] verticesNDFFoersteslaatt = {{0.06, 1.05},{0.09,1.05},{0.06,1.35}}; + double[][] verticesNDFFoersteslaatt = {{0.06, 1.05}, {0.09, 1.05}, {0.06, 1.35}}; PointCostPair optimumNDFFoersteslaatt = nelderMead.minimize(this, 1000, cChecker, verticesNDFFoersteslaatt); - if(DEBUG) - System.out.println("[GrovforModell/optimerParametre] DEBUG: NRmFoersteslaatt=" + optimumNDFFoersteslaatt.getPoint()[0] + ", c=" + optimumNDFFoersteslaatt.getPoint()[1]); - this.NRmFoersteslaatt = ((Double)optimumNDFFoersteslaatt.getPoint()[0]); - this.cFoersteslaatt = ((Double)optimumNDFFoersteslaatt.getPoint()[1]); + if (DEBUG) { + System.out.println("[GrovforModell/optimerParametre] DEBUG: NRmFoersteslaatt=" + optimumNDFFoersteslaatt.getPoint()[0] + ", c=" + optimumNDFFoersteslaatt.getPoint()[1]); + } + this.NRmFoersteslaatt = ((Double) optimumNDFFoersteslaatt.getPoint()[0]); + this.cFoersteslaatt = ((Double) optimumNDFFoersteslaatt.getPoint()[1]); } - if(NDFMaaltMellomFoersteOgAndreslaatt) - { + if (NDFMaaltMellomFoersteOgAndreslaatt) { // 3.2 Optimerer parametre for NDF for perioden mellom førsteslått og andreslått (gjenvekst) this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_NDF_ANDRESLAATT; // Bruker (inntil det ikke funker lenger) samme ConvergenceChecker som for MSC @@ -1888,13 +1753,14 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Men: Skiller mellom 1. og andreslått // Max- og min-verdier for de ulike elementene: // NRm {0.35-0.45} - double[][] verticesNDFAndreslaatt = {{0.36},{0.44}}; + double[][] verticesNDFAndreslaatt = {{0.36}, {0.44}}; PointCostPair optimumNDFAndreslaatt = nelderMead.minimize(this, 1000, cChecker, verticesNDFAndreslaatt); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: NRmAndreslaatt=" + optimumNDFAndreslaatt.getPoint()[0]); + } - this.NRmAndreslaatt = ((Double)optimumNDFAndreslaatt.getPoint()[0]); + this.NRmAndreslaatt = ((Double) optimumNDFAndreslaatt.getPoint()[0]); DEBUG = false; } // Nullstiller avlingsmatrisen, slik at andre beregninger går som planlagt @@ -1904,29 +1770,30 @@ public class RoughageNutritionModelImpl implements CostFunction{ boolean INDFMaaltFoerFoersteslaatt = false; boolean INDFMaaltMellomFoersteOgAndreslaatt = false; for (OptimizationObservation observasjon : this.observasjoner) { - if(observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getINDF() > 0) + if (observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getINDF() != null && observasjon.getINDF() > 0) { INDFMaaltFoerFoersteslaatt = true; - if(observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getINDF() > 0) + } + if (observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getINDF() != null && observasjon.getINDF() > 0) { INDFMaaltMellomFoersteOgAndreslaatt = true; + } } - if(INDFMaaltFoerFoersteslaatt) - { + if (INDFMaaltFoerFoersteslaatt) { // 4.1 Optimerer parametre for INDF for førsteslått this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_INDF_FOERSTESLAATT; // Bruker (inntil det ikke funker lenger) samme ConvergenceChecker som for MSC // Rekkefølge for elementene: {IRm} (ja, bare ett element!) // Max- og min-verdier for de ulike elementene: // IRm {0.004-0.01} - double[][] verticesINDFFoersteslaatt = {{0.005},{0.009}}; + double[][] verticesINDFFoersteslaatt = {{0.005}, {0.009}}; PointCostPair optimumINDFFoersteslaatt = nelderMead.minimize(this, 1000, cChecker, verticesINDFFoersteslaatt); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: IRmFoersteslaatt=" + optimumINDFFoersteslaatt.getPoint()[0]); + } - this.IRmFoersteslaatt = ((Double)optimumINDFFoersteslaatt.getPoint()[0]); + this.IRmFoersteslaatt = ((Double) optimumINDFFoersteslaatt.getPoint()[0]); } - if(INDFMaaltMellomFoersteOgAndreslaatt) - { + if (INDFMaaltMellomFoersteOgAndreslaatt) { DEBUG = false; // 4.1 Optimerer parametre for INDF for andreslått this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_INDF_ANDRESLAATT; @@ -1936,59 +1803,58 @@ public class RoughageNutritionModelImpl implements CostFunction{ // beta {0.0001-0.0002} // Fases ut før sesongen 2011 // P: Når MSC ved førsteslått < 2.4: {-0.0272,-0.006}, ellers {-0.0299,-0.0087} //double[][] verticesINDFAndreslaatt = {{0.00011},{0.00019}}; // Gjaldt for beta - double pMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? -0.0272 : -0.0299; - double pMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? -0.006 : -0.0087; + double pMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? -0.0272 : -0.0299; + double pMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? -0.006 : -0.0087; - if(DEBUG) - { + if (DEBUG) { System.out.println("this.MSCVedFoersteslaatt=" + this.getMSCVedFoersteslaatt()); System.out.println("pMin=" + pMin); System.out.println("pMax=" + pMax); } - double[][] verticesINDFAndreslaatt = {{pMin + 0.00001},{pMax - 0.0001}}; + double[][] verticesINDFAndreslaatt = {{pMin + 0.00001}, {pMax - 0.0001}}; //double[][] verticesINDFAndreslaatt = {{pMax - 0.0001},{pMin + 0.00001}}; PointCostPair optimumINDFAndreslaatt = nelderMead.minimize(this, 1000, cChecker, verticesINDFAndreslaatt); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: pINDF=" + optimumINDFAndreslaatt.getPoint()[0]); + } DEBUG = false; - this.pINDF = ((Double)optimumINDFAndreslaatt.getPoint()[0]); + this.pINDF = ((Double) optimumINDFAndreslaatt.getPoint()[0]); - } - - // 5. Optimerer parametre for FEm (Fôrenhetskonsentrasjonen (konsentrasjonen av energi i tørrstoffet, + + // 5. Optimerer parametre for FEm (Fôrenhetskonsentrasjonen (konsentrasjonen av energi i tørrstoffet, // uttrykt som FEm pr. kg tørrstoff boolean FEmMaaltFoerFoersteslaatt = false; boolean FEmMaaltMellomFoersteOgAndreslaatt = false; - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { OptimizationObservation observasjon = i.next(); - if(observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getFEm() > 0) + if (observasjon.getDate().before(this.datoFoersteSlaatt) && observasjon.getFEm() != null && observasjon.getFEm() > 0) { FEmMaaltFoerFoersteslaatt = true; - if(observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getFEm() > 0) + } + if (observasjon.getDate().after(this.datoFoersteSlaatt) && observasjon.getDate().before(this.datoAndreSlaatt) && observasjon.getFEm() != null && observasjon.getFEm() > 0) { FEmMaaltMellomFoersteOgAndreslaatt = true; + } } - if(FEmMaaltFoerFoersteslaatt) - { + if (FEmMaaltFoerFoersteslaatt) { DEBUG = false; this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_FEM_FOERSTESLAATT; // Bruker (inntil det ikke funker lenger) samme ConvergenceChecker som for MSC // Rekkefølge for elementene: {j} (ja, bare ett element!) // Max- og min-verdier for de ulike elementene: // j {1.1574-1.3074} - double[][] verticesFEmFoersteslaatt = {{1.1575},{1.3073}}; + double[][] verticesFEmFoersteslaatt = {{1.1575}, {1.3073}}; PointCostPair optimumFEmFoersteslaatt = nelderMead.minimize(this, 1000, cChecker, verticesFEmFoersteslaatt); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: FEmJFoersteslaatt=" + optimumFEmFoersteslaatt.getPoint()[0]); + } + + this.FEmJ = ((Double) optimumFEmFoersteslaatt.getPoint()[0]); - this.FEmJ = ((Double)optimumFEmFoersteslaatt.getPoint()[0]); - } - if(FEmMaaltMellomFoersteOgAndreslaatt) - { - DEBUG = true; + if (FEmMaaltMellomFoersteOgAndreslaatt) { + //DEBUG = true; this.optimeringsMaal = RoughageNutritionModelImpl.OPTIMERING_FEM_ANDRESLAATT; // Bruker (inntil det ikke funker lenger) samme ConvergenceChecker som for MSC // Rekkefølge for elementene: {j} (ja, bare ett element!) @@ -1997,15 +1863,16 @@ public class RoughageNutritionModelImpl implements CostFunction{ // m {1.08-1.16} // Ellers: // m {1.05-1.112} - double mMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? 1.08 : 1.05 ; - double mMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? 1.16 : 1.112 ; - double[][] verticesFEmAndreslaatt = {{mMin + 0.00001},{mMax - 0.0001}}; + double mMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? 1.08 : 1.05; + double mMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? 1.16 : 1.112; + double[][] verticesFEmAndreslaatt = {{mMin + 0.00001}, {mMax - 0.0001}}; PointCostPair optimumFEmAndreslaatt = nelderMead.minimize(this, 1000, cChecker, verticesFEmAndreslaatt); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/optimerParametre] DEBUG: FEmMAndreslaatt=" + optimumFEmAndreslaatt.getPoint()[0]); + } - this.FEmM = ((Double)optimumFEmAndreslaatt.getPoint()[0]); + this.FEmM = ((Double) optimumFEmAndreslaatt.getPoint()[0]); } // Nullstiller alle bakgrunnsdatamatriser, slik at øvrige beregninger går som planlagt @@ -2018,7 +1885,9 @@ public class RoughageNutritionModelImpl implements CostFunction{ } /** - * Denne brukes til å optimere alle de ulike parametrene. Husk å sette hva som skal optimeres først! + * Denne brukes til å optimere alle de ulike parametrene. Husk å sette hva + * som skal optimeres først! + * * @param arg0 * @return * @throws org.apache.commons.math.optimization.CostException @@ -2027,46 +1896,41 @@ public class RoughageNutritionModelImpl implements CostFunction{ public double cost(double[] parametre) throws CostException { boolean DEBUG = false; double grenseKost = 1000000000; - if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_UTVIKLING) - { + if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_UTVIKLING) { - if(DEBUG) + if (DEBUG) { System.out.println("Estimerer utvikling"); + } // Rekkefølge for elementene: {alfa} double alfa1 = parametre[0]; - // Max og min-verdier for de ulike elementene: // alfa {0.002-0.004} double alfaMin = 0.002; double alfaMax = 0.004; - // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(alfa1 < alfaMin || alfa1 > alfaMax) - { + if (alfa1 < alfaMin || alfa1 > alfaMax) { return grenseKost; - } - else - { + } else { // Vi setter de parametrene som er gitt, og beregner utvikling this.alfa = (double) alfa1; // Starter beregningen med blanke ark... this.AIBakgrunnsdataMatrise = null; double differanse = 0; - double maaltUtviklingstrinn; + Double maaltUtviklingstrinn; double beregnetUtviklingstrinn; - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); maaltUtviklingstrinn = observasjon.getMSC(); // Tar høyde for at observasjonen ikke inneholder måling av utviklingstrinn - if(maaltUtviklingstrinn < 0) + if (maaltUtviklingstrinn == null) { continue; + } beregnetUtviklingstrinn = this.getMSC(observasjon.getDate()); } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); @@ -2076,12 +1940,11 @@ public class RoughageNutritionModelImpl implements CostFunction{ } return differanse; } - - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_AVLING_FOERSTESLAATT) - { - if(DEBUG) + + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_AVLING_FOERSTESLAATT) { + if (DEBUG) { System.out.println("Estimerer avling førsteslått"); + } // Rekkefølge for elementene: {Rm} (ja, bare ett element!) double Rm = parametre[0]; @@ -2093,12 +1956,9 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(Rm < RmMin || Rm > RmMax) - { + if (Rm < RmMin || Rm > RmMax) { return grenseKost; - } - else - { + } else { // Vi setter de parametrene som er gitt, og beregner avling // Bruker bare de målingene som er før 1. slått this.RmFoersteslaatt = (double) Rm; @@ -2109,17 +1969,18 @@ public class RoughageNutritionModelImpl implements CostFunction{ double maaltAvling; double beregnetAvling; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er før 1. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) >= 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) >= 0) { break; + } maaltAvling = observasjon.getAvling(); // Tar høyde for at observasjonen ikke inneholder avlingsmåling - if(maaltAvling < 0) + if (maaltAvling < 0) { continue; + } beregnetAvling = this.getAvling(observasjon.getDate(), this.jordtype); } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); @@ -2132,11 +1993,10 @@ public class RoughageNutritionModelImpl implements CostFunction{ return differanse; } - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_AVLING_ANDRESLAATT) - { - if(DEBUG) + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_AVLING_ANDRESLAATT) { + if (DEBUG) { System.out.println("Estimerer avling andreslått"); + } // Rekkefølge for elementene: {Rm} (ja, bare ett element!) double Rm = parametre[0]; @@ -2148,12 +2008,9 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(Rm < RmMin || Rm > RmMax) - { + if (Rm < RmMin || Rm > RmMax) { return grenseKost; - } - else - { + } else { // Vi setter de parametrene som er gitt, og beregner avling // Bruker bare de målingene som er mellom 1. slått og 2. slått this.RmAndreslaatt = (double) Rm; @@ -2164,36 +2021,37 @@ public class RoughageNutritionModelImpl implements CostFunction{ double maaltAvling; double beregnetAvling; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er mellom 1. slått og 2. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) { continue; - if(observasjon.getDate().compareTo(this.datoAndreSlaatt) > 0) + } + if (observasjon.getDate().compareTo(this.datoAndreSlaatt) > 0) { break; + } maaltAvling = observasjon.getAvling(); // Tar høyde for at observasjonen ikke inneholder avlingsmåling - if(maaltAvling < 0) + if (maaltAvling < 0) { continue; + } beregnetAvling = this.getAvling(observasjon.getDate(), this.jordtype); } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); throw new CostException(ex); } - + // Kvadrerer for å droppe problematikk med negative tall differanse += Math.pow(maaltAvling - beregnetAvling, 2); } return differanse; } - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_NDF_FOERSTESLAATT) - { - if(DEBUG) + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_NDF_FOERSTESLAATT) { + if (DEBUG) { System.out.println("Estimerer NDF førsteslått"); + } // Rekkefølge for elementene: {NRm,c} // Men: Skiller mellom 1. og andreslått @@ -2209,12 +2067,9 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(NRm < NRmMin || NRm > NRmMax || c < cMin || c > cMax) - { + if (NRm < NRmMin || NRm > NRmMax || c < cMin || c > cMax) { return grenseKost; - } - else - { + } else { this.NRmFoersteslaatt = (double) NRm; this.cFoersteslaatt = (double) c; // Starter med blanke ark... @@ -2224,17 +2079,18 @@ public class RoughageNutritionModelImpl implements CostFunction{ double beregnetNDF; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er før 1. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) > 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) > 0) { break; + } maaltNDF = observasjon.getNDF(); // Tar høyde for at observasjonen ikke inneholder NDF-måling - if(maaltNDF < 0) + if (maaltNDF < 0) { continue; + } beregnetNDF = this.getNDF(observasjon.getDate(), this.kloeverandel, this.jordtype); } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); @@ -2249,11 +2105,10 @@ public class RoughageNutritionModelImpl implements CostFunction{ } - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_NDF_ANDRESLAATT) - { - if(DEBUG) + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_NDF_ANDRESLAATT) { + if (DEBUG) { System.out.println("Estimerer NDF andreslått"); + } // Rekkefølge for elementene: {NRm} (Ja, bare ett element) double NRm = parametre[0]; @@ -2265,12 +2120,9 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(NRm < NRmMin || NRm > NRmMax) - { + if (NRm < NRmMin || NRm > NRmMax) { return grenseKost; - } - else - { + } else { this.NRmAndreslaatt = (double) NRm; // Starter med blanke ark... this.NDFBakgrunnsdataMatrise = null; @@ -2279,19 +2131,21 @@ public class RoughageNutritionModelImpl implements CostFunction{ double beregnetNDF; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er mellom 1. slått og 2. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) { continue; - if(observasjon.getDate().compareTo(this.datoAndreSlaatt) > 0) + } + if (observasjon.getDate().compareTo(this.datoAndreSlaatt) > 0) { break; + } maaltNDF = observasjon.getNDF(); // Tar høyde for at observasjonen ikke inneholder NDF-måling - if(maaltNDF < 0) + if (maaltNDF < 0) { continue; + } beregnetNDF = this.getNDF(observasjon.getDate(), this.kloeverandel, this.jordtype); } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); @@ -2305,11 +2159,10 @@ public class RoughageNutritionModelImpl implements CostFunction{ return differanse; } - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_INDF_FOERSTESLAATT) - { - if(DEBUG) + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_INDF_FOERSTESLAATT) { + if (DEBUG) { System.out.println("Estimerer INDF førsteslått, NelderMead tester med IRm=" + parametre[0]); + } // Rekkefølge for elementene: {IRm} double IRm = parametre[0]; @@ -2317,15 +2170,12 @@ public class RoughageNutritionModelImpl implements CostFunction{ // IRm {0.004-0.01} double IRmMin = 0.004; double IRmMax = 0.01; - + // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(IRm < IRmMin || IRm > IRmMax) - { + if (IRm < IRmMin || IRm > IRmMax) { return grenseKost; - } - else - { + } else { this.IRmFoersteslaatt = (double) IRm; // Starter med blanke ark... this.INDFBakgrunnsdataMatrise = null; @@ -2334,17 +2184,18 @@ public class RoughageNutritionModelImpl implements CostFunction{ double beregnetINDF; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er før 1. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) > 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) > 0) { break; + } maaltINDF = observasjon.getINDF(); // Tar høyde for at observasjonen ikke inneholder INDF-måling - if(maaltINDF < 0) + if (maaltINDF < 0) { continue; + } beregnetINDF = this.getINDF(observasjon.getDate()); } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); @@ -2358,16 +2209,13 @@ public class RoughageNutritionModelImpl implements CostFunction{ return differanse; } - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_INDF_ANDRESLAATT) - { - if(DEBUG) + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_INDF_ANDRESLAATT) { + if (DEBUG) { System.out.println("Estimerer INDF andreslått"); + } // Rekkefølge for elementene: {beta} //double beta = parametre[0]; - - // Rekkefølge for elementene: {P} double p = parametre[0]; @@ -2375,21 +2223,16 @@ public class RoughageNutritionModelImpl implements CostFunction{ // beta {0.0001-0.0002} //double betaMin = 0.0001; //double betaMax = 0.0002; - // Max- og min-verdier for de ulike elementene: - try - { - double pMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? -0.0272 : -0.0299; - double pMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? -0.006 : -0.0087; - + try { + double pMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? -0.0272 : -0.0299; + double pMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? -0.006 : -0.0087; + // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(p < pMin || p > pMax) - { + if (p < pMin || p > pMax) { return grenseKost; - } - else - { + } else { //this.betaINDF = (double) beta; this.pINDF = (double) p; // Starter med blanke ark... @@ -2399,19 +2242,21 @@ public class RoughageNutritionModelImpl implements CostFunction{ double beregnetINDF; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er mellom 1. slått og 2. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) { continue; - if(observasjon.getDate().compareTo(this.datoAndreSlaatt) > 0) + } + if (observasjon.getDate().compareTo(this.datoAndreSlaatt) > 0) { break; + } maaltINDF = observasjon.getINDF(); // Tar høyde for at observasjonen ikke inneholder INDF-måling - if(maaltINDF < 0) + if (maaltINDF < 0) { continue; + } beregnetINDF = this.getINDF(observasjon.getDate()); } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); @@ -2425,13 +2270,13 @@ public class RoughageNutritionModelImpl implements CostFunction{ return differanse; } + } catch (ModelExcecutionException me) { + me.printStackTrace(); } - catch(ModelExcecutionException me) {me.printStackTrace();} - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_FEM_FOERSTESLAATT) - { - if(DEBUG) + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_FEM_FOERSTESLAATT) { + if (DEBUG) { System.out.println("Estimerer FEm førsteslått"); + } // Rekkefølge for elementene {FEmj} (Ja, bare ett element) double FEmjTemp = parametre[0]; @@ -2441,12 +2286,9 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(FEmjTemp < FEmjTempMin || FEmjTemp > FEmjTempMax) - { + if (FEmjTemp < FEmjTempMin || FEmjTemp > FEmjTempMax) { return grenseKost; - } - else - { + } else { this.FEmJ = (double) FEmjTemp; // Starter med blanke ark... this.FEmBakgrunnsdataMatrise = null; @@ -2455,19 +2297,20 @@ public class RoughageNutritionModelImpl implements CostFunction{ double beregnetFEm; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er før 1. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) > 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) > 0) { break; + } maaltFEm = observasjon.getFEm(); // Tar høyde for at observasjonen ikke inneholder FEm-måling - if(maaltFEm < 0) + if (maaltFEm < 0) { continue; + } beregnetFEm = this.getFEm(observasjon.getDate()); - }catch (ModelExcecutionException ex) { + } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); throw new CostException(ex); } @@ -2477,30 +2320,25 @@ public class RoughageNutritionModelImpl implements CostFunction{ } return differanse; } - } - else if(this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_FEM_ANDRESLAATT) - { - if(DEBUG) + } else if (this.optimeringsMaal == RoughageNutritionModelImpl.OPTIMERING_FEM_ANDRESLAATT) { + if (DEBUG) { System.out.println("Estimerer FEm andreslått"); + } // Rekkefølge for elementene {FEmj} (Ja, bare ett element) double FEmMTemp = parametre[0]; //Max- og min-verdier for de ulike elementene: //double FEmjTempMin = 1.1574; //double FEmjTempMax = 1.3074; - try - { - double FEmMTempMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? 1.08 : 1.05; - double FEmMTempMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSCVedFoersteslaattGrenseverdi ? 1.16 : 1.112; + try { + double FEmMTempMin = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? 1.08 : 1.05; + double FEmMTempMax = this.getMSCVedFoersteslaatt() < RoughageNutritionModelImpl.MSC_VED_FOERSTESLAATT_GRENSEVERDI ? 1.16 : 1.112; // Hvis utenfor min/max: returner en stor verdi. Da opptrer cost-funksjonen // som om den har grenser - if(FEmMTemp < FEmMTempMin || FEmMTemp > FEmMTempMax) - { + if (FEmMTemp < FEmMTempMin || FEmMTemp > FEmMTempMax) { return grenseKost; - } - else - { + } else { this.FEmM = (double) FEmMTemp; // Starter med blanke ark... this.FEmBakgrunnsdataMatrise = null; @@ -2509,21 +2347,23 @@ public class RoughageNutritionModelImpl implements CostFunction{ double beregnetFEm; // Forutsetter at observasjonene er sorterte - for(Iterator<OptimizationObservation> i = this.observasjoner.iterator();i.hasNext();) - { + for (Iterator<OptimizationObservation> i = this.observasjoner.iterator(); i.hasNext();) { try { OptimizationObservation observasjon = i.next(); // Bruker bare de målingene som er før 1. slått - if(observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) + if (observasjon.getDate().compareTo(this.datoFoersteSlaatt) <= 0) { continue; - if(observasjon.getDate().after(this.datoAndreSlaatt)) + } + if (observasjon.getDate().after(this.datoAndreSlaatt)) { break; + } maaltFEm = observasjon.getFEm(); // Tar høyde for at observasjonen ikke inneholder FEm-måling - if(maaltFEm < 0) + if (maaltFEm < 0) { continue; + } beregnetFEm = this.getFEm(observasjon.getDate()); - }catch (ModelExcecutionException ex) { + } catch (ModelExcecutionException ex) { Logger.getLogger(RoughageNutritionModel.class.getName()).log(Level.SEVERE, null, ex); throw new CostException(ex); } @@ -2533,32 +2373,36 @@ public class RoughageNutritionModelImpl implements CostFunction{ } return differanse; } + } catch (ModelExcecutionException me) { + me.printStackTrace(); } - catch(ModelExcecutionException me) {me.printStackTrace();} } - + return grenseKost; } /** - * Sjekker målte nivåer av råprotein på bestemte datoer, legger til differansen - * mellom målt og beregnet på denne datoen for alle datoer framover (evt. fram - * til dato for ny måling) + * Sjekker målte nivåer av råprotein på bestemte datoer, legger til + * differansen mellom målt og beregnet på denne datoen for alle datoer + * framover (evt. fram til dato for ny måling) */ - private void kallibrerRaaprotein() throws ModelExcecutionException - { + private void kalibrerRaaprotein() throws ModelExcecutionException { boolean DEBUG = false; - if(this.observasjoner == null) return; + if (this.observasjoner == null) { + return; + } // This has already been done, but you can never be too sure... Collections.sort(this.observasjoner); - double differanseMellomMaaltOgSimulertNMengde = 0; + double differanseMellomMaaltOgSimulertNMengde; Calendar cal = Calendar.getInstance(this.timeZone); - for(ListIterator<OptimizationObservation> i=this.observasjoner.listIterator();i.hasNext();) - { + for (ListIterator<OptimizationObservation> i = this.observasjoner.listIterator(); i.hasNext();) { OptimizationObservation observasjon = i.next(); - if(observasjon.getRawProtein() > 0 && this.getRaaprotein(observasjon.getDate(), this.jordtype) > 0) + if(observasjon.getRawProtein() == null) { + continue; + } + if (observasjon.getRawProtein() > 0 && this.getRaaprotein(observasjon.getDate(), this.jordtype) > 0) { double beregnetNMengde = this.getNMengdeFraRaaprotein(this.getRaaprotein(observasjon.getDate(), this.jordtype), observasjon.getDate(), this.jordtype); double maaltNMengde = this.getNMengdeFraRaaprotein(observasjon.getRawProtein(), observasjon.getDate(), this.jordtype); @@ -2566,56 +2410,56 @@ public class RoughageNutritionModelImpl implements CostFunction{ // Finner dato for neste gyldige måling. Dette blir "grensedato" Date grenseDato = null; - while(i.hasNext() && grenseDato == null) - { + while (i.hasNext() && grenseDato == null) { OptimizationObservation tmp = i.next(); - if(tmp.getRawProtein() > 0 && this.getRaaprotein(tmp.getDate(), this.jordtype) > 0) + if (tmp.getRawProtein() > 0 && this.getRaaprotein(tmp.getDate(), this.jordtype) > 0) { grenseDato = tmp.getDate(); + } } cal.setTime(observasjon.getDate()); - while((grenseDato == null || cal.getTime().before(grenseDato) ) && this.resultatMatrise.getParamDoubleValueForDate(cal.getTime(), ResultMatrix.RAAPROTEIN) != null) - { + while ((grenseDato == null || cal.getTime().before(grenseDato)) && this.resultatMatrise.getParamDoubleValueForDate(cal.getTime(), ResultMatrix.RAAPROTEIN) != null) { double preRaaprotein = this.resultatMatrise.getParamDoubleValueForDate(cal.getTime(), ResultMatrix.RAAPROTEIN); double preNMengde = preRaaprotein != 0 ? this.getNMengdeFraRaaprotein(preRaaprotein, cal.getTime(), this.jordtype) : 0; double justertNMengde = preRaaprotein != 0 ? preNMengde + differanseMellomMaaltOgSimulertNMengde : 0; double justertRaaprotein = preRaaprotein != 0 ? this.getRaaproteinFraNMengde(justertNMengde, cal.getTime(), this.jordtype) : 0; this.resultatMatrise.setParamDoubleValueForDate(cal.getTime(), ResultMatrix.RAAPROTEIN, justertRaaprotein); - if(DEBUG) + if (DEBUG) { System.out.println("[GrovforModell/kallibrerRaaprotein] DEBUG dato=" + cal.getTime().toString() + ", preRaaprotein = " + preRaaprotein + ", justertRaaprotein=" + justertRaaprotein + ", raaproteinDiff = " + (justertRaaprotein - preRaaprotein)); + } cal.add(Calendar.DATE, 1); } // Hvis vi fant en grensedato, går vi ett hakk tilbake... - if(grenseDato != null) + if (grenseDato != null) { i.previous(); - + } + } } } /** * Omregningsformel + * * @param raaprotein * @param dato * @return */ - protected double getNMengdeFraRaaprotein(double raaprotein, Date dato, int jordtype) throws ModelExcecutionException - { + protected double getNMengdeFraRaaprotein(double raaprotein, Date dato, int jordtype) throws ModelExcecutionException { double NKons = raaprotein / 6.25; return NKons * this.getAvling(dato, jordtype) / 100; } /** - * + * * @param NMengde * @param dato * @return */ - protected double getRaaproteinFraNMengde(double NMengde, Date dato, int jordtype) throws ModelExcecutionException - { + protected double getRaaproteinFraNMengde(double NMengde, Date dato, int jordtype) throws ModelExcecutionException { return NMengde / this.getAvling(dato, jordtype) * 6.25f * 100; } diff --git a/src/test/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelTest.java b/src/test/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelTest.java index 732ca7e..cb767b9 100644 --- a/src/test/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelTest.java +++ b/src/test/java/no/bioforsk/vips/model/roughagenutritionmodel/RoughageNutritionModelTest.java @@ -28,6 +28,7 @@ import com.ibm.icu.util.Calendar; import com.ibm.icu.util.TimeZone; import java.io.BufferedInputStream; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -87,6 +88,9 @@ public class RoughageNutritionModelTest { config.setConfigParameter("firstHarvest", cal.getTime()); config.setConfigParameter("soilType", RoughageNutritionModel.JORDTYPE_SAND); config.setConfigParameter("cloverShare", RoughageNutritionModel.ENGSAMMENSETNING_KLOEVERANDEL_LITEN); + String[] optimizationInfoLines = {"2015-06-02,2,3,,,,"}; + List<String> optimizationInfo = Arrays.asList(optimizationInfoLines); + config.setConfigParameter("optimizationInfo", optimizationInfo); RoughageNutritionModel instance = new RoughageNutritionModel(); instance.setConfiguration(config); //List<Result> expResult = null; -- GitLab