void GenerateEvolutionMatrix(
	int nK,
	const Parameters & param,
	DataMatrix<double> & matM,
	DataMatrix<double> & matB
) {
	int nMatrixSize = 5 * param.nPhiElements - 1;

	matM.Initialize(nMatrixSize, nMatrixSize);
	matB.Initialize(nMatrixSize, nMatrixSize);

	// Radius of the Earth
	const double ParamEarthRadius = 6.37122e6;

	// Ideal gas constant
	const double ParamRd = 287.0;

	// Inverse Rossby number
	double dInvRo =
		2.0 * ParamEarthRadius * param.dOmega * param.dXscale / param.dU0;

	// Scale height
	double dH = ParamRd * param.dT0 / param.dG;

	// Froude number
	double dFr = param.dU0 / sqrt(param.dG * dH);

	// Squared Froude number
	double dFr2 = dFr * dFr;

	// Aspect ratio
	double dAs = dH / (ParamEarthRadius / param.dXscale);

	// Velocity ratio
	double dAv = dAs;

	// Wave number
	double dK2 = static_cast<double>(nK * nK);

	// Gamma
	double dInvGamma = 1.0 / param.dGamma;

	// Grid spacing
	double dDeltaPhi = param.vecNode[1] - param.vecNode[0];

	// Loop through all elements
	for (int j = 0; j < param.nPhiElements; j++) {

		// Index of the start of this block
		int ix = 4 * j;
		int ixU = ix;
		int ixP = ix + 1;
		int ixW = ix + 2;
		int ixR = ix + 3;

		int ixVL = 4 * param.nPhiElements + j - 1;
		int ixVR = 4 * param.nPhiElements + j;

		// Phi at this node
		double dPhi = param.vecNode[j];

		// Cosine Phi
		double dCosPhi = cos(dPhi);

		// Sin Phi
		double dSinPhi = sin(dPhi);

		// Tan Phi
		double dTanPhi = tan(dPhi);

		// U evolution equations
		matM[ixU][ixU] = dFr2 * dCosPhi * dCosPhi;
		matM[ixP][ixU] = 1.0;

		if (j != 0) {
			matM[ixVL][ixU] = - 0.5 * dFr2 * (2.0 + dInvRo) * dSinPhi * dCosPhi;
		}
		if (j != param.nPhiElements - 1) {
			matM[ixVR][ixU] = - 0.5 * dFr2 * (2.0 + dInvRo) * dSinPhi * dCosPhi;
		}

		// V evolution equations
		if (j != 0) {
			int ixV = ixVL;

			int ixUL = ix - 4;
			int ixPL = ix - 3;
			int ixRL = ix - 1;

			int ixUR = ix;
			int ixPR = ix + 1;
			int ixRR = ix + 3;

			double dPhiS = param.vecEdge[j];
			double dSinPhiS = sin(dPhiS);
			double dCosPhiS = cos(dPhiS);

			matM[ixUL][ixV] = 0.5 * dFr2 * (2.0 + dInvRo) * dSinPhiS * dCosPhiS;
			matM[ixUR][ixV] = 0.5 * dFr2 * (2.0 + dInvRo) * dSinPhiS * dCosPhiS;

			matM[ixV][ixV] = - dK2 * dFr2;

			matM[ixPL][ixV] =
				- 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhiS * dCosPhiS
				- 1.0 / dDeltaPhi;
			matM[ixPR][ixV] =
				- 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhiS * dCosPhiS
				+ 1.0 / dDeltaPhi;

			matM[ixRL][ixV] = 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhiS * dCosPhiS;
			matM[ixRR][ixV] = 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhiS * dCosPhiS;
		}

		// P evolution equations
		matM[ixU][ixP] = 1.0;
		matM[ixR][ixP] = 1.0;

		if (j != 0) {
			matM[ixVL][ixP] =
				- 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhi * dCosPhi
				- 0.5 * dTanPhi
				- 1.0 / dDeltaPhi;
		}
		if (j != param.nPhiElements - 1) {
			matM[ixVR][ixP] =
				- 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhi * dCosPhi
				- 0.5 * dTanPhi
				+ 1.0 / dDeltaPhi;
		}

		// W evolution equation
		matM[ixW][ixW] = - dK2 * dAs * dAv * dFr2;
		matM[ixR][ixW] = 1.0;

		// R evolution equation
		matM[ixP][ixR] = dInvGamma / (1.0 - dInvGamma);
		matM[ixW][ixR] = dAv / dAs;
		matM[ixR][ixR] = - 1.0 / (1.0 - dInvGamma);

		if (j != 0) {
			matM[ixVL][ixR] = 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhi * dCosPhi;
		}
		if (j != param.nPhiElements - 1) {
			matM[ixVR][ixR] = 0.5 * dFr2 * (1.0 + dInvRo) * dSinPhi * dCosPhi;
		}

		// B matrix coefficients
		matB[ixP][ixW] = -1.0;
		matB[ixW][ixP] = -1.0;
	}
}
int main(int argc, char ** argv) {

	MPI_Init(&argc, &argv);

try {

	// Number of latitudes
	int nLat;

	// Number of longitudes
	int nLon;

	// Zonal wavenumber
	int nK;

	// Meridional power
	int nLpow;

	// Output file
	std::string strOutputFile;

	// Parse the command line
	BeginCommandLine()
		CommandLineInt(nLat, "lat", 40);
		CommandLineInt(nLon, "lon", 80);
		CommandLineString(strOutputFile, "out", "topo.nc");

		ParseCommandLine(argc, argv);
	EndCommandLine(argv)

	// Generate longitude and latitude arrays
	AnnounceBanner();
	AnnounceStartBlock("Generating longitude and latitude arrays");

	DataVector<double> dLon;
	dLon.Initialize(nLon);

	Parameters param;
	param.GenerateLatituteArray(nLat);

	std::vector<double> & dLat = param.vecNode;

	double dDeltaLon = 2.0 * M_PI / static_cast<double>(nLon);
	for (int i = 0; i < nLon; i++) {
		dLon[i] = (static_cast<double>(i) + 0.5) * dDeltaLon;
	}

	AnnounceEndBlock("Done");

	// Open NetCDF output file
	AnnounceStartBlock("Writing to file");

	NcFile ncdf_out(strOutputFile.c_str(), NcFile::Replace);

	// Output coordinates
	NcDim * dimLat = ncdf_out.add_dim("lat", nLat);
	NcDim * dimLon = ncdf_out.add_dim("lon", nLon);

	NcVar * varLat = ncdf_out.add_var("lat", ncDouble, dimLat);
	varLat->set_cur((long)0);
	varLat->put(&(param.vecNode[0]), nLat);

	NcVar * varLon = ncdf_out.add_var("lon", ncDouble, dimLon);
	varLon->set_cur((long)0);
	varLon->put(&(dLon[0]), nLon);

	// Generate topography
	DataMatrix<double> dTopo;
	dTopo.Initialize(nLat, nLon);

	double dK = static_cast<double>(nK);
	double dLpow = static_cast<double>(nLpow);

	double dA = 6.37122e6;
	double dX = 500.0;
	double dLatM = 0.0;
	double dLonM = M_PI / 4.0;
	double dD = 5000.0;
	double dH0 = 1.0;
	double dXiM = 4000.0;

	for (int j = 0; j < nLat; j++) {
	for (int i = 0; i < nLon; i++) {

		// Great circle distance
		double dR = dA / dX * acos(sin(dLatM) * sin(dLat[j])
			+ cos(dLatM) * cos(dLat[j]) * cos(dLon[i] - dLonM));

		double dCosXi = 1.0; //cos(M_PI * dR / dXiM);

		dTopo[j][i] = dH0 * exp(- dR * dR / (dD * dD))
			* dCosXi * dCosXi;
	}
	}

	// Write topography
	NcVar * varZs = ncdf_out.add_var("Zs", ncDouble, dimLat, dimLon);
	varZs->set_cur(0, 0);
	varZs->put(&(dTopo[0][0]), nLat, nLon);

	AnnounceEndBlock("Done");
	Announce("Completed successfully!");
	AnnounceBanner();

} catch(Exception & e) {
	Announce(e.ToString().c_str());
}
	MPI_Finalize();
}
int main(int argc, char** argv) {

	NcError error(NcError::verbose_nonfatal);

try {

	// Input file
	std::string strInputFile;

	// Input file list
	std::string strInputFileList;

	// Input file format
	std::string strInputFormat;

	// NetCDF file containing latitude and longitude arrays
	std::string strLatLonFile;

	// Output file (NetCDF)
	std::string strOutputFile;

	// Output variable name
	std::string strOutputVariable;

	// Column in which the longitude index appears
	int iLonIxCol;

	// Column in which the latitude index appears
	int iLatIxCol;

	// Begin latitude
	double dLatBegin;

	// End latitude
	double dLatEnd;

	// Begin longitude
	double dLonBegin;

	// End longitude
	double dLonEnd;

	// Number of latitudes in output
	int nLat;

	// Number of longitudes in output
	int nLon;

	// Parse the command line
	BeginCommandLine()
		CommandLineString(strInputFile, "in", "");
		CommandLineString(strInputFileList, "inlist", "");
		CommandLineStringD(strInputFormat, "in_format", "std", "(std|visit)");
		CommandLineString(strOutputFile, "out", "");
		CommandLineString(strOutputVariable, "outvar", "density");
		CommandLineInt(iLonIxCol, "iloncol", 8);
		CommandLineInt(iLatIxCol, "ilatcol", 9);
		CommandLineDouble(dLatBegin, "lat_begin", -90.0);
		CommandLineDouble(dLatEnd, "lat_end", 90.0);
		CommandLineDouble(dLonBegin, "lon_begin", 0.0);
		CommandLineDouble(dLonEnd, "lon_end", 360.0);
		CommandLineInt(nLat, "nlat", 180);
		CommandLineInt(nLon, "nlon", 360);

		ParseCommandLine(argc, argv);
	EndCommandLine(argv)

	AnnounceBanner();

	// Check input
	if ((strInputFile == "") && (strInputFileList == "")) {
		_EXCEPTIONT("No input file (--in) or (--inlist) specified");
	}
	if ((strInputFile != "") && (strInputFileList != "")) {
		_EXCEPTIONT("Only one input file (--in) or (--inlist) allowed");
	}
	if (strInputFormat != "std") {
		_EXCEPTIONT("UNIMPLEMENTED:  Only \"--in_format std\" supported");
	}

	// Check output
	if (strOutputFile == "") {
		_EXCEPTIONT("No output file (--out) specified");
	}

	// Check output variable
	if (strOutputVariable == "") {
		_EXCEPTIONT("No output variable name (--outvar) specified");
	}

	// Number of latitudes and longitudes
	if (nLat == 0) {
		_EXCEPTIONT("UNIMPLEMENTED: --nlat must be specified currently");
	}
	if (nLon == 0) {
		_EXCEPTIONT("UNIMPLEMENTED: --nlon must be specified currently");
	}

	// Input file list
	std::vector<std::string> vecInputFiles;

	if (strInputFile != "") {
		vecInputFiles.push_back(strInputFile);
	}
	if (strInputFileList != "") {
		GetInputFileList(strInputFileList, vecInputFiles);
	}

	int nFiles = vecInputFiles.size();

	// Density
	DataMatrix<int> nCounts;
	nCounts.Initialize(nLat, nLon);

	// Loop through all files in list
	AnnounceStartBlock("Processing files");

	std::string strBuffer;
	strBuffer.reserve(1024);

	for (int f = 0; f < nFiles; f++) {
		Announce("File \"%s\"", vecInputFiles[f].c_str());

		FILE * fp = fopen(vecInputFiles[f].c_str(), "r");
		if (fp == NULL) {
			_EXCEPTION1("Unable to open input file \"%s\"",
				vecInputFiles[f].c_str());
		}

		for (;;) {

			// Read in the next line
			fgets(&(strBuffer[0]), 1024, fp);

			int nLength = strlen(&(strBuffer[0]));

			// Check for end of file
			if (feof(fp)) {
				break;
			}

			// Check for comment line
			if (strBuffer[0] == '#') {
				continue;
			}

			// Check for new storm
			if (strncmp(&(strBuffer[0]), "start", 5) == 0) {
				continue;
			}

			// Parse line
			double dLon;
			double dLat;

			int iCol = 0;
			int iLast = 0;

			bool fWhitespace = true;

			for (int i = 0; i <= nLength; i++) {
				if ((strBuffer[i] == ' ') ||
					(strBuffer[i] == ',') ||
					(strBuffer[i] == '\t') ||
					(strBuffer[i] == '\0')
				) {
					if (!fWhitespace) {
						if (iCol == iLonIxCol) {
							strBuffer[i] = '\0';
							dLon = atof(&(strBuffer[iLast]));
						}
						if (iCol == iLatIxCol) {
							strBuffer[i] = '\0';
							dLat = atof(&(strBuffer[iLast]));
						}
					}

					fWhitespace = true;

				} else {
					if (fWhitespace) {
						iLast = i;
						iCol++;
					}
					fWhitespace = false;
				}
			}

			// Latitude and longitude index
			int iLon =
				static_cast<int>(static_cast<double>(nLon)
					* (dLon - dLonBegin) / (dLonEnd - dLonBegin));
			int iLat =
				static_cast<int>(static_cast<double>(nLat)
					* (dLat - dLatBegin) / (dLatEnd - dLatBegin));

			if (iLon == (-1)) {
				iLon = 0;
			}
			if (iLon == nLon) {
				iLon = nLon - 1;
			}
			if (iLat == (-1)) {
				iLat = 0;
			}
			if (iLat == nLat) {
				iLat = nLat - 1;
			}

			if ((iLat < 0) || (iLat >= nLat)) {
				_EXCEPTION1("Latitude index (%i) out of range", iLat);
			}
			if ((iLon < 0) || (iLon >= nLon)) {
				_EXCEPTION1("Longitude index (%i) out of range", iLon);
			}

			nCounts[iLat][iLon]++;
		}

		fclose(fp);
	}

	AnnounceEndBlock("Done");

	// Output results
	AnnounceStartBlock("Output results");

	// Load the netcdf output file
	NcFile ncOutput(strOutputFile.c_str(), NcFile::Replace);
	if (!ncOutput.is_valid()) {
		_EXCEPTION1("Unable to open output file \"%s\"",
			strOutputFile.c_str());
	}

	// Create output
	NcDim * dimLat = ncOutput.add_dim("lat", nLat);
	NcDim * dimLon = ncOutput.add_dim("lon", nLon);

	NcVar * varLat = ncOutput.add_var("lat", ncDouble, dimLat);
	NcVar * varLon = ncOutput.add_var("lon", ncDouble, dimLon);

	varLat->add_att("units", "degrees_north");
	varLon->add_att("units", "degrees_east");

	DataVector<double> dLat(nLat);
	DataVector<double> dLon(nLon);

	for (int j = 0; j < nLat; j++) {
		dLat[j] = dLatBegin
			+ (dLatEnd - dLatBegin)
				* (static_cast<double>(j) + 0.5)
				/ static_cast<double>(nLat);
	}
	for (int i = 0; i < nLon; i++) {
		dLon[i] = dLonBegin
			+ (dLonEnd - dLonBegin)
			* (static_cast<double>(i) + 0.5)
			/ static_cast<double>(nLon);
	}

	varLat->put(&(dLat[0]), nLat);
	varLon->put(&(dLon[0]), nLon);

	// Output counts
	NcVar * varCount =
		ncOutput.add_var(
			strOutputVariable.c_str(),
			ncInt,
			dimLat,
			dimLon);

	varCount->put(&(nCounts[0][0]), nLat, nLon);

	ncOutput.close();

	AnnounceEndBlock("Done");

	AnnounceBanner();

} catch(Exception & e) {
	Announce(e.ToString().c_str());
}
}