From 8d8be0e19933669a44308995f0121e751ee1b47a Mon Sep 17 00:00:00 2001
From: s1924442 <b.arendarczyk@sms.ed.ac.uk>
Date: Fri, 12 Mar 2021 18:16:20 +0000
Subject: [PATCH] Bug fixes. Code cleaned up.

---
 GAMS/IntExtOpt.gms                            | 15 ++++++--
 src/ac/ed/lurg/ModelMain.java                 |  5 ++-
 src/ac/ed/lurg/country/CountryAgent.java      |  4 ++
 .../country/gams/GamsLocationOptimiser.java   | 37 ++++++-------------
 .../ed/lurg/country/gams/GamsRasterInput.java |  1 -
 .../country/gams/GamsRasterOptimiser.java     | 10 -----
 src/ac/ed/lurg/landuse/LandUseItem.java       | 20 +---------
 src/ac/ed/lurg/landuse/WoodYieldReader.java   |  1 -
 src/ac/ed/lurg/types/LandCoverType.java       | 26 ++++++++-----
 src/ac/ed/lurg/utils/DoubleMap.java           |  4 +-
 src/ac/ed/lurg/utils/NestedMap.java           |  3 --
 src/ac/ed/lurg/utils/TripleMap.java           |  3 +-
 12 files changed, 49 insertions(+), 80 deletions(-)

diff --git a/GAMS/IntExtOpt.gms b/GAMS/IntExtOpt.gms
index 7cad291a..e4911ffe 100644
--- a/GAMS/IntExtOpt.gms
+++ b/GAMS/IntExtOpt.gms
@@ -120,7 +120,7 @@ $gdxin
 *PARAMETER lcTransitionCost(lc_type_previous, lc_type, location) cost of land transitions including carbon cost and conversion cost;
 *lcTransitionCost(lc_type_previous, lc_type, location) = carbonFluxRate(lc_type_previous, lc_type, location) * carbonPrice + agriExpansionCost(location);
 
- SCALAR forestExpansionMaxRate / 0.01 /;
+ SCALAR forestExpansionMaxRate / 0.05 /;
 
  VARIABLES
        cropArea(crop, location)             total area for crops - Mha
@@ -144,12 +144,16 @@ $gdxin
        woodHarvest(location)              total wood harvested
        carbonFlux(location)               total carbon flux
        totalFeedDM
+*       A                                  artificial variable "https://www.gams.com/blog/2017/07/misbehaving-model-infeasible/"
        total_cost                         total cost of domestic supply including net imports;
 
+
  POSITIVE VARIABLE cropArea, fertI, irrigI, otherIntensity, ruminantFeed, monogastricFeed, importAmount, exportAmount,
                    agriLandExpansion, cropIncrease, cropDecrease, pastureDecrease, pastureIncrease, totalFeedDM,
                    landCoverArea, landCoverChange, woodHarvest;
 
+* POSITIVE VARIABLE A;
+
  EQUATIONS
        UNIT_COST_EQ(crop, location)                     cost per area - $1000 per ha or $billion per Mha
        YIELD_EQ(crop, location)                         yield given chosen intensity - tonnes per hectare
@@ -223,7 +227,10 @@ $gdxin
 
  SETASIDE_AREA_CALC(location) .. cropArea('setaside', location) =E= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) * setAsideRate;
 
- TOTAL_LAND_CHANGE_CONSTRAINT(location) .. suitableLandArea(location) =G= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) / (1.0 - unhandledCropRate) + cropArea('pasture', location);
+* TOTAL_LAND_CHANGE_CONSTRAINT(location) .. suitableLandArea(location) =G= sum(crop_less_pasture, cropArea(crop_less_pasture, location)) / (1.0 - unhandledCropRate) + cropArea('pasture', location);
+
+* Needs inequality due to floating point errors
+ TOTAL_LAND_CHANGE_CONSTRAINT(location) .. suitableLandArea(location) =G= sum(land_cover, landCoverArea(land_cover, location));
 
  MAX_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =L= maxNetImport(import_crop);
  MIN_NET_IMPORT_CONSTRAINT(import_crop) .. importAmount(import_crop) - exportAmount(import_crop) =G= minNetImport(import_crop);
