public static OAConfig readConfigFile(String filename)
	{
		OAConfig config = new OAConfig();
		
		File file = new File(filename);

		// Check that file exists

		if(!file.exists())
		{
			System.out.println("Config file " + filename + " does not exist");
			return null;
		}

		// Read each line from the config file, setting the relevent variables

		try
		{
			BufferedReader in = new BufferedReader(new FileReader(file));

			config.setNumCells(Integer.parseInt(readNextLine(in)));
			config.setNumClones(Integer.parseInt(readNextLine(in)));
			config.setMaxIter(Integer.parseInt(readNextLine(in)));
			config.setSuppThres(Double.parseDouble(readNextLine(in)));
			config.setErrorThres(Double.parseDouble(readNextLine(in)));
			config.setDivRatio(Double.parseDouble(readNextLine(in)));
			config.setMutnParam(Double.parseDouble(readNextLine(in)));
			config.setNumDimensions(Integer.parseInt(readNextLine(in)));

			double[] lowerBounds = new double[config.getNumDimensions()];
			double[] upperBounds = new double[config.getNumDimensions()];

			// Specific bounds for each dimension must be provided

			for (int i = 0; i < config.getNumDimensions(); i++)
			{
				String s = readNextLine(in);

				if(s == null)
				{
					System.out.println("Upper and lower bounds for each dimension must be provided");
					return null;
				}

				// Split the input string in two using the "," character

				String[] bits = s.split(",");
				lowerBounds[i] = Double.parseDouble(bits[0]);
				upperBounds[i] = Double.parseDouble(bits[1]);
			}
			
			config.setLowerBounds(lowerBounds);
			config.setUpperBounds(upperBounds);
		} catch (Exception e)
		{
			System.out.println("Error opening config file " + filename);
			return null;
		}

		return config;
	}