diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms index e1a9e64eefb22bdc99d21189a9b29bfe862495d0..e1e15a44845005ca62ce17ee88b12868a3b16021 100644 --- a/GAMS/IntExtOpt.gms +++ b/GAMS/IntExtOpt.gms @@ -3,7 +3,6 @@ SET crop_less_pasture(crop) arable crops types includes treenuts but not pasture / cereals, fruits, oilcrops, starchyRoots, treenuts, vegetables /; SET feed_crop(crop) / cereals, oilcrops /; SET not_feed_crops(crop) / fruits, starchyRoots, treenuts, vegetables, pasture_or_meat /; - SET landuse land use type / cropland, pasture /; PARAMETER previous_area(landuse) areas for previous timestep in ha; @@ -12,36 +11,39 @@ PARAMETER world_input_energy(crop) average input energy from world exports used to determine if we should import or export; PARAMETER maxExport(crop) max export for each crop based on world market; - SCALAR meatEfficency; - SCALAR maxLandUseChange; - SCALAR maxIntensity; - SCALAR tradeBarrier / 1 / ; + SCALAR meatEfficency efficency of converting feed and pasture into animal products; + SCALAR maxLandUseChange max rate of land use change; + SCALAR maxIntensity max intensity which will rise over time; + SCALAR tradeBarrier trade barrier which adjust energy cost of imports; + SCALAR landChangeEnergy energy required to add ha of agricultural land; $gdxin %gdxincname% -$load previous_area, demand, yield_ref, world_input_energy, maxExport, meatEfficency, maxLandUseChange, maxIntensity +$load previous_area, demand, yield_ref, world_input_energy, maxExport, meatEfficency, maxLandUseChange, maxIntensity, tradeBarrier, landChangeEnergy $gdxin - + + DISPLAY landChangeEnergy + PARAMETER feedEnergy(crop) energy from feed in MJ per t / cereals 1 oilcrops 1.5 pasture_or_meat 0.4 / ; - + VARIABLES area(crop) total area for each crop - ha - feedArea(crop) area used for feed for each crop - ha + feedAmount(crop) amount of feed for each crop - t intensity(crop) intensity for each crop - unitless yield(crop) yield per area for each crop - t per ha unit_energy(crop) energy per area for each crop - energy exportAmount(crop) export of crop - t + importAmount(crop) export of crop - t tradeBarrierFactor(crop) energy total input energy - energy; - - POSITIVE VARIABLE area, intensity, feedArea; - + + POSITIVE VARIABLE area, intensity, feedAmount, exportAmount, importAmount; + EQUATIONS UNIT_ENERGY_EQ(crop) energy per area YIELD_EQ(crop) yield given chosen intensity - TRADE_BARRIER_FACTOR_EQ CROP_DEMAND_CONSTRAINT(crop_less_pasture) satisfy demand for crop MEAT_DEMAND_CONSTRAINT satisfy demand for crop MIN_INTENSITY_CONSTRAINT(crop) intensity constraint @@ -52,34 +54,35 @@ $gdxin NON_FEED_CROP_CONSTRAINT(not_feed_crops) constraint to set non feed crop feed usage to zero EXPORT_LEVEL_CONSTRAINT(crop) constraint on max exports ENERGY_EQ total energy objective function; - + UNIT_ENERGY_EQ(crop) .. unit_energy(crop) =E= intensity(crop) + intensity(crop) / 2 * intensity(crop) / 8; - + YIELD_EQ(crop) .. yield(crop) =E= yield_ref(crop) * intensity(crop); - TRADE_BARRIER_FACTOR_EQ(crop) .. tradeBarrierFactor(crop) =E= 1; - - CROP_DEMAND_CONSTRAINT(crop_less_pasture) .. (area(crop_less_pasture) - feedArea(crop_less_pasture)) * yield(crop_less_pasture) =G= demand(crop_less_pasture) + exportAmount(crop_less_pasture); - - MEAT_DEMAND_CONSTRAINT .. meatEfficency*sum(feed_crop, feedEnergy(feed_crop) * feedArea(feed_crop) * yield(feed_crop)) + - feedEnergy('pasture_or_meat') * area('pasture_or_meat') * yield('pasture_or_meat') =G= demand('pasture_or_meat') + exportAmount('pasture_or_meat'); - + CROP_DEMAND_CONSTRAINT(crop_less_pasture) .. area(crop_less_pasture) * yield(crop_less_pasture) - feedAmount(crop_less_pasture) =G= + demand(crop_less_pasture) + exportAmount(crop_less_pasture) - importAmount(crop_less_pasture); + + MEAT_DEMAND_CONSTRAINT .. meatEfficency*sum(feed_crop, feedEnergy(feed_crop) * feedAmount(feed_crop)) + feedEnergy('pasture_or_meat') * area('pasture_or_meat') * yield('pasture_or_meat') =G= + demand('pasture_or_meat') + exportAmount('pasture_or_meat') - importAmount('pasture_or_meat'); + MIN_INTENSITY_CONSTRAINT(crop) .. intensity(crop) =G= 1; - + MAX_INTENSITY_CONSTRAINT(crop) .. intensity(crop) =L= maxIntensity; - + CROPLAND_CHANGE_CONSTRAINT .. abs(sum(crop_less_pasture, area(crop_less_pasture))/previous_area('cropland') - 1) =L= maxLandUseChange; - + PASTURE_CHANGE_CONSTRAINT .. abs(area('pasture_or_meat')/previous_area('pasture') - 1) =L= maxLandUseChange; - + AGRI_LAND_CHANGE_CONSTRAINT .. abs(sum(crop, area(crop)) / sum(landuse, previous_area(landuse)) - 1) =L= maxLandUseChange; - NON_FEED_CROP_CONSTRAINT(not_feed_crops) .. feedArea(not_feed_crops) =E= 0; + NON_FEED_CROP_CONSTRAINT(not_feed_crops) .. feedAmount(not_feed_crops) =E= 0; EXPORT_LEVEL_CONSTRAINT(crop) .. exportAmount(crop) =L= maxExport(crop); - - ENERGY_EQ .. energy =E= SUM(crop, area(crop)*unit_energy(crop)) - sum(crop, exportAmount(crop) * world_input_energy(crop) * tradeBarrierFactor(crop)); - + + ENERGY_EQ .. energy =E= SUM(crop, area(crop)*unit_energy(crop)) + + (sum(crop, area(crop)) - sum(landuse, previous_area(landuse))) * landChangeEnergy + - sum(crop, (exportAmount(crop) - importAmount(crop) * tradeBarrier) * world_input_energy(crop)); + MODEL LAND_USE /ALL/ ; SOLVE LAND_USE USING DNLP MINIMIZING energy; diff --git a/src/ac/ed/lurg/GamsTest.java b/src/ac/ed/lurg/GamsTest.java index 9eca86acb1308373ffbf3ccef927f8b05e567f2f..ae8a36e9df5366e650c26b252ff1e1fc241ce32e 100644 --- a/src/ac/ed/lurg/GamsTest.java +++ b/src/ac/ed/lurg/GamsTest.java @@ -2,6 +2,8 @@ package ac.ed.lurg; import java.io.File; +import ac.ed.lurg.utils.LogWriter; + import com.gams.api.GAMSDatabase; import com.gams.api.GAMSGlobals; import com.gams.api.GAMSJob; @@ -14,7 +16,7 @@ import com.gams.api.GAMSWorkspaceInfo; public class GamsTest { - private final static String WORKING_DIR = "/Users/peteralexander/Documents/R_Workspace/tempDir"; + private final static String WORKING_DIR = "/Users/peteralexander/Documents/R_Workspace/temp"; public static void main(String[] args) { GamsTest aGamsTest = new GamsTest(); @@ -33,8 +35,8 @@ public class GamsTest { GAMSDatabase db = ws.addDatabase(); GAMSParameter parm = db.addParameter("previous_area", 1, "the previous area for each land use"); - parm.addRecord("cropland").setValue(200.0); - parm.addRecord("pasture").setValue(600.0); + parm.addRecord("cropland").setValue(300.0); + parm.addRecord("pasture").setValue(400.0); parm = db.addParameter("demand", 1, "demand for crop"); parm.addRecord("cereals").setValue(200.0); @@ -43,7 +45,7 @@ public class GamsTest { parm.addRecord("starchyRoots").setValue(100.0); parm.addRecord("treenuts").setValue(20.0); parm.addRecord("vegetables").setValue(50.0); - parm.addRecord("pasture_or_meat").setValue(500); // meat demand in t + parm.addRecord("pasture_or_meat").setValue(200); // meat demand in t parm = db.addParameter("yield_ref", 1, "ref yield t per ha"); parm.addRecord("cereals").setValue(3.0); @@ -55,16 +57,16 @@ public class GamsTest { parm.addRecord("pasture_or_meat").setValue(0.7); // grass yield parm = db.addParameter("world_input_energy", 1, "average input energy from world exports used to determine if we should import or export energy per t"); - parm.addRecord("cereals").setValue(0.45); + parm.addRecord("cereals").setValue(4); parm.addRecord("fruits").setValue(10.0); - parm.addRecord("oilcrops").setValue(2.0); - parm.addRecord("starchyRoots").setValue(0.3); - parm.addRecord("treenuts").setValue(2.0); - parm.addRecord("vegetables").setValue(2); - parm.addRecord("pasture_or_meat").setValue(5); // meat input energy + parm.addRecord("oilcrops").setValue(20.0); + parm.addRecord("starchyRoots").setValue(10.1265); + parm.addRecord("treenuts").setValue(20.0); + parm.addRecord("vegetables").setValue(20); + parm.addRecord("pasture_or_meat").setValue(50); // meat input energy parm = db.addParameter("maxExport", 1, "max export for each crop based on world market"); - parm.addRecord("cereals").setValue(100); + parm.addRecord("cereals").setValue(0); parm.addRecord("fruits").setValue(0); parm.addRecord("oilcrops").setValue(0); parm.addRecord("starchyRoots").setValue(0); @@ -75,6 +77,8 @@ public class GamsTest { db.addParameter("meatEfficency", 0, "efficency of converting feed and pasture into animal products").addRecord().setValue(0.1); db.addParameter("maxLandUseChange", 0, "max rate of land use change").addRecord().setValue(0.1); db.addParameter("maxIntensity", 0, "max intensity which will rise over time").addRecord().setValue(3); + db.addParameter("tradeBarrier", 0, "trade barrier which adjust energy cost of imports").addRecord().setValue(3); + db.addParameter("landChangeEnergy", 0, "energy required to add ha of agricultural land").addRecord().setValue(0.08); // create GAMSJob "t2" from the "model" string variable @@ -89,22 +93,28 @@ public class GamsTest { GAMSVariable varAreas = gamsJob.OutDB().getVariable("area"); GAMSVariable varIntensities = gamsJob.OutDB().getVariable("intensity"); - GAMSVariable varFeedArea = gamsJob.OutDB().getVariable("feedArea"); + GAMSVariable varFeedAmount = gamsJob.OutDB().getVariable("feedAmount"); GAMSVariable varExports = gamsJob.OutDB().getVariable("exportAmount"); + GAMSVariable varImports = gamsJob.OutDB().getVariable("importAmount"); + double totalArea = 0; + for (GAMSVariableRecord rec : varAreas) { - System.out.println(String.format("%s: \t\tarea= %.0f, \tintensity= %.3f, \tfeed_area= %.0f, \texports= %.0f", + LogWriter.println(String.format("%15s:\tarea= %.0f,\tintensity= %.3f,\tfeedAmount= %.0f,\texports= %.0f,\timports= %.0f", rec.getKeys()[0], rec.getLevel(), varIntensities.findRecord(rec.getKeys()[0]).getLevel(), - varFeedArea.findRecord(rec.getKeys()[0]).getLevel(), - varExports.findRecord(rec.getKeys()[0]).getLevel() + varFeedAmount.findRecord(rec.getKeys()[0]).getLevel(), + varExports.findRecord(rec.getKeys()[0]).getLevel(), + varImports.findRecord(rec.getKeys()[0]).getLevel() )); + totalArea += rec.getLevel(); // + " marginal=" + rec.getMarginal()); } - + LogWriter.println("Total Area: " + totalArea); //cleanup(ws.workingDirectory()); } + @SuppressWarnings("unused") private void cleanup(String directory) { File directoryToDelete = new File(directory); String files[] = directoryToDelete.list(); diff --git a/src/ac/ed/lurg/ModelConfig.java b/src/ac/ed/lurg/ModelConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..fd131e81fc4111672224cb6dde34191f17dc64fe --- /dev/null +++ b/src/ac/ed/lurg/ModelConfig.java @@ -0,0 +1,98 @@ +package ac.ed.lurg; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +public class ModelConfig { + + private Properties configFile; + private static ModelConfig modelConfig; + public static final String CONFIG_FILE = System.getProperty("CONFIG_FILE"); + + private ModelConfig() { + configFile = new Properties(); + try { + System.out.println("Config. file is " + CONFIG_FILE); + if (CONFIG_FILE != null) + configFile.load(new FileInputStream(CONFIG_FILE)); + } + catch (IOException e) { + System.err.println("Problems reading config file"); + System.err.println(e.getMessage()); + } + } + + private static ModelConfig getModelConfig() { + if (modelConfig == null) + modelConfig = new ModelConfig(); + + return modelConfig; + } + + private static String getProperty(String prop) { + return getModelConfig().getProp(prop); + } + private static String getProperty(String prop, String defaultString) { + String propValue = getProperty(prop); + return propValue == null ? defaultString : propValue; + } + private String getProp(String prop) { + return configFile.getProperty(prop); + } + + private static Integer getIntProperty(String prop, Integer defaultInt) { + Integer propValue = getModelConfig().getIntProp(prop); + return propValue == null ? defaultInt : propValue; + } + private Integer getIntProp(String prop) { + String v = configFile.getProperty(prop); + return v==null ? null : Integer.valueOf(v); + } + + private static Long getLongProperty(String prop, Long defaultLong) { + Long propValue = getModelConfig().getLongProp(prop); + return propValue == null ? defaultLong : propValue; + } + private Long getLongProp(String prop) { + String v = configFile.getProperty(prop); + return v==null ? null : Long.valueOf(v); + } + + private static Double getDoubleProperty(String prop, Double defaultDouble) { + Double propValue = getModelConfig().getDoubleProp(prop); + return propValue == null ? defaultDouble : propValue; + } + private Double getDoubleProp(String prop) { + String v = configFile.getProperty(prop); + return v==null ? null : Double.valueOf(v); + } + + private static Boolean getBooleanProperty(String prop, Boolean defaultBoolean) { + return getModelConfig().getBooleanProp(prop, defaultBoolean); + } + private boolean getBooleanProp(String prop, Boolean defaultBoolean) { + String v = configFile.getProperty(prop); + return v==null ? defaultBoolean : Boolean.valueOf(v); + } + + public static final boolean SUPPRESS_STD_OUTPUT = getBooleanProperty("SUPPRESS_STD_OUTPUT", Boolean.FALSE); + + public static final String BASE_DIR = getProperty("OUTPUT_DIR", "/Users/peteralexander/Documents/R_Workspace"); + public static final String TEMP_DIR = BASE_DIR + File.separator + "temp"; + public static final String OUTPUT_DIR = BASE_DIR + File.separator + "output"; + public static final String DATA_DIR = BASE_DIR + File.separator + "data"; + + public static final String DEMAND_CURVES_FILE = DATA_DIR + File.separator + "com_curves.csv"; + public static final String SSP_FILE = DATA_DIR + File.separator + "ssp.csv"; + public static final String COUNTRY_DATA_FILE = DATA_DIR + File.separator + "country_data.csv"; + + + + + public static final int START_TIMESTEP = getIntProperty("START_TIMESTEP", 0); + public static final int END_TIMESTEP = getIntProperty("END_TIMESTEP", 36); + public static final int BASE_YEAR = getIntProperty("BASE_YEAR", 2010); + +} \ No newline at end of file diff --git a/src/ac/ed/lurg/ModelContext.java b/src/ac/ed/lurg/ModelContext.java new file mode 100644 index 0000000000000000000000000000000000000000..703a40302e2423a3daab0529a8119e6e913a990e --- /dev/null +++ b/src/ac/ed/lurg/ModelContext.java @@ -0,0 +1,23 @@ +package ac.ed.lurg; + +import ac.ed.lurg.demand.DemandManager; +import ac.ed.lurg.yield.YieldManager; + +public class ModelContext { + private DemandManager demandManager; + private YieldManager yieldManager; + + public ModelContext(DemandManager demandManager, YieldManager yieldManager) { + super(); + this.demandManager = demandManager; + this.yieldManager = yieldManager; + } + + public DemandManager getDemandManager() { + return demandManager; + } + + public YieldManager getYieldManager() { + return yieldManager; + } +} diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java new file mode 100644 index 0000000000000000000000000000000000000000..b4ef407565e3f459fb33040c7842feeff1397d53 --- /dev/null +++ b/src/ac/ed/lurg/ModelMain.java @@ -0,0 +1,48 @@ +package ac.ed.lurg; + +import java.util.Collection; + +import ac.ed.lurg.country.CountryAgent; +import ac.ed.lurg.country.CountryAgentCreator; +import ac.ed.lurg.demand.DemandManager; +import ac.ed.lurg.types.ModelFitType; +import ac.ed.lurg.yield.DummyYieldManager; +import ac.ed.lurg.yield.YieldManager; + +public class ModelMain { + + private Collection<CountryAgent> countryAgents; + ModelContext modelContext; + + public static void main(String[] args) { + ModelMain theModel = new ModelMain(); + theModel.setup(); + theModel.run(); + } + + /* setup models, reading inputs, etc. */ + private void setup() { + DemandManager demandManager = new DemandManager(ModelFitType.get("logistic"), "SSP2_v9_130325"); + + YieldManager yieldManager = new DummyYieldManager(); // would actually create something for a desired scenario + + modelContext = new ModelContext(demandManager, yieldManager); + + // read initial country data (country, areas) + countryAgents = new CountryAgentCreator().getAgents(modelContext); + } + + /* run the model */ + private void run() { + for (int i = ModelConfig.START_TIMESTEP; i <= ModelConfig.END_TIMESTEP; i++) + doTimestep(i); + } + + private void doTimestep(int timestep) { + for (CountryAgent ca : countryAgents) { + ca.determineProduction(timestep); + } + + // examine global trade balance + } +} diff --git a/src/ac/ed/lurg/country/Country.java b/src/ac/ed/lurg/country/Country.java new file mode 100644 index 0000000000000000000000000000000000000000..0337033e7175b761b6a95710bd341137d171a9e6 --- /dev/null +++ b/src/ac/ed/lurg/country/Country.java @@ -0,0 +1,57 @@ +package ac.ed.lurg.country; + +import java.util.HashMap; +import java.util.Map; + +public class Country { + + private String countryName; + private static final Map<String, Country> cache = new HashMap<String, Country>(); + + private Country(String countryName) { + super(); + this.countryName = countryName; + } + + + public static Country get(String name) { + Country type = cache.get(name); + + if (type == null) { + type = new Country(name); + cache.put(name, type); + } + + return type; + } + + public String getCountryName() { + return countryName; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((countryName == null) ? 0 : countryName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Country other = (Country) obj; + if (countryName == null) { + if (other.countryName != null) + return false; + } else if (!countryName.equals(other.countryName)) + return false; + return true; + } +} diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..405b0fd1780c87c7bf108fa9df01babd953f054f --- /dev/null +++ b/src/ac/ed/lurg/country/CountryAgent.java @@ -0,0 +1,50 @@ +package ac.ed.lurg.country; + +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.ModelContext; +import ac.ed.lurg.types.Item; +import ac.ed.lurg.utils.LogWriter; + +/* + * Contains data for a country (perhaps a entire timeseries?!) + */ +public class CountryAgent { + + private ModelContext modelContext; + private Country country; + private Map<Integer, CountryData> timeseries = new HashMap<Integer, CountryData>(); + + public CountryAgent(ModelContext modelContext, Country country, CountryData initialCountryData) { + this.modelContext = modelContext; + this.country = country; + timeseries.put(0, initialCountryData); + } + + public Country getCountry() { + return country; + } + + public double getCropArea(int timestep, Item item) { + CountryData cd = timeseries.get(timestep); + if (cd == null) { + LogWriter.printlnError("Asked for timestep that we don't have " + timestep); + return Double.NaN; + } + + return cd.getCropArea(item); + } + + public void determineProduction(int timestep) { + // get projected demand + Map<Item, Double> projectedDemand = modelContext.getDemandManager().getDemand(country, ModelConfig.BASE_YEAR + timestep); + + // get projected ref yields + + // optimise areas and intensity + + + } +} diff --git a/src/ac/ed/lurg/country/CountryAgentCreator.java b/src/ac/ed/lurg/country/CountryAgentCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..52da9d7ac5a657dea6b539a00cdaa94e8169d8f1 --- /dev/null +++ b/src/ac/ed/lurg/country/CountryAgentCreator.java @@ -0,0 +1,69 @@ +package ac.ed.lurg.country; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.Collection; +import java.util.HashSet; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.ModelContext; +import ac.ed.lurg.types.LandCoverType; +import ac.ed.lurg.utils.LogWriter; + +public class CountryAgentCreator { + + private static final int COUNTRY_COL = 0; + private static final int YEAR_COL = 1; +// private static final int REGION_COL = 2; +// private static final int INCOME_GROUP_COL = 3; + private static final int LAND_COL = 4; + private static final int ARABLE_COL = 5; + private static final int FOREST_COL = 7; + private static final int PERM_CROP_COL = 8; + private static final int PASTURE_COL = 9; + + public Collection<CountryAgent> getAgents(ModelContext modelContext) { + Collection<CountryAgent> countryAgents = new HashSet<CountryAgent>(); + String filename = ModelConfig.COUNTRY_DATA_FILE; + try { + BufferedReader fitReader = new BufferedReader(new FileReader(filename)); + String line, countryName; + int year; + + fitReader.readLine(); // read header + + while ((line=fitReader.readLine()) != null) { + String[] tokens = line.split(","); + + if (tokens.length < 10) + LogWriter.printlnError("Too few columns in " + filename + ", " + line); + + countryName = tokens[COUNTRY_COL]; + year = Integer.parseInt(tokens[YEAR_COL]); + + if (year != ModelConfig.BASE_YEAR) { + LogWriter.printlnError("Baseline country data year (" + year + ") does not match configured year"); + } + + CountryData initialCountryData = new CountryData(); + initialCountryData.setLandCoverArea(LandCoverType.LAND, Double.parseDouble(tokens[LAND_COL])); + initialCountryData.setLandCoverArea(LandCoverType.ARABLE, Double.parseDouble(tokens[ARABLE_COL])); + initialCountryData.setLandCoverArea(LandCoverType.FOREST, Double.parseDouble(tokens[FOREST_COL])); + initialCountryData.setLandCoverArea(LandCoverType.PERM_CROP, Double.parseDouble(tokens[PERM_CROP_COL])); + initialCountryData.setLandCoverArea(LandCoverType.PASTURE, Double.parseDouble(tokens[PASTURE_COL])); + + CountryAgent ca = new CountryAgent(modelContext, Country.get(countryName), initialCountryData); + + countryAgents.add(ca); + } + fitReader.close(); + + } catch (Exception e) { + LogWriter.printlnError("Failed in reading commodity demand fits"); + e.printStackTrace(); + } + + LogWriter.println("Processed " + filename + ", create " + countryAgents.size() + " country agents"); + return countryAgents; + } +} diff --git a/src/ac/ed/lurg/country/CountryData.java b/src/ac/ed/lurg/country/CountryData.java new file mode 100644 index 0000000000000000000000000000000000000000..845f59136b66b781c59abc58e4fa96d2ac78087d --- /dev/null +++ b/src/ac/ed/lurg/country/CountryData.java @@ -0,0 +1,34 @@ +package ac.ed.lurg.country; + +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.types.Item; +import ac.ed.lurg.types.LandCoverType; + +/* + * Data for one year and country, but it does not know which one + */ +public class CountryData { + + private Map<Item, Double> cropAreas = new HashMap<Item, Double>(); + private Map<LandCoverType, Double> landCoverAreas = new HashMap<LandCoverType, Double>(); + + public double getLandCoverArea(LandCoverType c) { + Double d = landCoverAreas.get(c); + return d == null ? Double.NaN : d; + } + + public void setLandCoverArea(LandCoverType c, double d) { + landCoverAreas.put(c, d); + } + + public double getCropArea(Item c) { + Double d = cropAreas.get(c); + return d == null ? Double.NaN : d; + } + + public void setCropArea(Item c, double d) { + cropAreas.put(c, d); + } +} diff --git a/src/ac/ed/lurg/demand/DemandCurve.java b/src/ac/ed/lurg/demand/DemandCurve.java new file mode 100644 index 0000000000000000000000000000000000000000..c704f8d8526f76897c25896ae380c9ba1c5e06af --- /dev/null +++ b/src/ac/ed/lurg/demand/DemandCurve.java @@ -0,0 +1,78 @@ +package ac.ed.lurg.demand; + +import ac.ed.lurg.types.Item; +import ac.ed.lurg.types.ModelFitType; + + +public class DemandCurve { + + private double a; + private double b; + private double c; + private ModelFitType fitType; + private Item item; + + DemandCurve(Item item, double a, double b, double c, ModelFitType fitType) { + super(); + this.item = item; + this.a = a; + this.b = b; + this.c = c; + this.fitType = fitType; + } + + public double getA() { + return a; + } + + public double getB() { + return b; + } + + public double getC() { + return c; + } + + public ModelFitType getFitType() { + return fitType; + } + + public Item getItem() { + return item; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fitType == null) ? 0 : fitType.hashCode()); + result = prime * result + ((item == null) ? 0 : item.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DemandCurve other = (DemandCurve) obj; + if (fitType == null) { + if (other.fitType != null) + return false; + } else if (!fitType.equals(other.fitType)) + return false; + if (item == null) { + if (other.item != null) + return false; + } else if (!item.equals(other.item)) + return false; + return true; + } + + public double project(double gdpPc) { + return a/(1+Math.exp((b-gdpPc)/c)); + } +} diff --git a/src/ac/ed/lurg/demand/DemandCurveManager.java b/src/ac/ed/lurg/demand/DemandCurveManager.java new file mode 100644 index 0000000000000000000000000000000000000000..75d3f8a2d2710413e764d7863d5d9b55ae7d9781 --- /dev/null +++ b/src/ac/ed/lurg/demand/DemandCurveManager.java @@ -0,0 +1,69 @@ +package ac.ed.lurg.demand; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.Collection; +import java.util.HashSet; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.types.Item; +import ac.ed.lurg.types.ModelFitType; +import ac.ed.lurg.utils.LogWriter; + +public class DemandCurveManager { + private static final int ITEM_COL = 0; + private static final int A_COL = 1; + private static final int B_COL = 2; + private static final int C_COL = 3; + private static final int FIT_TYPE_COL = 6; + + private Collection<DemandCurve> curves = new HashSet<DemandCurve>(); + + public DemandCurveManager() { + super(); + read(); + } + + public void read() { + String filename = ModelConfig.DEMAND_CURVES_FILE; + try { + BufferedReader fitReader = new BufferedReader(new FileReader(filename)); + String line, item, fitType; + double a, b, c; + fitReader.readLine(); // read header + + while ((line=fitReader.readLine()) != null) { + String[] tokens = line.split(","); + + if (tokens.length < 7) + LogWriter.printlnError("Too few columns in " + filename + ", " + line); + + item = tokens[ITEM_COL]; + a = Double.parseDouble(tokens[A_COL]); + b = Double.parseDouble(tokens[B_COL]); + c = Double.parseDouble(tokens[C_COL]); + fitType = tokens[FIT_TYPE_COL]; + + DemandCurve dc = new DemandCurve(Item.get(item), a, b, c, ModelFitType.get(fitType)); + curves.add(dc); + } + fitReader.close(); + + } catch (Exception e) { + LogWriter.printlnError("Failed in reading commodity demand fits"); + e.printStackTrace(); + } + LogWriter.println("Processed " + filename + ", create " + curves.size() + " demand curves"); + } + + public Collection<DemandCurve> get(ModelFitType fitType) { + Collection<DemandCurve> fitTypeCurves = new HashSet<DemandCurve>(); + + for (DemandCurve dc : curves) { + if (dc.getFitType().equals(fitType)) { + fitTypeCurves.add(dc); + } + } + return fitTypeCurves; + } +} diff --git a/src/ac/ed/lurg/demand/DemandManager.java b/src/ac/ed/lurg/demand/DemandManager.java new file mode 100644 index 0000000000000000000000000000000000000000..3654c1bbdbe3a5e90fd1071fec11bc8f4b637efa --- /dev/null +++ b/src/ac/ed/lurg/demand/DemandManager.java @@ -0,0 +1,39 @@ +package ac.ed.lurg.demand; + +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.country.Country; +import ac.ed.lurg.types.Item; +import ac.ed.lurg.types.ModelFitType; + +public class DemandManager { + + private DemandCurveManager demandCurveManager; + private SspManager sspManager; + + private ModelFitType fitType; // these could be mutable + private String ssp_scenario; + + public DemandManager (ModelFitType fitType, String ssp_scenario) { + this.fitType = fitType; + this.ssp_scenario = ssp_scenario; + demandCurveManager = new DemandCurveManager(); + sspManager = new SspManager(); + } + + public Map<Item, Double> getDemand(Country country, int year) { + Map<Item, Double> demandMap = new HashMap<Item, Double>(); + SspData sd = sspManager.get(ssp_scenario, year, country); + + for (DemandCurve dc : demandCurveManager.get(fitType)) { + double d = dc.project(sd.getGdpPc()); + demandMap.put(dc.getItem(), d); + } + return demandMap; + } + + public String getModelAndScenarioDescription() { + return fitType + " " + ssp_scenario; + } +} diff --git a/src/ac/ed/lurg/demand/SspData.java b/src/ac/ed/lurg/demand/SspData.java new file mode 100644 index 0000000000000000000000000000000000000000..5dedb78ba40cc6346812e6af15c4dc6ff6b7934e --- /dev/null +++ b/src/ac/ed/lurg/demand/SspData.java @@ -0,0 +1,25 @@ +package ac.ed.lurg.demand; + + +public class SspData { + + private double population; + private double gdp; + + public SspData(double population, double gdp) { + super(); + this.population = population; + this.gdp = gdp; + } + + public double getPopulation() { + return population; + } + public double getGdp() { + return gdp; + } + + public double getGdpPc() { + return gdp/population; + } +} diff --git a/src/ac/ed/lurg/demand/SspKey.java b/src/ac/ed/lurg/demand/SspKey.java new file mode 100644 index 0000000000000000000000000000000000000000..725284bd07cedf2fd023451b625d87eccf6b7062 --- /dev/null +++ b/src/ac/ed/lurg/demand/SspKey.java @@ -0,0 +1,63 @@ +package ac.ed.lurg.demand; + +import ac.ed.lurg.country.Country; + +public class SspKey { + private String scenario; + private int year; + private Country country; + + public SspKey(String scenario, int year, Country country) { + super(); + this.scenario = scenario; + this.year = year; + this.country = country; + } + + public String getScenario() { + return scenario; + } + + public int getYear() { + return year; + } + + public Country getCountry() { + return country; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((country == null) ? 0 : country.hashCode()); + result = prime * result + + ((scenario == null) ? 0 : scenario.hashCode()); + result = prime * result + year; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SspKey other = (SspKey) obj; + if (country == null) { + if (other.country != null) + return false; + } else if (!country.equals(other.country)) + return false; + if (scenario == null) { + if (other.scenario != null) + return false; + } else if (!scenario.equals(other.scenario)) + return false; + if (year != other.year) + return false; + return true; + } +} diff --git a/src/ac/ed/lurg/demand/SspManager.java b/src/ac/ed/lurg/demand/SspManager.java new file mode 100644 index 0000000000000000000000000000000000000000..0699b074d4e199501903122fb3d17fc88bd423e4 --- /dev/null +++ b/src/ac/ed/lurg/demand/SspManager.java @@ -0,0 +1,74 @@ +package ac.ed.lurg.demand; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.HashMap; +import java.util.Map; + +import ac.ed.lurg.ModelConfig; +import ac.ed.lurg.country.Country; +import ac.ed.lurg.utils.LogWriter; + +public class SspManager { +// private static final int MODEL_COL = 0; + private static final int SCENARIO_COL = 1; + private static final int YEAR_COL = 2; + private static final int POPULATION_COL = 3; + private static final int COUNTRY_COL = 4; + private static final int GDP_COL = 5; + + private Map<SspKey, SspData> sspMap = new HashMap<SspKey, SspData>(); + + public SspManager() { + super(); + read(); + } + + public void read() { + String filename = ModelConfig.SSP_FILE; + try { + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line, scenario, countryName; + double population, gdp; + int year; + reader.readLine(); // read header + + while ((line=reader.readLine()) != null) { + String[] tokens = line.split(","); + + if (tokens.length < 6) + LogWriter.printlnError("Too few columns in " + filename + ", " + line); + + try { + //model = tokens[MODEL_COL]; + scenario = tokens[SCENARIO_COL]; + year = Integer.parseInt(tokens[YEAR_COL]); + population = Double.parseDouble(tokens[POPULATION_COL]); + countryName = tokens[COUNTRY_COL]; + gdp = Double.parseDouble(tokens[GDP_COL]); + + SspKey sspKey = new SspKey(scenario, year, Country.get(countryName)); + SspData sspData = new SspData(population, gdp); + sspMap.put(sspKey, sspData); + } catch (Exception e) { + LogWriter.printlnError("Failed in processing ssp line " + line); + e.printStackTrace(); + } + } + reader.close(); + + } catch (Exception e) { + LogWriter.printlnError("Failed in reading ssp data file"); + e.printStackTrace(); + } + LogWriter.println("Processed " + filename + ", create " + sspMap.size() + " SSP entries"); + } + + public SspData get(String scenario, int year, Country country) { + return get(new SspKey(scenario, year, country)); + } + + public SspData get(SspKey sspKey) { + return sspMap.get(sspKey); + } +} diff --git a/src/ac/ed/lurg/types/AbstractNameType.java b/src/ac/ed/lurg/types/AbstractNameType.java new file mode 100644 index 0000000000000000000000000000000000000000..86e3f4f41f3d54b3289215e2d76cc01337dba99f --- /dev/null +++ b/src/ac/ed/lurg/types/AbstractNameType.java @@ -0,0 +1,40 @@ +package ac.ed.lurg.types; + + +public abstract class AbstractNameType { + String name; + + protected AbstractNameType(String name) { + this.name = name; + } + + public String toString() { + return getClass().getName() + " [" + name + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AbstractNameType other = (AbstractNameType) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } +} + diff --git a/src/ac/ed/lurg/types/Item.java b/src/ac/ed/lurg/types/Item.java new file mode 100644 index 0000000000000000000000000000000000000000..d54fc854b6d42919f2e386d01448b4187c801f0a --- /dev/null +++ b/src/ac/ed/lurg/types/Item.java @@ -0,0 +1,29 @@ +package ac.ed.lurg.types; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class Item extends AbstractNameType{ + + private static final Map<String, Item> cache = new HashMap<String, Item>(); + + public Item(String name) { + super(name); + } + + public static Item get(String name) { + Item type = cache.get(name); + + if (type == null) { + type = new Item(name); + cache.put(name, type); + } + + return type; + } + + public static Collection<Item> getAllItems() { + return cache.values(); + } +} diff --git a/src/ac/ed/lurg/types/LandCoverType.java b/src/ac/ed/lurg/types/LandCoverType.java new file mode 100644 index 0000000000000000000000000000000000000000..b2b89867d29c41770bfe50eccd6d75291e7cb800 --- /dev/null +++ b/src/ac/ed/lurg/types/LandCoverType.java @@ -0,0 +1,11 @@ +package ac.ed.lurg.types; + + +public enum LandCoverType { + LAND, + ARABLE, + FOREST, + PERM_CROP, + PASTURE +} + diff --git a/src/ac/ed/lurg/types/ModelFitType.java b/src/ac/ed/lurg/types/ModelFitType.java new file mode 100644 index 0000000000000000000000000000000000000000..2afd0d8831e8e1d790ee93a77a3ae3c2aa0634a5 --- /dev/null +++ b/src/ac/ed/lurg/types/ModelFitType.java @@ -0,0 +1,24 @@ +package ac.ed.lurg.types; + +import java.util.HashMap; +import java.util.Map; + +public class ModelFitType extends AbstractNameType { + + private static final Map<String, ModelFitType> cache = new HashMap<String, ModelFitType>(); + + private ModelFitType(String name) { + super(name); + } + + public static ModelFitType get(String name) { + ModelFitType type = cache.get(name); + + if (type == null) { + type = new ModelFitType(name); + cache.put(name, type); + } + + return type; + } +} diff --git a/src/ac/ed/lurg/utils/LogWriter.java b/src/ac/ed/lurg/utils/LogWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..9031eb8f87354f5f8ab4df103464b0dbb89bab8b --- /dev/null +++ b/src/ac/ed/lurg/utils/LogWriter.java @@ -0,0 +1,73 @@ +package ac.ed.lurg.utils; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; + +import ac.ed.lurg.ModelConfig; + +public class LogWriter { + + private static LogWriter logWriter; + private BufferedWriter logFile; + private boolean suppressStd; + + public static synchronized void println(String s) { + getLogWriter().print(s, System.out, true); + } + + public static synchronized void print(String s) { + getLogWriter().print(s, System.out, false); + } + + public static synchronized void printlnError(String s) { + getLogWriter().print(s, System.err, true); + } + + private static LogWriter getLogWriter() { + if (logWriter == null) + logWriter = new LogWriter(ModelConfig.OUTPUT_DIR + File.separator + "log.txt"); + + return logWriter; + } + + private LogWriter(String outputFileName) { + FileWriter fstream; + try { + fstream = new FileWriter(outputFileName,false); + logFile = new BufferedWriter(fstream); + suppressStd = ModelConfig.SUPPRESS_STD_OUTPUT; + } + catch (IOException e) { + e.printStackTrace(); + } + } + + private void print(String s, PrintStream stream, boolean newLine) { + try { + logFile.write(s); + if (newLine) logFile.newLine(); + logFile.flush(); + } + catch (IOException e) { + e.printStackTrace(); + } + + if (!suppressStd) { + stream.print(s); + if (newLine) stream.println(); + } + } + + // probably not called, but never mind as keep stream flushed + protected void finalize() { + try { + logFile.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/ac/ed/lurg/yield/DummyYieldManager.java b/src/ac/ed/lurg/yield/DummyYieldManager.java new file mode 100644 index 0000000000000000000000000000000000000000..d4dbd05bbf408f456b67cd4417f3ff8011af03d2 --- /dev/null +++ b/src/ac/ed/lurg/yield/DummyYieldManager.java @@ -0,0 +1,19 @@ +package ac.ed.lurg.yield; + +import ac.ed.lurg.country.Country; +import ac.ed.lurg.types.Item; + +public class DummyYieldManager implements YieldManager { + + @Override + public double getRefYield(Country country, int year, Item commodity) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String getModelAndScenarioDescription() { + return "Dummy yield model"; + } + +} diff --git a/src/ac/ed/lurg/yield/YieldManager.java b/src/ac/ed/lurg/yield/YieldManager.java new file mode 100644 index 0000000000000000000000000000000000000000..2b7ee8d92e9771408455c1e8a23fc8867d76da7d --- /dev/null +++ b/src/ac/ed/lurg/yield/YieldManager.java @@ -0,0 +1,12 @@ +package ac.ed.lurg.yield; + +import ac.ed.lurg.country.Country; +import ac.ed.lurg.types.Item; + +public interface YieldManager { + + public double getRefYield(Country country, int year, Item commodity); + + public String getModelAndScenarioDescription(); + +}