@@ -254,9 +261,9 @@ $gdxin
 
  FOREST_EXPANSION_CONSTRAINT(forest, location) .. landCoverArea(forest, location) - previousLandCoverArea(forest, location) =L= suitableLandArea(location) * forestExpansionMaxRate;
 
-* MINIMUM_LAND_COVER_CONSTRAINT(land_cover, location) .. landCoverArea(land_cover, location) =G= minimumLandCoverArea(land_cover, location);
 * minimum of minimumLandCoverArea and previousLandCoverArea corrects for floating point errors
-  MINIMUM_LAND_COVER_CONSTRAINT(land_cover, location) .. landCoverChange(land_cover, land_cover, location) =G= min(minimumLandCoverArea(land_cover, location), previousLandCoverArea(land_cover, location));
+*  MINIMUM_LAND_COVER_CONSTRAINT(land_cover, location) .. landCoverChange(land_cover, land_cover, location) =G= min(minimumLandCoverArea(land_cover, location), previousLandCoverArea(land_cover, location));
+ MINIMUM_LAND_COVER_CONSTRAINT(land_cover, location) .. landCoverChange(land_cover, land_cover, location) =G= minimumLandCoverArea(land_cover, location);
 
  WOOD_HARVEST_CALC(location) .. woodHarvest(location) =E= sum((land_cover_before, land_cover_after), landCoverChange(land_cover_before, land_cover_after, location) * woodYield(land_cover_before, land_cover_after, location));
 
diff --git a/src/ac/ed/lurg/ModelMain.java b/src/ac/ed/lurg/ModelMain.java
index 66294c60..4bc0ff98 100644
--- a/src/ac/ed/lurg/ModelMain.java
+++ b/src/ac/ed/lurg/ModelMain.java
@@ -141,8 +141,7 @@ public class ModelMain {
 		double gen2Increase = (gen2EcDDemand>previousGen2EcDDemand) ? gen2EcDDemand - previousGen2EcDDemand : 0.0;
 		
 		CarbonFluxRasterSet currentCarbonFluxData = getCarbonFluxData(timestep);
-		LogWriter.println("Carbon flux data read");
-		LogWriter.println("First line: " + currentCarbonFluxData.get(1, 1).getCarbonFluxes());
+		
 		WoodYieldRasterSet currentWoodYieldData = getWoodYieldData(timestep);
 		
 		countryAgents.determineProductionForAll(timestep, yieldSurfaces, currentIrrigationData, gen2Increase, currentCarbonFluxData, currentWoodYieldData);
@@ -666,6 +665,7 @@ public class ModelMain {
 	private CarbonFluxRasterSet getCarbonFluxData(Timestep timestep) {
 		CarbonFluxRasterSet carbonFluxData = new CarbonFluxRasterSet(desiredProjection);
 		new CarbonFluxReader(carbonFluxData).getRasterDataFromFile(timestep.getYearSubDir(ModelConfig.FOREST_DIR) + File.separator + "cflux_net.out");
+		LogWriter.println("Carbon flux data read");
 		return carbonFluxData;
 	}
 	
@@ -673,6 +673,7 @@ public class ModelMain {
 	private WoodYieldRasterSet getWoodYieldData(Timestep timestep) {
 		WoodYieldRasterSet woodYieldData = new WoodYieldRasterSet(desiredProjection);
 		new WoodYieldReader(woodYieldData).getRasterDataFromFile(timestep.getYearSubDir(ModelConfig.FOREST_DIR) + File.separator + "wood_yield.out");
+		LogWriter.println("Wood yield data read");
 		return woodYieldData;
 	}
 
diff --git a/src/ac/ed/lurg/country/CountryAgent.java b/src/ac/ed/lurg/country/CountryAgent.java
index 0ab01089..6c55218a 100644
--- a/src/ac/ed/lurg/country/CountryAgent.java
+++ b/src/ac/ed/lurg/country/CountryAgent.java
@@ -160,6 +160,10 @@ public class CountryAgent extends AbstractCountryAgent {
 					FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + "_gams_java_gdb2.gdx"),
 					FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + country.getName().replaceAll("\\s+","") + currentTimestep.getYear() + ext + ".gdx"),
 					StandardCopyOption.REPLACE_EXISTING);
+			Files.copy(
+					FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + "_gams_java_gjo1.lst"),
+					FileSystems.getDefault().getPath(ModelConfig.TEMP_DIR + File.separator + country.getName().replaceAll("\\s+","") + currentTimestep.getYear() + ext + "Listing" + ".lst"),
+					StandardCopyOption.REPLACE_EXISTING);
 		} catch (IOException e) {
 			LogWriter.print(e);
 		}
