diff --git a/debug_config.properties b/debug_config.properties index 717dffaff034bd4bac6453e69eb9d95c661faabf..6f97253c832cd0d8951ab8fb1e968b6b2fcf3d2f 100644 --- a/debug_config.properties +++ b/debug_config.properties @@ -1,30 +1,29 @@ BASE_DIR=.. -YIELD_DIR=/Users/peteralexander/Documents/LURG/LPJ/LPJGPLUM_remap6p7_20190225/rcp26 -YIELD_FILENAME=yield.out +YIELD_DIR=/Users/palexand/Documents/LURG/LPJ/CMIP_emulated_test_forPLUM_20200821180759/GFDL-ESM4_ssp126_v20200821_rmol/sim_LPJ-GUESS -DEBUG_LIMIT_COUNTRIES=false -DEBUG_COUNTRY_NAME=Mongolia +END_TIMESTEP=0 -IS_CALIBRATION_RUN = true -GENERATE_NEW_YIELD_CLUSTERS = false -NUM_YIELD_CLUSTERS=8000 +END_FIRST_STAGE_CALIBRATION=10 +TIMESTEP_SIZE=1 -END_TIMESTEP=0 -TIMESTEP_SIZE=10 +IS_CALIBRATION_RUN=true -INTERPOLATE_OUTPUT_YEARS = false +GENERATE_NEW_YIELD_CLUSTERS=false -CHANGE_YIELD_DATA_YEAR=false +DEMAND_PRICE_IMPORT_AND_PROD_COST=true -ORIG_LEAST_COST_MIN=true +YIELD_FILENAME=yield_intpinfs_rmol.out +IRRIG_MAX_WATER_FILENAME=gsirrigation_intpinfs_rmol.out -DEBUG_JUST_DEMAND_OUTPUT=false -PRICE_ELASTIC_DEMAND=true +MIN_FERT_AMOUNT=10 +MID_FERT_AMOUNT=60 +MAX_FERT_AMOUNT=200 +FERT_AMOUNT_PADDING=3 -GAMS_COUNTRY_TO_SAVE=Brazil +LPJG_TIMESTEP_SIZE=10 -SSP_SCENARIO=SSP2_v9_130325 -DONT_REBASE_DEMAND=false +EXTRAPOLATE_YIELD_FERT_RESPONSE=false -DEMAND_RECALC_MAX_ITERATIONS=0 \ No newline at end of file +DEBUG_LIMIT_COUNTRIES=true +DEBUG_COUNTRY_NAME=Italy \ No newline at end of file diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java index 1e7a00fe0665a84f3c4f317b8d825ec13fb139b6..ab610f85d8818fc9b931a14039b5cd4290565f9d 100755 --- a/src/ac/ed/lurg/ModelConfig.java +++ b/src/ac/ed/lurg/ModelConfig.java @@ -155,7 +155,7 @@ public class ModelConfig { public static final boolean DONT_REBASE_DEMAND = getBooleanProperty("DONT_REBASE_DEMAND", false);; public static final String DEMAND_CURVES_FILE = getProperty("DEMAND_CURVES_FILE", DATA_DIR + File.separator + "com_curves.csv"); // either DEMAND_CURVES_FILE or DEMAND_CONSUMPTION_FILE is used, but not both public static final String DEMAND_CONSUMPTION_FILE = getProperty("DEMAND_CONSUMPTION_FILE", DATA_DIR + File.separator + "hist_comsump.csv"); - public static final boolean DEMAND_PRICE_IMPORT_AND_PROD_COST = getBooleanProperty("DEMAND_PRICE_IMPORT_AND_PROD_COST", false); + public static final boolean DEMAND_PRICE_IMPORT_AND_PROD_COST = getBooleanProperty("DEMAND_PRICE_IMPORT_AND_PROD_COST", true); public static final String SSP_FILE = DATA_DIR + File.separator + "ssp.csv"; public static final String BASELINE_CONSUMP_FILE = DATA_DIR + File.separator + "base_consump.csv"; public static final String CALORIE_PER_T_FILE = DATA_DIR + File.separator + "calories_per_t.csv"; @@ -304,7 +304,6 @@ public class ModelConfig { public static final double MAX_INCOME_PROPORTION_FOOD_SPEND = getDoubleProperty("MAX_INCOME_PROPORTION_FOOD_SPEND", 0.7); public static final double PASTURE_HARVEST_FRACTION = getDoubleProperty("PASTURE_HARVEST_FRACTION", 0.5); - public static final double ANIMAL_FEED_FROM_OTHER_SOURCES_RATE = getDoubleProperty("ANIMAL_FEED_FROM_OTHER_SOURCES_RATE", 0.127); // animal nutrition coming from sources other crops modelled public static final double MEAT_EFFICIENCY = getDoubleProperty("MEAT_EFFICIENCY", 1.0); // 'meat' is includes feed conversion ratio already, this is tech. change or similar public static final double IRRIGIATION_EFFICIENCY = getDoubleProperty("IRRIGIATION_EFFICIENCY", 0.5); public static final int ELLIOTT_BASEYEAR = 2010; @@ -408,4 +407,6 @@ public class ModelConfig { public static final boolean USE_CRAFTY_COUNTRIES = getBooleanProperty("USE_CRAFTY_COUNTRIES", false); public static final String CRAFTY_PRODUCTION_DIR = getProperty("CRAFTY_PRODUCTION_DIR", OUTPUT_DIR + File.separator + "crafty"); + + public static final boolean EXTRAPOLATE_YIELD_FERT_RESPONSE = getBooleanProperty("EXTRAPOLATE_YIELD_FERT_RESPONSE", false); } diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java index 5f7d2f0dd3e92bc12fc3fcede25aa59f29dcf91a..af4e20c6a75a76683bf519b623c553fb65697916 100644 --- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java @@ -31,6 +31,7 @@ import ac.ed.lurg.types.CommodityType; import ac.ed.lurg.types.CropType; import ac.ed.lurg.types.LandCoverType; import ac.ed.lurg.types.Parameter; +import ac.ed.lurg.types.YieldType; import ac.ed.lurg.utils.LazyHashMap; import ac.ed.lurg.utils.LogWriter; import ac.ed.lurg.yield.YieldResponsesItem; @@ -227,13 +228,17 @@ public class GamsLocationOptimiser { double maxIrrig = irrigationItem.getMaxIrrigAmount(crop); if (DEBUG) LogWriter.println(String.format("%d %15s,\t %.1f,\t %.1f, \t %.1f,\t %.1f,\t %.2f,\t\t [%.2f],\t [%.2f],\t {%.2f}", - locationId, crop.getGamsName(), yresp.getYieldNone(crop), yresp.getYieldFertOnly(crop), yresp.getYieldIrrigOnly(crop), yresp.getYieldMax(crop), yresp.getShockRate(crop), - yresp.getFertParam(crop), yresp.getIrrigParam(crop), maxIrrig)); - - setGamsParamValue(yNoneP.addRecord(v), yresp.getYieldNone(crop), 4); - setGamsParamValue(y_fert.addRecord(v), yresp.getYieldFertOnly(crop), 4); - setGamsParamValue(y_irrig.addRecord(v), yresp.getYieldIrrigOnly(crop), 4); - setGamsParamValue(y_both.addRecord(v), yresp.getYieldMax(crop), 4); + locationId, crop.getGamsName(), + yresp.getExtrapolatedYield(YieldType.NO_FERT_NO_IRRIG, crop), + yresp.getExtrapolatedYield(YieldType.FERT_MAX_NO_IRRIG, crop), + yresp.getExtrapolatedYield(YieldType.NO_FERT_IRRIG_MAX, crop), + yresp.getExtrapolatedYield(YieldType.FERT_MAX_IRRIG_MAX, crop), + yresp.getShockRate(crop), yresp.getFertParam(crop), yresp.getIrrigParam(crop), maxIrrig)); + + setGamsParamValue(yNoneP.addRecord(v), yresp.getExtrapolatedYield(YieldType.NO_FERT_NO_IRRIG, crop), 4); + setGamsParamValue(y_fert.addRecord(v), yresp.getExtrapolatedYield(YieldType.FERT_MAX_NO_IRRIG, crop), 4); + setGamsParamValue(y_irrig.addRecord(v), yresp.getExtrapolatedYield(YieldType.NO_FERT_IRRIG_MAX, crop), 4); + setGamsParamValue(y_both.addRecord(v), yresp.getExtrapolatedYield(YieldType.FERT_MAX_IRRIG_MAX, crop), 4); setGamsParamValue(y_shock.addRecord(v), yresp.getShockRate(crop), 4); setGamsParamValue(fert_p.addRecord(v), yresp.getFertParam(crop), 4); setGamsParamValue(irrig_p.addRecord(v), yresp.getIrrigParam(crop), 4); diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java index b6602c587c6356b96f3672708019d982c47bd8a2..75ff7437b552ea2461cb28b7485c0842684bc48e 100644 --- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java +++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java @@ -290,8 +290,8 @@ public class GamsRasterOptimiser { RasterKey key = entry.getKey(); CropType crop = CropType.WHEAT; - if (yresp != null && (yresp.getYieldMax(crop) < yresp.getYieldFertOnly(crop) || yresp.getYieldMax(crop) < yresp.getYieldIrrigOnly(crop))) { - logWarningWithCoord("Inconsistency F only:" + yresp.getYieldFertOnly(crop) + ", I only" + yresp.getYieldIrrigOnly(crop) + ", max " + yresp.getYieldMax(crop) + " at ", key, yieldRaster); + if (yresp != null && (yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) < yresp.getYield(YieldType.FERT_MAX_NO_IRRIG, crop) || yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) < yresp.getYield(YieldType.NO_FERT_IRRIG_MAX, crop))) { + logWarningWithCoord("Inconsistency F only:" + yresp.getYield(YieldType.FERT_MAX_NO_IRRIG, crop) + ", I only" + yresp.getYield(YieldType.NO_FERT_IRRIG_MAX, crop) + ", max " + yresp.getYield(YieldType.FERT_MAX_IRRIG_MAX, crop) + " at ", key, yieldRaster); } } diff --git a/src/ac/ed/lurg/yield/YieldClusterPoint.java b/src/ac/ed/lurg/yield/YieldClusterPoint.java index f8f5cfc3f07cfa0a30945401b82c0a2b3545362d..91f9eb806ce9ddf2cc0013026b19b985a130f115 100644 --- a/src/ac/ed/lurg/yield/YieldClusterPoint.java +++ b/src/ac/ed/lurg/yield/YieldClusterPoint.java @@ -5,6 +5,7 @@ import java.util.Collection; import ac.ed.lurg.landuse.IrrigationItem; import ac.ed.lurg.types.CropType; +import ac.ed.lurg.types.YieldType; import ac.ed.lurg.utils.LogWriter; import ac.ed.lurg.utils.cluster.ClusteringPoint; import ac.sac.raster.RasterKey; @@ -34,12 +35,12 @@ public class YieldClusterPoint implements ClusteringPoint<String> { this.rasterKey = rasterKey; // not sure if we be better to get a reference to the YieldResponsesItem, rather than caching these values? - this.wheatMin = yields.getYieldNone(CropType.WHEAT); - this.riceMax = yields.getYieldMax(CropType.RICE); - this.maizeMin = yields.getYieldNone(CropType.MAIZE); - this.pasture = yields.getYieldNone(CropType.PASTURE); - this.wheatMax = yields.getYieldMax(CropType.WHEAT); - this.maizeMax = yields.getYieldMax(CropType.MAIZE); + this.wheatMin = yields.getYield(YieldType.NO_FERT_NO_IRRIG, CropType.WHEAT); + this.riceMax = yields.getYield(YieldType.FERT_MAX_IRRIG_MAX, CropType.RICE); + this.maizeMin = yields.getYield(YieldType.NO_FERT_NO_IRRIG, CropType.MAIZE); + this.pasture = yields.getYield(YieldType.NO_FERT_NO_IRRIG, CropType.PASTURE); + this.wheatMax = yields.getYield(YieldType.FERT_MAX_IRRIG_MAX, CropType.WHEAT); + this.maizeMax = yields.getYield(YieldType.FERT_MAX_IRRIG_MAX, CropType.MAIZE); this.irrigWaterAval = irrigItem.getIrrigConstraint(); this.protectedArea = protectedArea; } diff --git a/src/ac/ed/lurg/yield/YieldResponse.java b/src/ac/ed/lurg/yield/YieldResponse.java index 86e1e9b2a66110c20f554b4cfd9a50422a2d7230..fb4b7c4a58a68f3f3a574c00040e3c1323fee796 100644 --- a/src/ac/ed/lurg/yield/YieldResponse.java +++ b/src/ac/ed/lurg/yield/YieldResponse.java @@ -5,9 +5,11 @@ import java.util.Map; import ac.ed.lurg.ModelConfig; import ac.ed.lurg.types.YieldType; +import ac.ed.lurg.utils.LogWriter; public class YieldResponse { private Map<YieldType, Double> yields = new HashMap<YieldType, Double>(); + private Map<YieldType, Double> yieldsExtrapolated; private double yieldShock; private double fertParm; private double irrigParm; @@ -15,7 +17,7 @@ public class YieldResponse { private static double defaultParam; static { - defaultParam = calcParam(0, 0.8, 1, 0, 0.5, 1.0); // default assumes 80% at mid point + defaultParam = calcParam(0, 0.8, 1, 0.5); // default assumes 80% at mid point } @@ -36,14 +38,59 @@ public class YieldResponse { return d.doubleValue(); } + public double getExtrapolatedYield(YieldType type) { + if (yieldsExtrapolated == null) + calcParams(); + + Double d = yieldsExtrapolated.get(type); + + if (d == null) + return Double.NaN; + + return d.doubleValue(); + } + public double getFertParam() { - if (fertParm == 0) { - // fertParm = calcParam (0, 0.5, 1, 5.0/200, 50.0/200, 200.0/200); // we do have MID fert data, but looks wrong - fertParm = calcParam(getYield(YieldType.NO_FERT_NO_IRRIG), getYield(YieldType.FERT_MID_NO_IRRIG), getYield(YieldType.FERT_MAX_NO_IRRIG), - ModelConfig.MIN_FERT_AMOUNT/ModelConfig.MAX_FERT_AMOUNT, ModelConfig.MID_FERT_AMOUNT/ModelConfig.MAX_FERT_AMOUNT, ModelConfig.MAX_FERT_AMOUNT/ModelConfig.MAX_FERT_AMOUNT); + if (fertParm == 0) + calcParams(); + + return fertParm; + } + + private double calcParams() { + double yMinNoI = getYield(YieldType.NO_FERT_NO_IRRIG); + double yMidNoI = getYield(YieldType.FERT_MID_NO_IRRIG); + double yMaxNoI = getYield(YieldType.FERT_MAX_NO_IRRIG); + + double fMid = (ModelConfig.MID_FERT_AMOUNT-ModelConfig.MIN_FERT_AMOUNT)/(ModelConfig.MAX_FERT_AMOUNT-ModelConfig.MIN_FERT_AMOUNT); + + if (ModelConfig.EXTRAPOLATE_YIELD_FERT_RESPONSE && !(yMidNoI <= yMinNoI || yMaxNoI <= yMidNoI)) { + double yMinI = getYield(YieldType.NO_FERT_IRRIG_MAX); + double yMidI = getYield(YieldType.FERT_MID_IRRIG_MAX); + double yMaxI = getYield(YieldType.FERT_MAX_IRRIG_MAX); + + double asymptoteYieldIncNoI = yieldAsymptoteSolve(yMidNoI-yMinNoI, yMaxNoI-yMinNoI, fMid); + fertParm = -1/fMid * Math.log(1-(yMidI-yMinI)/asymptoteYieldIncNoI); + double asymptoteYieldIncI = (yMaxI-yMinI) / (1 - Math.exp(-fertParm)); + //LogWriter.println(String.format("fertParm from solver: asymptoteYieldIncNoI: %.2f, asymptoteYieldIncI: %.2f, fertParm %.4f", asymptoteYieldIncNoI, asymptoteYieldIncI, fertParm)); - //LogWriter.println(String.format("%s %s %s", getYield(YieldType.NO_FERT_NO_IRRIG), getYield(YieldType.FERT_MID_NO_IRRIG), getYield(YieldType.FERT_MAX_NO_IRRIG))); + if (Double.isNaN(asymptoteYieldIncI) || Double.isNaN(fertParm)) { + LogWriter.println("Not finding a suitable extrapolating solution. Defaulting to old style"); + } + else { + yieldsExtrapolated = new HashMap<YieldType, Double>(); + yieldsExtrapolated.put(YieldType.NO_FERT_NO_IRRIG, yMinNoI); + yieldsExtrapolated.put(YieldType.FERT_MAX_NO_IRRIG, asymptoteYieldIncNoI+yMinI); + yieldsExtrapolated.put(YieldType.NO_FERT_IRRIG_MAX, yMinI); + yieldsExtrapolated.put(YieldType.FERT_MAX_IRRIG_MAX, asymptoteYieldIncI+yMinI); + } + } + + if (yieldsExtrapolated == null) { // Default behaviour if EXTRAPOLATE_YIELD_FERT_RESPONSE is false or a problem with the other approach + fertParm = calcParam(yMinNoI, yMidNoI, yMaxNoI, fMid); + yieldsExtrapolated = yields; } + return fertParm; } @@ -55,11 +102,11 @@ public class YieldResponse { return irrigParm; } - private static double calcParam(double yMin, double yMid, double yMax, double xMin, double xMid, double xMax) { + private static double calcParam(double yMin, double yMid, double yMax, double xMid) { if (yMid <= yMin || yMax <= yMid) return defaultParam; - - return -Math.log(1 - ((yMid-yMin)/(yMax-yMin))) * (xMax-xMin) / (xMid-xMin); // 1-exp(-x) function + + return -Math.log(1 - ((yMid-yMin)/(yMax-yMin))) / xMid; // 1-exp(-x) function // return Math.log((yMid - yMin)/(yMax - yMin)) / Math.log((xMid - xMin)/(xMax - xMin)) / xMax; // Cobb-Douglas } @@ -70,4 +117,48 @@ public class YieldResponse { public double getShock() { return yieldShock; } + + private static double f(double x, double yM, double yH, double fM) { + return 1-yM/x - Math.pow(1-yH/x, fM); + } + + private double yieldAsymptoteSolve (double yM, double yH, double fM) { + + double a = yH; + double b = yH * 20; + double epsilon = 0.0001; + + if (fM * yH > yM) { + LogWriter.printlnError(String.format("yieldAsymptoteSolve: not diminishing yield increases to N: yM=%.3f, yH=%.3f, fM=%.3f", yM, yH, fM)); + return Double.NaN; + } + + double m = Double.NaN; + double fm; + double fa = f(a, yM, yH, fM); + double fb = f(b, yM, yH, fM); + + if (fa * fb > 0) { + LogWriter.printlnError(String.format("yieldAsymptoteSolve: not bracketing root: yM=%.3f, yH=%.3f, fM=%.3f", yM, yH, fM)); + return Double.NaN; + } + + while ((b-a) > epsilon) + { + m = (a+b)/2; // Mid point + fm = f(m, yM, yH, fM); + + if ( fm * fa > 0 ) + { // f(a) and f(m) have same signs: move a + a = m; + fa = fm; + } + else + { // f(a) and f(m) have different signs: move b + b = m; + fb = fm; + } + } + return m; + } } \ No newline at end of file diff --git a/src/ac/ed/lurg/yield/YieldResponsesItem.java b/src/ac/ed/lurg/yield/YieldResponsesItem.java index e78fa2ff79316084cd243949d99aa0d0a9265720..e6723643f8a33dee46399e90f8eb44eaeef77d1b 100644 --- a/src/ac/ed/lurg/yield/YieldResponsesItem.java +++ b/src/ac/ed/lurg/yield/YieldResponsesItem.java @@ -29,26 +29,11 @@ public class YieldResponsesItem implements RasterItem { return d; } - public double getYieldNone(CropType crop) { - return getYield(YieldType.NO_FERT_NO_IRRIG, crop); + public double getExtrapolatedYield(YieldType yieldType, CropType crop) { + YieldResponse yr = getYieldResponseForCrop(crop); + return yr.getExtrapolatedYield(yieldType); } - public double getYieldMidFertOnly(CropType crop) { - return getYield(YieldType.FERT_MID_NO_IRRIG, crop); - } - - public double getYieldFertOnly(CropType crop) { - return getYield(YieldType.FERT_MAX_NO_IRRIG, crop); - } - - public double getYieldIrrigOnly(CropType crop) { - return getYield(YieldType.NO_FERT_IRRIG_MAX, crop); - } - - public double getYieldMax(CropType crop) { - return getYield(YieldType.FERT_MAX_IRRIG_MAX, crop); - } - public double getFertParam(CropType crop) { return getYieldResponseForCrop(crop).getFertParam(); }