void GridPatchCartesianGLL::InterpolateData(
	const DataVector<double> & dAlpha,
	const DataVector<double> & dBeta,
	const DataVector<int> & iPatch,
	DataType eDataType,
	DataLocation eDataLocation,
	bool fInterpAllVariables,
	DataMatrix3D<double> & dInterpData,
	bool fIncludeReferenceState,
	bool fConvertToPrimitive
) {
	if (dAlpha.GetRows() != dBeta.GetRows()) {
		_EXCEPTIONT("Point vectors must have equivalent length.");
	}

	// Vector for storage interpolated points
	DataVector<double> dAInterpCoeffs;
	dAInterpCoeffs.Initialize(m_nHorizontalOrder);

	DataVector<double> dBInterpCoeffs;
	dBInterpCoeffs.Initialize(m_nHorizontalOrder);

	DataVector<double> dADiffCoeffs;
	dADiffCoeffs.Initialize(m_nHorizontalOrder);

	DataVector<double> dBDiffCoeffs;
	dBDiffCoeffs.Initialize(m_nHorizontalOrder);

	DataVector<double> dAInterpPt;
	dAInterpPt.Initialize(m_nHorizontalOrder);

	// Element-wise grid spacing
	double dDeltaA = GetElementDeltaA();
	double dDeltaB = GetElementDeltaB();

	// Physical constants
	const PhysicalConstants & phys = m_grid.GetModel().GetPhysicalConstants();

	// Loop throught all points
	for (int i = 0; i < dAlpha.GetRows(); i++) {

		// Element index
		if (iPatch[i] != GetPatchIndex()) {
			continue;
		}

		// Verify point lies within domain of patch
		const double Eps = 1.0e-10;
		if ((dAlpha[i] < m_box.GetAEdge(m_box.GetAInteriorBegin()) - Eps) ||
			(dAlpha[i] > m_box.GetAEdge(m_box.GetAInteriorEnd()) + Eps) ||
			(dBeta[i] < m_box.GetBEdge(m_box.GetBInteriorBegin()) - Eps) ||
			(dBeta[i] > m_box.GetBEdge(m_box.GetBInteriorEnd()) + Eps)
		) {
			_EXCEPTIONT("Point out of range");
		}

		// Determine finite element index
		int iA =
			(dAlpha[i] - m_box.GetAEdge(m_box.GetAInteriorBegin())) / dDeltaA;

		int iB =
			(dBeta[i] - m_box.GetBEdge(m_box.GetBInteriorBegin())) / dDeltaB;

		// Bound the index within the element
		if (iA < 0) {
			iA = 0;
		}
		if (iA >= (m_box.GetAInteriorWidth() / m_nHorizontalOrder)) {
			iA = m_box.GetAInteriorWidth() / m_nHorizontalOrder - 1;
		}
		if (iB < 0) {
			iB = 0;
		}
		if (iB >= (m_box.GetBInteriorWidth() / m_nHorizontalOrder)) {
			iB = m_box.GetBInteriorWidth() / m_nHorizontalOrder - 1;
		}

		iA = m_box.GetHaloElements() + iA * m_nHorizontalOrder;
		iB = m_box.GetHaloElements() + iB * m_nHorizontalOrder;

		// Compute interpolation coefficients
		PolynomialInterp::LagrangianPolynomialCoeffs(
			m_nHorizontalOrder,
			&(m_box.GetAEdges()[iA]),
			dAInterpCoeffs,
			dAlpha[i]);

		PolynomialInterp::LagrangianPolynomialCoeffs(
			m_nHorizontalOrder,
			&(m_box.GetBEdges()[iB]),
			dBInterpCoeffs,
			dBeta[i]);

		int nComponents;
		int nRElements = m_grid.GetRElements();

		double ** pData2D;

		// State Data: Perform interpolation on all variables
		if (eDataType == DataType_State) {
			if (eDataLocation == DataLocation_Node) {
				nComponents = m_datavecStateNode[0].GetComponents();
			} else {
				nComponents = m_datavecStateREdge[0].GetComponents();
				nRElements = m_grid.GetRElements() + 1;
			}

		// Tracer Data: Perform interpolation on all variables
		} else if (eDataType == DataType_Tracers) {
			nComponents = m_datavecTracers[0].GetComponents();

		// Topography Data: Special handling due to 2D nature of data
		} else if (eDataType == DataType_Topography) {
			nComponents = 1;
			pData2D = (double**)(m_dataTopography);

		// Vorticity Data
		} else if (eDataType == DataType_Vorticity) {
			nComponents = 1;

		// Divergence Data
		} else if (eDataType == DataType_Divergence) {
			nComponents = 1;

		// Temperature Data
		} else if (eDataType == DataType_Temperature) {
			nComponents = 1;

		} else {
			_EXCEPTIONT("Invalid DataType");
		}

		// Number of radial elements
		for (int c = 0; c < nComponents; c++) {

			const double *** pData;
			if (eDataType == DataType_State) {
				if (eDataLocation == DataLocation_Node) {
					pData = (const double ***)(m_datavecStateNode[0][c]);
				} else {
					pData = (const double ***)(m_datavecStateREdge[0][c]);
				}
	
			} else if (eDataType == DataType_Topography) {
				pData = (const double ***)(&pData2D);
				nRElements = 1;

			} else if (eDataType == DataType_Tracers) {
				pData = (const double ***)(m_datavecTracers[0][c]);

			} else if (eDataType == DataType_Vorticity) {
				pData = (const double ***)(double ***)(m_dataVorticity);

			} else if (eDataType == DataType_Divergence) {
				pData = (const double ***)(double ***)(m_dataDivergence);

			} else if (eDataType == DataType_Temperature) {
				pData = (const double ***)(double ***)(m_dataTemperature);
			}

			// Perform interpolation on all levels
			for (int k = 0; k < nRElements; k++) {

				dInterpData[c][k][i] = 0.0;

				for (int m = 0; m < m_nHorizontalOrder; m++) {
				for (int n = 0; n < m_nHorizontalOrder; n++) {
					dInterpData[c][k][i] +=
						  dAInterpCoeffs[m]
						* dBInterpCoeffs[n]
						* pData[k][iA+m][iB+n];
				}
				}

				// Do not include the reference state
				if ((eDataType == DataType_State) &&
					(!fIncludeReferenceState)
				) {
					if (eDataLocation == DataLocation_Node) {
						for (int m = 0; m < m_nHorizontalOrder; m++) {
						for (int n = 0; n < m_nHorizontalOrder; n++) {
							dInterpData[c][k][i] -=
								  dAInterpCoeffs[m]
								* dBInterpCoeffs[n]
								* m_dataRefStateNode[c][k][iA+m][iB+n];
						}
						}

					} else {
						for (int m = 0; m < m_nHorizontalOrder; m++) {
						for (int n = 0; n < m_nHorizontalOrder; n++) {
							dInterpData[c][k][i] -=
								  dAInterpCoeffs[m]
								* dBInterpCoeffs[n]
								* m_dataRefStateREdge[c][k][iA+m][iB+n];
						}
						}
					}
				}
			}
		}
	}
}
void GridPatchCartesianGLL::EvaluateGeometricTerms() {

	// Physical constants
	const PhysicalConstants & phys = m_grid.GetModel().GetPhysicalConstants();

	// Obtain Gauss Lobatto quadrature nodes and weights
	DataVector<double> dGL;
	DataVector<double> dWL;

	GaussLobattoQuadrature::GetPoints(m_nHorizontalOrder, 0.0, 1.0, dGL, dWL);

	// Obtain normalized areas in the vertical
	const DataVector<double> & dWNode =
		m_grid.GetREtaLevelsNormArea();
	const DataVector<double> & dWREdge =
		m_grid.GetREtaInterfacesNormArea();

	// Verify that normalized areas are correct
	double dWNodeSum = 0.0;
	for (int k = 0; k < dWNode.GetRows(); k++) {
		dWNodeSum += dWNode[k];
	}
	if (fabs(dWNodeSum - 1.0) > 1.0e-13) {
		_EXCEPTION1("Error in normalized areas (%1.15e)", dWNodeSum);
	}

	if (m_grid.GetVerticalStaggering() !=
	    Grid::VerticalStaggering_Interfaces
	) {
		double dWREdgeSum = 0.0;
		for (int k = 0; k < dWREdge.GetRows(); k++) {
			dWREdgeSum += dWREdge[k];
		}
		if (fabs(dWREdgeSum - 1.0) > 1.0e-13) {
			_EXCEPTION1("Error in normalized areas (%1.15e)", dWREdgeSum);
		}
	}

	// Derivatives of basis functions
	GridCartesianGLL & gridCartesianGLL =
		dynamic_cast<GridCartesianGLL &>(m_grid);

	const DataMatrix<double> & dDxBasis1D = gridCartesianGLL.GetDxBasis1D();
	
	double dy0 = 0.5 * fabs(m_dGDim[3] - m_dGDim[2]);
	double dfp = 2.0 * phys.GetOmega() * sin(m_dRefLat);
	double dbetap = 2.0 * phys.GetOmega() * cos(m_dRefLat) / 
					phys.GetEarthRadius();
	// Initialize the Coriolis force at each node
	for (int i = 0; i < m_box.GetATotalWidth(); i++) {
	for (int j = 0; j < m_box.GetBTotalWidth(); j++) {
		// Coriolis force by beta approximation
		//m_dataCoriolisF[i][j] = dfp + dbetap * (m_dataLat[i][j] - dy0);
		//m_dataCoriolisF[i][j] = dfp;
		//m_dataCoriolisF[i][j] = 0.0;
	}
	}

	// Initialize metric and Christoffel symbols in terrain-following coords
	for (int a = 0; a < GetElementCountA(); a++) {
	for (int b = 0; b < GetElementCountB(); b++) {

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

			// Nodal points
			int iElementA = m_box.GetAInteriorBegin() + a * m_nHorizontalOrder;
			int iElementB = m_box.GetBInteriorBegin() + b * m_nHorizontalOrder;

			int iA = iElementA + i;
			int iB = iElementB + j;

			// Topography height and its derivatives
			double dZs = m_dataTopography[iA][iB];
			double dDaZs = m_dataTopographyDeriv[0][iA][iB];
			double dDbZs = m_dataTopographyDeriv[1][iA][iB];

			// Initialize 2D Jacobian
			m_dataJacobian2D[iA][iB] = 1.0;

			// Initialize 2D contravariant metric
			m_dataContraMetric2DA[iA][iB][0] = 1.0;
			m_dataContraMetric2DA[iA][iB][1] = 0.0;

			m_dataContraMetric2DB[iA][iB][0] = 0.0;
			m_dataContraMetric2DB[iA][iB][1] = 1.0;

			// Initialize 2D covariant metric
			m_dataCovMetric2DA[iA][iB][0] = 1.0;
			m_dataCovMetric2DA[iA][iB][1] = 0.0;

			m_dataCovMetric2DB[iA][iB][0] = 0.0;
			m_dataCovMetric2DB[iA][iB][1] = 1.0;

			// Vertical coordinate transform and its derivatives
			for (int k = 0; k < m_grid.GetRElements(); k++) {

				// Gal-Chen and Somerville (1975) terrain following coord
				// Schar Exponential Decay terrain following coord
				double dREta = m_grid.GetREtaLevel(k);

				double dREtaStretch;
				double dDxREtaStretch;
				m_grid.EvaluateVerticalStretchF(
					dREta, dREtaStretch, dDxREtaStretch);

				//double dZ = dZs + (m_grid.GetZtop() - dZs) * dREtaStretch;
				//double dbZ = sinh(m_grid.GetZtop() * (1.0 - dREtaStretch) / m_dSL)
				//	/ sinh(m_grid.GetZtop() / m_dSL);
				//double dZ = m_grid.GetZtop() * dREtaStretch + dZs; // * dbZ;

				double dZ = dZs + (m_grid.GetZtop() - dZs) * dREtaStretch;

				double dDaZ = (1.0 - dREtaStretch) * dDaZs;
				double dDbZ = (1.0 - dREtaStretch) * dDbZs;
				double dDxZ = (m_grid.GetZtop() - dZs) * dDxREtaStretch;

/*
				double dDaZ = dbZ * dDaZs;
				double dDbZ = dbZ * dDbZs;
				double dDxZ = m_grid.GetZtop() - dZs * m_grid.GetZtop() * 
					cosh(m_grid.GetZtop() * (1.0 - dREtaStretch) / m_dSL) /
					(m_dSL * sinh(m_grid.GetZtop() / m_dSL));
				dDxZ *= dDxREtaStretch;
*/
				// Calculate pointwise Jacobian
				m_dataJacobian[k][iA][iB] =
					dDxZ * m_dataJacobian2D[iA][iB];

				// Element area associated with each model level GLL node
				m_dataElementArea[k][iA][iB] =
					m_dataJacobian[k][iA][iB]
					* dWL[i] * GetElementDeltaA()
					* dWL[j] * GetElementDeltaB()
					* dWNode[k];

				// Contravariant metric components
				m_dataContraMetricA[k][iA][iB][0] =
					m_dataContraMetric2DA[iA][iB][0];
				m_dataContraMetricA[k][iA][iB][1] =
					m_dataContraMetric2DA[iA][iB][1];
				m_dataContraMetricA[k][iA][iB][2] =
					- dDaZ / dDxZ;

				m_dataContraMetricB[k][iA][iB][0] =
					m_dataContraMetric2DB[iA][iB][0];
				m_dataContraMetricB[k][iA][iB][1] =
					m_dataContraMetric2DB[iA][iB][1];
				m_dataContraMetricB[k][iA][iB][2] =
					- dDbZ / dDxZ;

				m_dataContraMetricXi[k][iA][iB][0] =
					m_dataContraMetricA[k][iA][iB][2];
				m_dataContraMetricXi[k][iA][iB][1] =
					m_dataContraMetricB[k][iA][iB][2];
				m_dataContraMetricXi[k][iA][iB][2] =
					(1.0 + dDaZ * dDaZ + dDbZ * dDbZ) / (dDxZ * dDxZ);

				// Covariant metric components
				m_dataCovMetricA[k][iA][iB][0] =
					m_dataCovMetric2DA[iA][iB][0] + dDaZ * dDaZ;
				m_dataCovMetricA[k][iA][iB][1] =
					m_dataCovMetric2DA[iA][iB][1] + dDaZ * dDbZ;
				m_dataCovMetricA[k][iA][iB][2] =
					dDaZ * dDxZ;

				m_dataCovMetricB[k][iA][iB][0] =
					m_dataCovMetric2DB[iA][iB][0] + dDbZ * dDaZ;
				m_dataCovMetricB[k][iA][iB][1] =
					m_dataCovMetric2DB[iA][iB][1] + dDbZ * dDbZ;
				m_dataCovMetricB[k][iA][iB][2] =
					dDbZ * dDxZ;

				m_dataCovMetricXi[k][iA][iB][0] =
					dDaZ * dDxZ;
				m_dataCovMetricXi[k][iA][iB][1] =
					dDbZ * dDxZ;
				m_dataCovMetricXi[k][iA][iB][2] =
					dDxZ * dDxZ;

				// Derivatives of the vertical coordinate transform
				m_dataDerivRNode[k][iA][iB][0] = dDaZ;
				m_dataDerivRNode[k][iA][iB][1] = dDbZ;
				m_dataDerivRNode[k][iA][iB][2] = dDxZ;
			}

			// Metric terms at vertical interfaces
			for (int k = 0; k <= m_grid.GetRElements(); k++) {

				// Gal-Chen and Somerville (1975) terrain following coord
				// Schar Exponential decay terrain following coord
				double dREta = m_grid.GetREtaInterface(k);
/*				
				double dREtaStretch;
				double dDxREtaStretch;
				m_grid.EvaluateVerticalStretchF(
					dREta, dREtaStretch, dDxREtaStretch);

				double dZ = dZs + (m_grid.GetZtop() - dZs) * dREtaStretch;

				double dDaZ = (1.0 - dREtaStretch) * dDaZs;
				double dDbZ = (1.0 - dREtaStretch) * dDbZs;
				double dDxZ = (m_grid.GetZtop() - dZs) * dDxREtaStretch;
*/
				double dREtaStretch;
				double dDxREtaStretch;
				m_grid.EvaluateVerticalStretchF(
					dREta, dREtaStretch, dDxREtaStretch);
/*
				//double dZ = dZs + (m_grid.GetZtop() - dZs) * dREtaStretch;
				double dbZ = sinh(m_grid.GetZtop() * (1.0 - dREtaStretch) / m_dSL) / 
					sinh(m_grid.GetZtop() / m_dSL);
				double dZ = m_grid.GetZtop() * dREtaStretch + dZs * dbZ;
*/

				double dDaZ = (1.0 - dREtaStretch) * dDaZs;
				double dDbZ = (1.0 - dREtaStretch) * dDbZs;
		     	double dDxZ = (m_grid.GetZtop() - dZs) * dDxREtaStretch;
/*
				double dDaZ = dbZ * dDaZs;
				double dDbZ = dbZ * dDbZs;
				double dDxZ = m_grid.GetZtop() - dZs * m_grid.GetZtop() * 
					cosh(m_grid.GetZtop() * (1.0 - dREtaStretch) / m_dSL) /
					(m_dSL * sinh(m_grid.GetZtop() / m_dSL));
				dDxZ *= dDxREtaStretch;
*/
				// Calculate pointwise Jacobian
				m_dataJacobianREdge[k][iA][iB] =
					dDxZ * m_dataJacobian2D[iA][iB];

				// Element area associated with each model interface GLL node
				m_dataElementAreaREdge[k][iA][iB] =
					m_dataJacobianREdge[k][iA][iB]
					* dWL[i] * GetElementDeltaA()
					* dWL[j] * GetElementDeltaB()
					* dWREdge[k];

				// Components of the contravariant metric
				m_dataContraMetricAREdge[k][iA][iB][0] =
					m_dataContraMetric2DA[iA][iB][0];
				m_dataContraMetricAREdge[k][iA][iB][1] =
					m_dataContraMetric2DA[iA][iB][1];
				m_dataContraMetricAREdge[k][iA][iB][2] =
					- dDaZ / dDxZ;

				m_dataContraMetricBREdge[k][iA][iB][0] =
					m_dataContraMetric2DB[iA][iB][0];
				m_dataContraMetricBREdge[k][iA][iB][1] =
					m_dataContraMetric2DB[iA][iB][1];
				m_dataContraMetricBREdge[k][iA][iB][2] =
					- dDbZ / dDxZ;

				m_dataContraMetricXiREdge[k][iA][iB][0] =
					- dDaZ / dDxZ;
				m_dataContraMetricXiREdge[k][iA][iB][1] =
					- dDbZ / dDxZ;
				m_dataContraMetricXiREdge[k][iA][iB][2] =
					(1.0 + dDaZ * dDaZ + dDbZ * dDbZ) / (dDxZ * dDxZ);

				// Derivatives of the vertical coordinate transform
				m_dataDerivRREdge[k][iA][iB][0] = dDaZ;
				m_dataDerivRREdge[k][iA][iB][1] = dDbZ;
				m_dataDerivRREdge[k][iA][iB][2] = dDxZ;
			}
		}
		}
	}
	}
}
void GridPatchCartesianGLL::ComputeCurlAndDiv(
	const GridData3D & dataUa,
	const GridData3D & dataUb
) const {
	// Parent grid
	const GridCartesianGLL & gridCSGLL =
		dynamic_cast<const GridCartesianGLL &>(m_grid);

	// Compute derivatives of the field
	const DataMatrix<double> & dDxBasis1D = gridCSGLL.GetDxBasis1D();

	// Number of finite elements in each direction
	int nAFiniteElements = m_box.GetAInteriorWidth() / m_nHorizontalOrder;
	int nBFiniteElements = m_box.GetBInteriorWidth() / m_nHorizontalOrder;

	// Contravariant velocity within an element
	DataMatrix<double> dConUa(m_nHorizontalOrder, m_nHorizontalOrder);
	DataMatrix<double> dConUb(m_nHorizontalOrder, m_nHorizontalOrder);

	// Loop over all elements in the box
	for (int k = 0; k < gridCSGLL.GetRElements(); k++) {
	for (int a = 0; a < nAFiniteElements; a++) {
	for (int b = 0; b < nBFiniteElements; b++) {

		// Index of lower-left corner node
		int iA = a * m_nHorizontalOrder + m_box.GetHaloElements();
		int iB = b * m_nHorizontalOrder + m_box.GetHaloElements();

		// Calculate contravariant velocity at each node within the element
		for (int i = 0; i < m_nHorizontalOrder; i++) {
		for (int j = 0; j < m_nHorizontalOrder; j++) {
			dConUa[i][j] =
				  m_dataContraMetric2DA[iA+i][iB+j][0]
				  	* dataUa[k][iA+i][iB+j]
				+ m_dataContraMetric2DA[iA+i][iB+j][1]
					* dataUb[k][iA+i][iB+j];

			dConUb[i][j] =
				  m_dataContraMetric2DB[iA+i][iB+j][0]
				  	* dataUa[k][iA+i][iB+j]
				+ m_dataContraMetric2DB[iA+i][iB+j][1]
					* dataUb[k][iA+i][iB+j];
		}
		}

		// Calculate divergance and curl
		for (int i = 0; i < m_nHorizontalOrder; i++) {
		for (int j = 0; j < m_nHorizontalOrder; j++) {

			// Compute derivatives at each node
			double dDaJUa = 0.0;
			double dDbJUb = 0.0;

			double dCovDaUb = 0.0;
			double dCovDbUa = 0.0;

			for (int s = 0; s < m_nHorizontalOrder; s++) {
				dDaJUa += dDxBasis1D[s][i]
					* m_dataJacobian2D[iA+s][iB+j]
					* dConUa[s][j];

				dDbJUb += dDxBasis1D[s][j]
					* m_dataJacobian2D[iA+i][iB+s]
					* dConUb[i][s];

				dCovDaUb += dDxBasis1D[s][i] * dataUb[k][iA+s][iB+j];
					//( m_dataCovMetric2DB[iA+s][iB+j][0] * dataUa[k][iA+s][iB+j]
					//+ m_dataCovMetric2DB[iA+s][iB+j][1] * dataUb[k][iA+s][iB+j]);
				dCovDbUa += dDxBasis1D[s][j] * dataUa[k][iA+i][iB+s];
					//( m_dataCovMetric2DA[iA+i][iB+s][0] * dataUa[k][iA+i][iB+s]
					//+ m_dataCovMetric2DA[iA+i][iB+s][1] * dataUb[k][iA+i][iB+s]);
			}

			dDaJUa /= GetElementDeltaA();
			dDbJUb /= GetElementDeltaB();

			dCovDaUb /= GetElementDeltaA();
			dCovDbUa /= GetElementDeltaB();

			m_dataVorticity[k][iA+i][iB+j] =
				(dCovDaUb - dCovDbUa) / m_dataJacobian2D[iA+i][iB+j];

			// Compute the divergence at node
			m_dataDivergence[k][iA+i][iB+j] =
				(dDaJUa + dDbJUb) / m_dataJacobian2D[iA+i][iB+j];

/*
			// Compute covariant derivatives at node
			double dCovDaUa = dDaUa
				+ m_dataChristoffelA[iA+i][iB+j][0] * dUa
				+ m_dataChristoffelA[iA+i][iB+j][1] * 0.5 * dUb;

			double dCovDaUb = dDaUb
				+ m_dataChristoffelB[iA+i][iB+j][0] * dUa
				+ m_dataChristoffelB[iA+i][iB+j][1] * 0.5 * dUb;

			double dCovDbUa = dDbUa
				+ m_dataChristoffelA[iA+i][iB+j][1] * 0.5 * dUa
				+ m_dataChristoffelA[iA+i][iB+j][2] * dUb;

			double dCovDbUb = dDbUb
				+ m_dataChristoffelB[iA+i][iB+j][1] * 0.5 * dUa
				+ m_dataChristoffelB[iA+i][iB+j][2] * dUb;

			// Compute curl at node
			m_dataVorticity[k][iA+i][iB+j] = m_dataJacobian2D[iA+i][iB+j] * (
				+ m_dataContraMetric2DA[iA+i][iB+j][0] * dDaUb
				+ m_dataContraMetric2DA[iA+i][iB+j][1] * dDbUb
				- m_dataContraMetric2DB[iA+i][iB+j][0] * dDaUa
				- m_dataContraMetric2DB[iA+i][iB+j][1] * dDbUa);

			// Compute the divergence at node
			m_dataDivergence[k][iA+i][iB+j] = dDaUa + dDbUb;
*/
		}
		}
	}
	}
	}
}
void GridPatchCartesianGLL::EvaluateTopography(
	const TestCase & test
) {
	const PhysicalConstants & phys = m_grid.GetModel().GetPhysicalConstants();

	// Compute values of topography
	for (int i = 0; i < m_box.GetATotalWidth(); i++) {
	for (int j = 0; j < m_box.GetBTotalWidth(); j++) {

		double dX = m_box.GetANode(i);
		double dY = m_box.GetBNode(j);

		m_dataTopography[i][j] = test.EvaluateTopography(phys, dX, dY);

		if (m_dataTopography[i][j] >= m_grid.GetZtop()) {
			_EXCEPTIONT("TestCase topography exceeds model top.");
		}
	}
	}

	// Get derivatves from basis
	GridCartesianGLL & gridCartesianGLL =
		dynamic_cast<GridCartesianGLL &>(m_grid);

	const DataMatrix<double> & dDxBasis1D =
		gridCartesianGLL.GetDxBasis1D();

	// Compute derivatives of topography
	for (int a = 0; a < GetElementCountA(); a++) {
	for (int b = 0; b < GetElementCountB(); b++) {

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

			// Nodal points
			int iElementA = m_box.GetAInteriorBegin() + a * m_nHorizontalOrder;
			int iElementB = m_box.GetBInteriorBegin() + b * m_nHorizontalOrder;

			int iA = iElementA + i;
			int iB = iElementB + j;

			// Topography height and its derivatives
			double dZs = m_dataTopography[iA][iB];

			double dDaZs = 0.0;
			double dDbZs = 0.0;

			for (int s = 0; s < m_nHorizontalOrder; s++) {
				dDaZs += dDxBasis1D[s][i] * m_dataTopography[iElementA+s][iB];
				dDbZs += dDxBasis1D[s][j] * m_dataTopography[iA][iElementB+s];
			}

			dDaZs /= GetElementDeltaA();
			dDbZs /= GetElementDeltaB();

			m_dataTopographyDeriv[0][iA][iB] = dDaZs;
			m_dataTopographyDeriv[1][iA][iB] = dDbZs;
		}
		}
	}
	}
}
Пример #5
0
void GridPatchCSGLL::ComputeCurlAndDiv(
	const DataArray3D<double> & dataUa,
	const DataArray3D<double> & dataUb
) {
	// Parent grid
	const GridCSGLL & gridCSGLL = dynamic_cast<const GridCSGLL &>(m_grid);

	// If SpectralElement dynamics are used, apply direct stiffness summation
	bool fDiscontinuous = false;

	// Get derivatives of the basis functions
	const DataArray2D<double> & dDxBasis1D = gridCSGLL.GetDxBasis1D();

	// Get derivatives of the flux reconstruction function
	const DataArray1D<double> & dFluxDeriv1D = gridCSGLL.GetFluxDeriv1D();

	// Number of finite elements in each direction
	int nAFiniteElements = m_box.GetAInteriorWidth() / m_nHorizontalOrder;
	int nBFiniteElements = m_box.GetBInteriorWidth() / m_nHorizontalOrder;
/*
	// Allocate temporary data for contravariant velocities
	DataArray2D<double> dConUa(m_nHorizontalOrder, m_nHorizontalOrder);
	DataArray2D<double> dConUb(m_nHorizontalOrder, m_nHorizontalOrder);
*/
	// Inverse grid spacings
	const double dInvElementDeltaA = 1.0 / GetElementDeltaA();
	const double dInvElementDeltaB = 1.0 / GetElementDeltaB();

	// Loop over all elements in the box
	for (int k = 0; k < gridCSGLL.GetRElements(); k++) {
	for (int a = 0; a < nAFiniteElements; a++) {
	for (int b = 0; b < nBFiniteElements; b++) {

		// Index of lower-left corner node
		int iElementA = a * m_nHorizontalOrder + m_box.GetHaloElements();
		int iElementB = b * m_nHorizontalOrder + m_box.GetHaloElements();

		// Calculate contravariant velocities in element
		for (int i = 0; i < m_nHorizontalOrder; i++) {
		for (int j = 0; j < m_nHorizontalOrder; j++) {

			int iA = iElementA + i;
			int iB = iElementB + j;

			m_dBufferConU[0][i][j] =
				+ m_dataContraMetric2DA[iA][iB][0] * dataUa[k][iA][iB]
				+ m_dataContraMetric2DA[iA][iB][1] * dataUb[k][iA][iB];

			m_dBufferConU[1][i][j] =
				+ m_dataContraMetric2DB[iA][iB][0] * dataUa[k][iA][iB]
				+ m_dataContraMetric2DB[iA][iB][1] * dataUb[k][iA][iB];
		}
		}

		// Calculate curl and div
		for (int i = 0; i < m_nHorizontalOrder; i++) {
		for (int j = 0; j < m_nHorizontalOrder; j++) {

			int iA = iElementA + i;
			int iB = iElementB + j;

			// Pointwise field values
			double dUa = dataUa[k][iA][iB];
			double dUb = dataUb[k][iA][iB];

			// Compute derivatives at each node
			//double dDaUa = 0.0;
			double dDaUb = 0.0;
			double dDbUa = 0.0;
			//double dDbUb = 0.0;

			double dDaJUa = 0.0;
			double dDbJUb = 0.0;

			for (int s = 0; s < m_nHorizontalOrder; s++) {
				//dDaUa += dataUa[k][iElementA+s][iB] * dDxBasis1D[s][i];
				dDaUb += dataUb[k][iElementA+s][iB] * dDxBasis1D[s][i];
				dDbUa += dataUa[k][iA][iElementB+s] * dDxBasis1D[s][j];
				//dDbUb += dataUb[k][iA][iElementB+s] * dDxBasis1D[s][j];

				dDaJUa +=
					m_dataJacobian2D[iElementA+s][iB]
					* m_dBufferConU[0][s][j]
					* dDxBasis1D[s][i];

				dDbJUb +=
					m_dataJacobian2D[iA][iElementB+s]
					* m_dBufferConU[1][i][s]
					* dDxBasis1D[s][j];
			}

			dDaUb *= dInvElementDeltaA;
			dDbUa *= dInvElementDeltaB;

			dDaJUa *= dInvElementDeltaA;
			dDbJUb *= dInvElementDeltaB;
/*
			if (fDiscontinuous) {
				_EXCEPTIONT("This needs to be updated for covariant indices");

				double dUpdateDerivA =
					  dFluxDeriv1D[m_nHorizontalOrder-1] / GetElementDeltaA();
				double dUpdateDerivB =
					  dFluxDeriv1D[m_nHorizontalOrder-1] / GetElementDeltaB();

				if (i == 0) {
					double dUaL = dataUa[k][iA-1][iB];
					double dUaR = dataUa[k][iA  ][iB];
					double dUbL = dataUb[k][iA-1][iB];
					double dUbR = dataUb[k][iA  ][iB];

					dDaUa += 0.5 * dUpdateDerivA * (dUaR - dUaL);
					dDaUb += 0.5 * dUpdateDerivA * (dUbR - dUbL);
				}
				if (i == m_nHorizontalOrder-1) {
					double dUaL = dataUa[k][iA  ][iB];
					double dUaR = dataUa[k][iA+1][iB];
					double dUbL = dataUb[k][iA  ][iB];
					double dUbR = dataUb[k][iA+1][iB];

					dDaUa += 0.5 * dUpdateDerivA * (dUaR - dUaL);
					dDaUb += 0.5 * dUpdateDerivA * (dUbR - dUbL);
				}
				if (j == 0) {
					double dUaL = dataUa[k][iA][iB-1];
					double dUaR = dataUa[k][iA][iB  ];
					double dUbL = dataUb[k][iA][iB-1];
					double dUbR = dataUb[k][iA][iB  ];

					dDbUa += 0.5 * dUpdateDerivB * (dUaR - dUaL);
					dDbUb += 0.5 * dUpdateDerivB * (dUbR - dUbL);
				}
				if (j == m_nHorizontalOrder-1) {
					double dUaL = dataUa[k][iA][iB  ];
					double dUaR = dataUa[k][iA][iB+1];
					double dUbL = dataUb[k][iA][iB  ];
					double dUbR = dataUb[k][iA][iB+1];

					dDbUa += 0.5 * dUpdateDerivB * (dUaR - dUaL);
					dDbUb += 0.5 * dUpdateDerivB * (dUbR - dUbL);
				}
			}
*/
			m_dataDivergence[k][iA][iB] =
				(dDaJUa + dDbJUb) / m_dataJacobian2D[iA][iB];
			m_dataVorticity[k][iA][iB] =
				(dDaUb - dDbUa) / m_dataJacobian2D[iA][iB];

/*
			// Compute covariant derivatives at node
			double dCovDaUa = dDaUa
				+ m_dataChristoffelA[iA][iB][0] * dUa
				+ m_dataChristoffelA[iA][iB][1] * 0.5 * dUb;

			double dCovDaUb = dDaUb
				+ m_dataChristoffelB[iA][iB][0] * dUa
				+ m_dataChristoffelB[iA][iB][1] * 0.5 * dUb;

			double dCovDbUa = dDbUa
				+ m_dataChristoffelA[iA][iB][1] * 0.5 * dUa
				+ m_dataChristoffelA[iA][iB][2] * dUb;

			double dCovDbUb = dDbUb
				+ m_dataChristoffelB[iA][iB][1] * 0.5 * dUa
				+ m_dataChristoffelB[iA][iB][2] * dUb;

			// Compute curl at node
			m_dataVorticity[k][iA][iB] = m_dataJacobian2D[iA][iB] * (
				+ m_dataContraMetric2DA[iA][iB][0] * dCovDaUb
				+ m_dataContraMetric2DA[iA][iB][1] * dCovDbUb
				- m_dataContraMetric2DB[iA][iB][0] * dCovDaUa
				- m_dataContraMetric2DB[iA][iB][1] * dCovDbUa);

			// Compute the divergence at node
			m_dataDivergence[k][iA][iB] = dCovDaUa + dCovDbUb;
			//double dInvJacobian = 1.0 / m_dataJacobian2D[iA][iB];
			//m_dataDivergence[k][iA][iB] =
			//	dInvJacobian * (dDaJUa + dDbJUb);
*/
		}
		}
	}
	}
	}
}
Пример #6
0
void GridPatchCSGLL::EvaluateGeometricTerms() {

	// Physical constants
	const PhysicalConstants & phys = m_grid.GetModel().GetPhysicalConstants();

	// 2D equation set
	bool fIs2DEquationSet = false;
	if (m_grid.GetModel().GetEquationSet().GetDimensionality() == 2) {
		fIs2DEquationSet = true;
	}

	if ((fIs2DEquationSet) && (m_grid.GetZtop() != 1.0)) {
		_EXCEPTIONT("Ztop must be 1.0 for 2D equation sets");
	}

	// Obtain Gauss Lobatto quadrature nodes and weights
	DataArray1D<double> dGL;
	DataArray1D<double> dWL;

	GaussLobattoQuadrature::GetPoints(m_nHorizontalOrder, 0.0, 1.0, dGL, dWL);

	// Obtain normalized areas in the vertical
	const DataArray1D<double> & dWNode =
		m_grid.GetREtaLevelsNormArea();
	const DataArray1D<double> & dWREdge =
		m_grid.GetREtaInterfacesNormArea();

	// Verify that normalized areas are correct
	double dWNodeSum = 0.0;
	for (int k = 0; k < dWNode.GetRows(); k++) {
		dWNodeSum += dWNode[k];
	}
	if (fabs(dWNodeSum - 1.0) > 1.0e-13) {
		_EXCEPTION1("Error in normalized areas (%1.15e)", dWNodeSum);
	}

	if (m_grid.GetVerticalStaggering() !=
	    Grid::VerticalStaggering_Interfaces
	) {
		double dWREdgeSum = 0.0;
		for (int k = 0; k < dWREdge.GetRows(); k++) {
			dWREdgeSum += dWREdge[k];
		}
		if (fabs(dWREdgeSum - 1.0) > 1.0e-13) {
			_EXCEPTION1("Error in normalized areas (%1.15e)", dWREdgeSum);
		}
	}

	// Derivatives of basis functions
	GridCSGLL & gridCSGLL = dynamic_cast<GridCSGLL &>(m_grid);

	const DataArray2D<double> & dDxBasis1D = gridCSGLL.GetDxBasis1D();

	// Initialize the Coriolis force at each node
	for (int i = 0; i < m_box.GetATotalWidth(); i++) {
	for (int j = 0; j < m_box.GetBTotalWidth(); j++) {
		m_dataCoriolisF[i][j] = 2.0 * phys.GetOmega() * sin(m_dataLat[i][j]);
	}
	}

	// Initialize metric in terrain-following coords
	for (int a = 0; a < GetElementCountA(); a++) {
	for (int b = 0; b < GetElementCountB(); b++) {

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

		// Nodal points
		int iElementA = m_box.GetAInteriorBegin() + a * m_nHorizontalOrder;
		int iElementB = m_box.GetBInteriorBegin() + b * m_nHorizontalOrder;

		int iA = iElementA + i;
		int iB = iElementB + j;

		// Gnomonic coordinates
		double dX = tan(m_dANode[iA]);
		double dY = tan(m_dBNode[iB]);
		double dDelta2 = (1.0 + dX * dX + dY * dY);
		double dDelta = sqrt(dDelta2);

		// Topography height and its derivatives
		double dZs = m_dataTopography[iA][iB];
		double dDaZs = m_dataTopographyDeriv[0][iA][iB];
		double dDbZs = m_dataTopographyDeriv[1][iA][iB];

		// 2D equations
		if (fIs2DEquationSet) {
			dZs = 0.0;
			dDaZs = 0.0;
			dDbZs = 0.0;
		}

		// Initialize 2D Jacobian
		m_dataJacobian2D[iA][iB] =
			(1.0 + dX * dX) * (1.0 + dY * dY) / (dDelta * dDelta * dDelta);

		m_dataJacobian2D[iA][iB] *=
			  phys.GetEarthRadius()
			* phys.GetEarthRadius();

		// Initialize 2D contravariant metric
		double dContraMetricScale = 
			dDelta2 / (1.0 + dX * dX) / (1.0 + dY * dY)
			/ (phys.GetEarthRadius() * phys.GetEarthRadius());

		m_dataContraMetric2DA[iA][iB][0] =
			dContraMetricScale * (1.0 + dY * dY);
		m_dataContraMetric2DA[iA][iB][1] =
			dContraMetricScale * dX * dY;

		m_dataContraMetric2DB[iA][iB][0] =
			dContraMetricScale * dX * dY;
		m_dataContraMetric2DB[iA][iB][1] =
			dContraMetricScale * (1.0 + dX * dX);

		// Initialize 2D covariant metric
		double dCovMetricScale =
			phys.GetEarthRadius() * phys.GetEarthRadius()
			* (1.0 + dX * dX) * (1.0 + dY * dY)
			/ (dDelta2 * dDelta2);

		m_dataCovMetric2DA[iA][iB][0] =
			dCovMetricScale * (1.0 + dX * dX);
		m_dataCovMetric2DA[iA][iB][1] =
			dCovMetricScale * (- dX * dY);

		m_dataCovMetric2DB[iA][iB][0] =
			dCovMetricScale * (- dX * dY);
		m_dataCovMetric2DB[iA][iB][1] =
			dCovMetricScale * (1.0 + dY * dY);

		// Vertical coordinate transform and its derivatives
		for (int k = 0; k < m_grid.GetRElements(); k++) {

			// Gal-Chen and Somerville (1975) linear terrain-following coord
			double dREta = m_grid.GetREtaLevel(k);
/*
			double dREtaStretch;
			double dDxREtaStretch;
			m_grid.EvaluateVerticalStretchF(
				dREta, dREtaStretch, dDxREtaStretch);

			double dZ = dZs + (m_grid.GetZtop() - dZs) * dREtaStretch;
			double dDaR = (1.0 - dREtaStretch) * dDaZs;
			double dDbR = (1.0 - dREtaStretch) * dDbZs;
			double dDxR = (m_grid.GetZtop() - dZs) * dDxREtaStretch;
*/

			double dZ = dZs + (m_grid.GetZtop() - dZs) * dREta;
			double dDaR = (1.0 - dREta) * dDaZs;
			double dDbR = (1.0 - dREta) * dDbZs;
			double dDxR = (m_grid.GetZtop() - dZs);

			// Calculate pointwise Jacobian
			m_dataJacobian[k][iA][iB] = dDxR * m_dataJacobian2D[iA][iB];

			// Element area associated with each model level GLL node
			m_dataElementAreaNode[k][iA][iB] =
				m_dataJacobian[k][iA][iB]
				* dWL[i] * GetElementDeltaA()
				* dWL[j] * GetElementDeltaB()
				* dWNode[k];

			// Contravariant metric components
			m_dataContraMetricA[k][iA][iB][0] =
				m_dataContraMetric2DA[iA][iB][0];
			m_dataContraMetricA[k][iA][iB][1] =
				m_dataContraMetric2DA[iA][iB][1];
			m_dataContraMetricA[k][iA][iB][2] =
				- dContraMetricScale / dDxR * (
					(1.0 + dY * dY) * dDaR + dX * dY * dDbR);

			m_dataContraMetricB[k][iA][iB][0] =
				m_dataContraMetric2DB[iA][iB][0];
			m_dataContraMetricB[k][iA][iB][1] =
				m_dataContraMetric2DB[iA][iB][1];
			m_dataContraMetricB[k][iA][iB][2] =
				- dContraMetricScale / dDxR * (
					dX * dY * dDaR + (1.0 + dX * dX) * dDbR);

			m_dataContraMetricXi[k][iA][iB][0] =
				m_dataContraMetricA[k][iA][iB][2];
			m_dataContraMetricXi[k][iA][iB][1] =
				m_dataContraMetricB[k][iA][iB][2];
			m_dataContraMetricXi[k][iA][iB][2] =
				  1.0 / (dDxR * dDxR)
				- 1.0 / dDxR * (
					  m_dataContraMetricXi[k][iA][iB][0] * dDaR
					+ m_dataContraMetricXi[k][iA][iB][1] * dDbR);

			// Derivatives of the vertical coordinate transform
			m_dataDerivRNode[k][iA][iB][0] = dDaR;
			m_dataDerivRNode[k][iA][iB][1] = dDbR;
			m_dataDerivRNode[k][iA][iB][2] = dDxR;
		}

		// Metric terms at vertical interfaces
		for (int k = 0; k <= m_grid.GetRElements(); k++) {

			// Gal-Chen and Somerville (1975) linear terrain-following coord
			double dREta = m_grid.GetREtaInterface(k);
/*
			double dREtaStretch;
			double dDxREtaStretch;
			m_grid.EvaluateVerticalStretchF(
				dREta, dREtaStretch, dDxREtaStretch);

			double dZ = dZs + (m_grid.GetZtop() - dZs) * dREtaStretch;

			double dDaR = (1.0 - dREtaStretch) * dDaZs;
			double dDbR = (1.0 - dREtaStretch) * dDbZs;
			double dDxR = (m_grid.GetZtop() - dZs) * dDxREtaStretch;
*/
			double dZ = dZs + (m_grid.GetZtop() - dZs) * dREta;
			double dDaR = (1.0 - dREta) * dDaZs;
			double dDbR = (1.0 - dREta) * dDbZs;
			double dDxR = (m_grid.GetZtop() - dZs);

			// Calculate pointwise Jacobian
			m_dataJacobianREdge[k][iA][iB] =
				(1.0 + dX * dX) * (1.0 + dY * dY) / (dDelta * dDelta * dDelta);

			m_dataJacobianREdge[k][iA][iB] *=
				dDxR
				* phys.GetEarthRadius()
				* phys.GetEarthRadius();

			// Element area associated with each model interface GLL node
			m_dataElementAreaREdge[k][iA][iB] =
				m_dataJacobianREdge[k][iA][iB]
				* dWL[i] * GetElementDeltaA()
				* dWL[j] * GetElementDeltaB()
				* dWREdge[k];

			// Contravariant metric (alpha)
			m_dataContraMetricAREdge[k][iA][iB][0] =
				m_dataContraMetric2DA[iA][iB][0];
			m_dataContraMetricAREdge[k][iA][iB][1] =
				m_dataContraMetric2DA[iA][iB][1];
			m_dataContraMetricAREdge[k][iA][iB][2] =
				- dContraMetricScale / dDxR * (
					(1.0 + dY * dY) * dDaR + dX * dY * dDbR);

			// Contravariant metric (beta)
			m_dataContraMetricBREdge[k][iA][iB][0] =
				m_dataContraMetric2DB[iA][iB][0];
			m_dataContraMetricBREdge[k][iA][iB][1] =
				m_dataContraMetric2DB[iA][iB][1];
			m_dataContraMetricBREdge[k][iA][iB][2] =
				- dContraMetricScale / dDxR * (
					dX * dY * dDaR + (1.0 + dX * dX) * dDbR);

			// Contravariant metric (xi)
			m_dataContraMetricXiREdge[k][iA][iB][0] =
				- dContraMetricScale / dDxR * (
					(1.0 + dY * dY) * dDaR + dX * dY * dDbR);

			m_dataContraMetricXiREdge[k][iA][iB][1] =
				- dContraMetricScale / dDxR * (
					dX * dY * dDaR + (1.0 + dX * dX) * dDbR);

			m_dataContraMetricXiREdge[k][iA][iB][2] =
				  1.0 / (dDxR * dDxR)
				- 1.0 / dDxR * (
					  m_dataContraMetricXiREdge[k][iA][iB][0] * dDaR
					+ m_dataContraMetricXiREdge[k][iA][iB][1] * dDbR);

			// Derivatives of the vertical coordinate transform
			m_dataDerivRREdge[k][iA][iB][0] = dDaR;
			m_dataDerivRREdge[k][iA][iB][1] = dDbR;
			m_dataDerivRREdge[k][iA][iB][2] = dDxR;
		}
	}
	}

	}
	}
}
Пример #7
0
void GridPatchCSGLL::EvaluateTopography(
	const TestCase & test
) {
	const PhysicalConstants & phys = m_grid.GetModel().GetPhysicalConstants();

	// Compute values of topography
	for (int i = 0; i < m_box.GetATotalWidth(); i++) {
	for (int j = 0; j < m_box.GetBTotalWidth(); j++) {

		double dLon;
		double dLat;

		CubedSphereTrans::RLLFromABP(
			m_dANode[i],
			m_dBNode[j],
			m_box.GetPanel(),
			dLon,
			dLat);

		m_dataTopography[i][j] = test.EvaluateTopography(phys, dLon, dLat);
	}
	}

	// Get derivatves from basis
	GridCSGLL & gridCSGLL = dynamic_cast<GridCSGLL &>(m_grid);

	const DataArray2D<double> & dDxBasis1D = gridCSGLL.GetDxBasis1D();

	// Compute derivatives of topography
	for (int a = 0; a < GetElementCountA(); a++) {
	for (int b = 0; b < GetElementCountB(); b++) {

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

			// Nodal points
			int iElementA = m_box.GetAInteriorBegin() + a * m_nHorizontalOrder;
			int iElementB = m_box.GetBInteriorBegin() + b * m_nHorizontalOrder;

			int iA = iElementA + i;
			int iB = iElementB + j;

			// Topography height and its derivatives
			double dZs = m_dataTopography[iA][iB];

			double dDaZs = 0.0;
			double dDbZs = 0.0;

			for (int s = 0; s < m_nHorizontalOrder; s++) {
				dDaZs += dDxBasis1D[s][i] * m_dataTopography[iElementA+s][iB];
				dDbZs += dDxBasis1D[s][j] * m_dataTopography[iA][iElementB+s];
			}

			dDaZs /= GetElementDeltaA();
			dDbZs /= GetElementDeltaB();

			m_dataTopographyDeriv[0][iA][iB] = dDaZs;
			m_dataTopographyDeriv[1][iA][iB] = dDbZs;
		}
		}
	}
	}
}
Пример #8
0
void GridPatchCSGLL::InterpolateData(
	DataType eDataType,
	const DataArray1D<double> & dREta,
	const DataArray1D<double> & dAlpha,
	const DataArray1D<double> & dBeta,
	const DataArray1D<int> & iPatch,
	DataArray3D<double> & dInterpData,
	DataLocation eOnlyVariablesAt,
	bool fIncludeReferenceState,
	bool fConvertToPrimitive
) {
	if ((dAlpha.GetRows() != dBeta.GetRows()) ||
		(dAlpha.GetRows() != iPatch.GetRows())
	) {
		_EXCEPTIONT("Point vectors must have equivalent length.");
	}

	// Vector for storage interpolated points
	DataArray1D<double> dAInterpCoeffs(m_nHorizontalOrder);
	DataArray1D<double> dBInterpCoeffs(m_nHorizontalOrder);
	DataArray1D<double> dADiffCoeffs(m_nHorizontalOrder);
	DataArray1D<double> dBDiffCoeffs(m_nHorizontalOrder);
	DataArray1D<double> dAInterpPt(m_nHorizontalOrder);

	// Physical constants
	const PhysicalConstants & phys = m_grid.GetModel().GetPhysicalConstants();

	// Perform interpolation on all variables
	int nComponents = 0;
	int nRElements = m_grid.GetRElements();

	// Discretization type
	Grid::VerticalDiscretization eVerticalDiscType =
		m_grid.GetVerticalDiscretization();

	// State Data: Perform interpolation on all variables
	if (eDataType == DataType_State) {
		nComponents = m_datavecStateNode[0].GetSize(0);
		nRElements = m_grid.GetRElements() + 1;

	// Tracer Data: Perform interpolation on all variables
	} else if (eDataType == DataType_Tracers) {
		nComponents = m_datavecTracers[0].GetSize(0);

	// Topography Data
	} else if (eDataType == DataType_Topography) {
		nComponents = 1;
		nRElements = 1;

	// Vorticity Data
	} else if (eDataType == DataType_Vorticity) {
		nComponents = 1;

	// Divergence Data
	} else if (eDataType == DataType_Divergence) {
		nComponents = 1;

	// Temperature Data
	} else if (eDataType == DataType_Temperature) {
		nComponents = 1;

	// Surface Pressure Data
	} else if (eDataType == DataType_SurfacePressure) {
		nComponents = 1;
		nRElements = 1;

	// 2D User Data
	} else if (eDataType == DataType_Auxiliary2D) {
		nComponents = m_dataUserData2D.GetSize(0);
		nRElements = 1;

	} else {
		_EXCEPTIONT("Invalid DataType");
	}

	// Buffer storage in column
	DataArray1D<double> dColumnDataOut(dREta.GetRows());

	// Loop through all components
	for (int c = 0; c < nComponents; c++) {

		DataLocation eDataLocation = DataLocation_Node;

		if (eDataType == DataType_State) {
			eDataLocation = m_grid.GetVarLocation(c);

			// Exclude variables not at the specified DataLocation
			if ((eOnlyVariablesAt != DataLocation_None) &&
			    (eOnlyVariablesAt != eDataLocation)
			) {
				continue;
			}

			// Adjust RElements depending on state data location
			if (eDataLocation == DataLocation_Node) {
				nRElements = m_grid.GetRElements();
			} else if (eDataLocation == DataLocation_REdge) {
				nRElements = m_grid.GetRElements() + 1;
			} else {
				_EXCEPTIONT("Invalid DataLocation");
			}
		}

		// Vertical interpolation operator
		LinearColumnInterpFEM opInterp;

		if (nRElements != 1) {

			// Finite element interpolation
			if (eVerticalDiscType ==
				Grid::VerticalDiscretization_FiniteElement
			) {
				if (eDataLocation == DataLocation_Node) {
					opInterp.Initialize(
						LinearColumnInterpFEM::InterpSource_Levels,
						m_nVerticalOrder,
						m_grid.GetREtaLevels(),
						m_grid.GetREtaInterfaces(),
						dREta);

				} else if (eDataLocation == DataLocation_REdge) {
					opInterp.Initialize(
						LinearColumnInterpFEM::InterpSource_Interfaces,
						m_nVerticalOrder,
						m_grid.GetREtaLevels(),
						m_grid.GetREtaInterfaces(),
						dREta);

				} else {
					_EXCEPTIONT("Invalid DataLocation");
				}

			// Finite volume interpolation
			} else if (
				eVerticalDiscType ==
				Grid::VerticalDiscretization_FiniteVolume
			) {
				if (eDataLocation == DataLocation_Node) {
					opInterp.Initialize(
						LinearColumnInterpFEM::InterpSource_Levels,
						1,
						m_grid.GetREtaLevels(),
						m_grid.GetREtaInterfaces(),
						dREta);

				} else if (eDataLocation == DataLocation_REdge) {
					opInterp.Initialize(
						LinearColumnInterpFEM::InterpSource_Interfaces,
						1,
						m_grid.GetREtaLevels(),
						m_grid.GetREtaInterfaces(),
						dREta);

				} else {
					_EXCEPTIONT("Invalid DataLocation");
				}

			// Invalid vertical discretization type
			} else {
				_EXCEPTIONT("Invalid VerticalDiscretization");
			}

		} else {
			opInterp.InitializeIdentity(1);
		}

		// Buffer storage in column
		DataArray1D<double> dColumnData(nRElements);

		// Get a pointer to the 3D data structure
		DataArray3D<double> pData;
		DataArray3D<double> pDataRef;

		pData.SetSize(
			nRElements,
			m_box.GetATotalWidth(),
			m_box.GetBTotalWidth());

		pDataRef.SetSize(
			nRElements,
			m_box.GetATotalWidth(),
			m_box.GetBTotalWidth());

		if (eDataType == DataType_State) {
			if (eDataLocation == DataLocation_Node) {
				pData.AttachToData(&(m_datavecStateNode[0][c][0][0][0]));
				pDataRef.AttachToData(&(m_dataRefStateNode[c][0][0][0]));
			} else if (eDataLocation == DataLocation_REdge) {
				pData.AttachToData(&(m_datavecStateREdge[0][c][0][0][0]));
				pDataRef.AttachToData(&(m_dataRefStateREdge[c][0][0][0]));
			} else {
				_EXCEPTIONT("Invalid DataLocation");
			}

		} else if (eDataType == DataType_Tracers) {
			pData.AttachToData(&(m_datavecTracers[0][c][0][0][0]));

		} else if (eDataType == DataType_Topography) {
			pData.AttachToData(&(m_dataTopography[0][0]));

		} else if (eDataType == DataType_Vorticity) {
			pData.AttachToData(&(m_dataVorticity[0][0][0]));

		} else if (eDataType == DataType_Divergence) {
			pData.AttachToData(&(m_dataDivergence[0][0][0]));

		} else if (eDataType == DataType_Temperature) {
			pData.AttachToData(&(m_dataTemperature[0][0][0]));

		} else if (eDataType == DataType_SurfacePressure) {
			pData.AttachToData(&(m_dataSurfacePressure[0][0]));

		} else if (eDataType == DataType_Auxiliary2D) {
			pData.AttachToData(&(m_dataUserData2D[c][0][0]));
		}

		// Loop throught all points
		for (int i = 0; i < dAlpha.GetRows(); i++) {

			// Element index
			if (iPatch[i] != GetPatchIndex()) {
				continue;
			}

			// Verify point lies within domain of patch
			const double Eps = 1.0e-10;
			if ((dAlpha[i] < m_dAEdge[m_box.GetAInteriorBegin()] - Eps) ||
				(dAlpha[i] > m_dAEdge[m_box.GetAInteriorEnd()] + Eps) ||
				(dBeta[i] < m_dBEdge[m_box.GetBInteriorBegin()] - Eps) ||
				(dBeta[i] > m_dBEdge[m_box.GetBInteriorEnd()] + Eps)
			) {
				_EXCEPTIONT("Point out of range");
			}

			// Determine finite element index
			int iA =
				(dAlpha[i] - m_dAEdge[m_box.GetAInteriorBegin()])
					/ GetElementDeltaA();

			int iB =
				(dBeta[i] - m_dBEdge[m_box.GetBInteriorBegin()])
					/ GetElementDeltaB();

			// Bound the index within the element
			if (iA < 0) {
				iA = 0;
			}
			if (iA >= (m_box.GetAInteriorWidth() / m_nHorizontalOrder)) {
				iA = m_box.GetAInteriorWidth() / m_nHorizontalOrder - 1;
			}
			if (iB < 0) {
				iB = 0;
			}
			if (iB >= (m_box.GetBInteriorWidth() / m_nHorizontalOrder)) {
				iB = m_box.GetBInteriorWidth() / m_nHorizontalOrder - 1;
			}

			iA = m_box.GetHaloElements() + iA * m_nHorizontalOrder;
			iB = m_box.GetHaloElements() + iB * m_nHorizontalOrder;

			// Compute interpolation coefficients
			PolynomialInterp::LagrangianPolynomialCoeffs(
				m_nHorizontalOrder,
				&(m_dAEdge[iA]),
				dAInterpCoeffs,
				dAlpha[i]);

			PolynomialInterp::LagrangianPolynomialCoeffs(
				m_nHorizontalOrder,
				&(m_dBEdge[iB]),
				dBInterpCoeffs,
				dBeta[i]);

			// Perform interpolation on all levels
			for (int k = 0; k < nRElements; k++) {

				dColumnData[k] = 0.0;

				// Rescale vertical velocity
				const int WIx = 3;
				if ((c == WIx) && (fConvertToPrimitive)) {
					if (m_grid.GetVarLocation(WIx) == DataLocation_REdge) {
						for (int m = 0; m < m_nHorizontalOrder; m++) {
						for (int n = 0; n < m_nHorizontalOrder; n++) {
							dColumnData[k] +=
								  dAInterpCoeffs[m]
								* dBInterpCoeffs[n]
								* pData[k][iA+m][iB+n]
								/ m_dataDerivRREdge[k][iA][iB][2];
						}
						}

					} else {
						for (int m = 0; m < m_nHorizontalOrder; m++) {
						for (int n = 0; n < m_nHorizontalOrder; n++) {
							dColumnData[k] +=
								  dAInterpCoeffs[m]
								* dBInterpCoeffs[n]
								* pData[k][iA+m][iB+n]
								/ m_dataDerivRNode[k][iA][iB][2];
						}
						}
					}

				} else {
					for (int m = 0; m < m_nHorizontalOrder; m++) {
					for (int n = 0; n < m_nHorizontalOrder; n++) {
						dColumnData[k] +=
							  dAInterpCoeffs[m]
							* dBInterpCoeffs[n]
							* pData[k][iA+m][iB+n];
					}
					}
				}

				// Do not include the reference state
				if ((eDataType == DataType_State) &&
					(!fIncludeReferenceState)
				) {
					for (int m = 0; m < m_nHorizontalOrder; m++) {
					for (int n = 0; n < m_nHorizontalOrder; n++) {
						dColumnData[k] -=
							  dAInterpCoeffs[m]
							* dBInterpCoeffs[n]
							* pDataRef[k][iA+m][iB+n];
					}
					}
				}
			}

			// Interpolate vertically
			opInterp.Apply(
				&(dColumnData[0]),
				&(dColumnDataOut[0]));

			// Store data
			for (int k = 0; k < dREta.GetRows(); k++) {
				dInterpData[c][k][i] = dColumnDataOut[k];
			}

		}
	}

	// Convert to primitive variables
	if ((eDataType == DataType_State) && (fConvertToPrimitive)) {
		for (int i = 0; i < dAlpha.GetRows(); i++) { 
			if (iPatch[i] != GetPatchIndex()) {
				continue;
			}

			for (int k = 0; k < dREta.GetRows(); k++) {
				double dUalpha =
					dInterpData[0][k][i] / phys.GetEarthRadius();
				double dUbeta =
					dInterpData[1][k][i] / phys.GetEarthRadius();

				CubedSphereTrans::CoVecTransRLLFromABP(
					tan(dAlpha[i]),
					tan(dBeta[i]),
					GetPatchBox().GetPanel(),
					dUalpha,
					dUbeta,
					dInterpData[0][k][i],
					dInterpData[1][k][i]);
			}
		}
	}
}