diff --git a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
index 8fa74210..6844c309 100644
--- a/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsLocationOptimiser.java
@@ -31,7 +31,6 @@ import ac.ed.lurg.landuse.LandUseItem;
 import ac.ed.lurg.landuse.WoodYieldItem;
 import ac.ed.lurg.types.CommodityType;
 import ac.ed.lurg.types.CropType;
-import ac.ed.lurg.types.GamsLandCoverType;
 import ac.ed.lurg.types.LandCoverType;
 import ac.ed.lurg.types.Parameter;
 import ac.ed.lurg.types.YieldType;
@@ -126,7 +125,7 @@ public class GamsLocationOptimiser {
 			double suitableLand = landUseItem.getSuitableArea();
 			totalSuitable += suitableLand;
 			if (DEBUG) LogWriter.println(String.format("  %d   %15s,\t %.3f", locationId, "suitableLand", suitableLand));
-			setGamsParamValueTruncate(landP.addRecord(locString), suitableLand, 3);
+			setGamsParamValueTruncate(landP.addRecord(locString), suitableLand, 5);
 			
 			double agriExpansionCost = ModelConfig.AGRI_EXPANSION_COST_BASE + landUseItem.getForestManagedFraction() * ModelConfig.AGRI_EXPANSION_COST_BASE_MANAGED_FOREST;
 			if (DEBUG) LogWriter.println(String.format("  %d   %15s,\t %.3f", locationId, "agriExpansionCost", agriExpansionCost));
@@ -176,33 +175,13 @@ public class GamsLocationOptimiser {
 			}
 			
 			// Previous land covers
-			// TODO Increase number of decimal places?
 			for (LandCoverType lc : LandCoverType.getConvertibleTypes()) {
 				Vector<String> v = new Vector<String>();
 				v.add(lc.getName());
 				v.add(locString);
 				setGamsParamValueTruncate(prevLandCoverP.addRecord(v), landUseItem.getUnprotectedLandCoverArea(lc), 5);
 			}
-			
-			/*
-			Vector<String> v = new Vector<String>();
-			
-			v.add("cropland");
-			v.add(locString);
-			setGamsParamValueTruncate(prevLandCoverP.addRecord(v), landUseItem.getUnprotectedLandCoverArea(LandCoverType.CROPLAND), 3);
-			
-			v.set(0, "pasture");
-			setGamsParamValueTruncate(prevLandCoverP.addRecord(v), landUseItem.getUnprotectedLandCoverArea(LandCoverType.PASTURE), 3);
-			
-			v.set(0, "timberForest");
-			setGamsParamValueTruncate(prevLandCoverP.addRecord(v), landUseItem.getUnprotectedLandCoverArea(LandCoverType.TIMBER_FOREST), 3);
-			
-			v.set(0, "carbonForest");
-			setGamsParamValueTruncate(prevLandCoverP.addRecord(v), landUseItem.getUnprotectedLandCoverArea(LandCoverType.CARBON_FOREST), 3);
-			
-			v.set(0, "natural");
-			setGamsParamValueTruncate(prevLandCoverP.addRecord(v), landUseItem.getUnprotectedLandCoverArea(LandCoverType.OTHER_NATURAL) + landUseItem.getUnprotectedLandCoverArea(LandCoverType.UNMANAGED_FOREST), 3);
-			*/		
+				
 		}
 		
 		if (DEBUG) LogWriter.println(String.format("  Total agricultural %.1f,\t suitable %.1f", totalAgriLand, totalSuitable));
@@ -417,9 +396,14 @@ public class GamsLocationOptimiser {
 			
 			for (Entry<LandCoverType, Double> coverMap : locMap.getValue().entrySet()) {
 				Vector<String> v = new Vector<String>();
-				v.add(coverMap.getKey().getName());
+				LandCoverType landCover = coverMap.getKey();
+				v.add(landCover.getName());
 				v.add(locString);
-				setGamsParamValueTruncate(minimumLandCover.addRecord(v), coverMap.getValue(), 5);
+				double minCover = coverMap.getValue();
+				double previousCover = inputData.getPreviousLandUse().get(locationId).getUnprotectedLandCoverArea(landCover);
+				// Correction for floating point errors
+				double minCoverCorrected = Math.min(minCover, previousCover);
+				setGamsParamValueTruncate(minimumLandCover.addRecord(v), minCoverCorrected, 5);
 			}
 		}
 		
@@ -649,13 +633,14 @@ public class GamsLocationOptimiser {
 		return results;
 	}
 	
+	/*
 	private double getPrevUnmanagedForestProp(Integer locId) {
 		double prevOtherNaturalArea = inputData.getPreviousLandUse().get(locId).getUnprotectedLandCoverArea(LandCoverType.OTHER_NATURAL);
 		double prevUnmanagedForestArea = inputData.getPreviousLandUse().get(locId).getUnprotectedLandCoverArea(LandCoverType.UNMANAGED_FOREST);
 		double prevUnmanagedForestProp = (prevOtherNaturalArea + prevUnmanagedForestArea) != 0 ? prevUnmanagedForestArea / (prevUnmanagedForestArea + prevOtherNaturalArea) : 0;
 		return prevUnmanagedForestProp;
 	}
-	
+	*/
 
 	private double getParmValue(GAMSParameter aParm, String itemName) {
 		try {
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterInput.java b/src/ac/ed/lurg/country/gams/GamsRasterInput.java
index 1f655776..759107b3 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterInput.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterInput.java
@@ -5,7 +5,6 @@ import ac.ed.lurg.landuse.LandUseItem;
 import ac.ed.lurg.landuse.WoodYieldItem;
 import ac.ed.lurg.types.LandCoverType;
 import ac.ed.lurg.utils.DoubleMap;
-import ac.ed.lurg.utils.TripleMap;
 import ac.ed.lurg.landuse.CarbonFluxItem;
 import ac.ed.lurg.landuse.IrrigationItem;
 import ac.ed.lurg.yield.YieldRaster;
diff --git a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
index d732bf50..74fe24d2 100644
--- a/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
+++ b/src/ac/ed/lurg/country/gams/GamsRasterOptimiser.java
@@ -12,7 +12,6 @@ import ac.ed.lurg.landuse.IrrigationItem;
 import ac.ed.lurg.landuse.LandUseItem;
 import ac.ed.lurg.landuse.WoodYieldItem;
 import ac.ed.lurg.types.CropType;
-import ac.ed.lurg.types.GamsLandCoverType;
 import ac.ed.lurg.types.LandCoverType;
 import ac.ed.lurg.types.YieldType;
 import ac.ed.lurg.utils.DoubleMap;
@@ -219,11 +218,6 @@ public class GamsRasterOptimiser {
 
 			}	
 			
-			if (locId == 31) {
-				int foo = 1;
-			}
-				
-			
 			// Allocate land cover change to grid cells
 			for (Entry<LandCoverType, Map<LandCoverType, Double>> fromMap: landCoverChange.getMap().entrySet()) {
 				LandCoverType fromLC = fromMap.getKey();
@@ -416,10 +410,6 @@ public class GamsRasterOptimiser {
 				CarbonFluxItem aggCFlux = aggregatedCarbonFluxes.lazyGet(clusterId);
 				WoodYieldItem aggWYield = aggregatedWoodYields.lazyGet(clusterId);
 				
-				if (clusterId == 1) {
-					int foo = 1;
-				}
-				
 				landUseItem.updateSuitableArea(rasterInputData.getTimestep().getYear());
 				double suitableAreaThisTime  = landUseItem.getSuitableArea();
 				double suitableAreaSoFar = aggLandUse.getSuitableArea();
diff --git a/src/ac/ed/lurg/landuse/LandUseItem.java b/src/ac/ed/lurg/landuse/LandUseItem.java
index b6e60f82..07541d8f 100644
--- a/src/ac/ed/lurg/landuse/LandUseItem.java
+++ b/src/ac/ed/lurg/landuse/LandUseItem.java
@@ -1,17 +1,13 @@
 package ac.ed.lurg.landuse;
 
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Map.Entry;
-
 import ac.ed.lurg.ModelConfig;
 import ac.ed.lurg.types.CropToDouble;
 import ac.ed.lurg.types.CropType;
 import ac.ed.lurg.types.LandCoverType;
-import ac.ed.lurg.utils.DoubleMap;
 import ac.ed.lurg.utils.Interpolator;
 import ac.sac.raster.InterpolatingRasterItem;
 
@@ -22,7 +18,6 @@ public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serial
 	private Map<CropType, Double> cropFractions = new HashMap<CropType, Double>();
 	private Map<LandCoverType, Double> landCoverAreas = new HashMap<LandCoverType, Double>(); //Mha
 	private Map<LandCoverType, Double> unprotectedAreas = new HashMap<LandCoverType, Double>(); //Mha
-	//private DoubleMap<LandCoverType, Integer, Double> minimumLandCover = new DoubleMap<LandCoverType, Integer, Double>(); //Minimum land cover by year of expiry (Mha)
 	private double protectedArea; //protected area in Mha
 	private double unavailableArea; //area unavailable due to altitude etc 
 	private double suitableArea; //Mha
@@ -291,7 +286,7 @@ public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serial
 	public double getTotalUnprotectedNatural() {	
 		double unprotectedNatural = 0;
 		for (LandCoverType landType : LandCoverType.values()) {
-			if (landType.isProtectable()) {
+			if (landType.isNatural()) {
 				unprotectedNatural += unprotectedAreas.get(landType);
 				}
 		}
@@ -436,19 +431,6 @@ public class LandUseItem implements InterpolatingRasterItem<LandUseItem>, Serial
 		return total;
 	}
 	
-	/*
-	public double getMinimumLandCover(LandCoverType lcType, Integer year) {
-		double area = 0;
-		Map<Integer, Double> yearMap = minimumLandCover.getInnerMap(lcType);
-		for (Entry<Integer, Double> entry : yearMap.entrySet()) {
-			if (entry.getKey() > year) {
-				area += entry.getValue();
-			}
-		}
-		return area;
-	}
-	*/
-	
 	@Override
 	public String toString() {
 		return "LandUseItem: [landCoverAreas=" + landCoverAreas + ", protectedArea=" + protectedArea + ", unavailableArea=" + unavailableArea + "]";
diff --git a/src/ac/ed/lurg/landuse/WoodYieldReader.java b/src/ac/ed/lurg/landuse/WoodYieldReader.java
index eca499a9..76f92aaf 100644
--- a/src/ac/ed/lurg/landuse/WoodYieldReader.java
+++ b/src/ac/ed/lurg/landuse/WoodYieldReader.java
@@ -2,7 +2,6 @@ package ac.ed.lurg.landuse;
 
 import java.util.Map;
 
-import ac.ed.lurg.types.GamsLandCoverType;
 import ac.ed.lurg.types.LandCoverType;
 import ac.sac.raster.AbstractTabularRasterReader;
 import ac.sac.raster.RasterKey;
diff --git a/src/ac/ed/lurg/types/LandCoverType.java b/src/ac/ed/lurg/types/LandCoverType.java
index 93d1fd0a..2784a4f6 100644
--- a/src/ac/ed/lurg/types/LandCoverType.java
+++ b/src/ac/ed/lurg/types/LandCoverType.java
@@ -9,24 +9,26 @@ import ac.ed.lurg.utils.LogWriter;
 
 public enum LandCoverType {
 	
-	TIMBER_FOREST("timberForest", false, true, true),
-	CARBON_FOREST("carbonForest", false, true, true),
-	UNMANAGED_FOREST ("unmanagedForest", true, true, true),
-	OTHER_NATURAL ("otherNatural", true, true, false),
-	CROPLAND ("cropland", false, true, false),
-	PASTURE ("pasture", false, true, false),
-	BARREN ("barren", false, false, false),
-	URBAN("urban", false, false, false);
+	TIMBER_FOREST("timberForest", false, true, true, true),
+	CARBON_FOREST("carbonForest", false, true, true, true),
+	UNMANAGED_FOREST ("unmanagedForest", true, true, true, true),
+	OTHER_NATURAL ("otherNatural", true, true, true, false),
+	CROPLAND ("cropland", false, true, false, false),
+	PASTURE ("pasture", false, true, false, false),
+	BARREN ("barren", false, false, false, false),
+	URBAN("urban", false, false, false, false);
 
 	private String name;
 	private boolean isProtectable;
 	private boolean isConvertible;
+	private boolean isNatural;
 	private boolean isForest;
 	
-	LandCoverType(String name, boolean isProtectable, boolean isConvertible, boolean isForest) {
+	LandCoverType(String name, boolean isProtectable, boolean isConvertible, boolean isNatural, boolean isForest) {
 		this.name = name;
 		this.isProtectable = isProtectable;
 		this.isConvertible = isConvertible;
+		this.isNatural = isNatural;
 		this.isForest = isForest;
 	}
 	
@@ -38,6 +40,10 @@ public enum LandCoverType {
 		return isProtectable;
 	}
 	
+	public boolean isNatural() {
+		return isNatural;
+	}
+	
 	private static final Map<String, LandCoverType> nameCache = new HashMap<String, LandCoverType>();
     static {
         for (LandCoverType c : values()) {
@@ -80,6 +86,7 @@ public enum LandCoverType {
 
 	}
 	
+	/*
 	public static Collection<LandCoverType> getConvertibleNonForestTypes() {
 
 		Collection<LandCoverType> convNonforestTypes = new HashSet<LandCoverType>();
@@ -91,5 +98,6 @@ public enum LandCoverType {
 		return convNonforestTypes;
 
 	}
+	*/
 }
 
diff --git a/src/ac/ed/lurg/utils/DoubleMap.java b/src/ac/ed/lurg/utils/DoubleMap.java
index 14f7fdcf..8a50e7c0 100644
--- a/src/ac/ed/lurg/utils/DoubleMap.java
+++ b/src/ac/ed/lurg/utils/DoubleMap.java
@@ -6,7 +6,7 @@ import java.util.Map;
 public class DoubleMap<K, L, V> {
 	
 	// Stores a double mapped by two consecutive keys
-	Map<K, Map<L, Double>> theMap; 
+	private Map<K, Map<L, Double>> theMap; 
 	
 	public DoubleMap() {
 		this.theMap = new HashMap<K, Map<L, Double>>(); 
@@ -50,7 +50,5 @@ public class DoubleMap<K, L, V> {
 		} else {
 			put(key1, key2, value);
 		}
-		
-
 	}
 }
diff --git a/src/ac/ed/lurg/utils/NestedMap.java b/src/ac/ed/lurg/utils/NestedMap.java
index 397f686f..4fc2bebc 100644
--- a/src/ac/ed/lurg/utils/NestedMap.java
+++ b/src/ac/ed/lurg/utils/NestedMap.java
@@ -1,9 +1,6 @@
 package ac.ed.lurg.utils;
 
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.Map;
-import java.util.List;
 
 public class NestedMap<K, V> {
 
diff --git a/src/ac/ed/lurg/utils/TripleMap.java b/src/ac/ed/lurg/utils/TripleMap.java
index 11c6cf37..aa9e32dd 100644
--- a/src/ac/ed/lurg/utils/TripleMap.java
+++ b/src/ac/ed/lurg/utils/TripleMap.java
@@ -5,7 +5,7 @@ import java.util.Map;
 
 public class TripleMap<K, L, M, V> {
 	
-	Map<K, DoubleMap<L, M, V >> theMap = new HashMap<K, DoubleMap<L, M, V>>(); 
+	private Map<K, DoubleMap<L, M, V >> theMap = new HashMap<K, DoubleMap<L, M, V>>(); 
 	
 	public TripleMap() {
 		
@@ -21,7 +21,6 @@ public class TripleMap<K, L, M, V> {
 		}
 	} 
 
-
 	public Double get(K key1, L key2, M key3) {
 		if (theMap.containsKey(key1)) {
 			return theMap.get(key1).get(key2, key3);
-- 
GitLab