int TimestepSchemeStrang::GetSubStepCount() const {

	HorizontalDynamics * pHorizontalDynamics =
		m_model.GetHorizontalDynamics();

	if (pHorizontalDynamics == NULL) {
		_EXCEPTIONT("HorizontalDynamics has not been initialized");
	}

	int nHorizontalDynamicsSubSteps =
		pHorizontalDynamics->GetSubStepAfterSubCycleCount();

	return (m_nExplicitSubSteps + nHorizontalDynamicsSubSteps + 1);
}
int TimestepSchemeStrang::SubStep(
	bool fFirstStep,
	bool fLastStep,
	const Time & time,
	double dDeltaT,
	int iSubStep
) {
	Grid * pGrid = m_model.GetGrid();

	HorizontalDynamics * pHorizontalDynamics = m_model.GetHorizontalDynamics();

	VerticalDynamics * pVerticalDynamics = m_model.GetVerticalDynamics();

	if (pGrid == NULL) {
		_EXCEPTIONT("Model Grid has not been initialized");
	}
	if (pHorizontalDynamics == NULL) {
		_EXCEPTIONT("Model HorizontalDynamics has not been initialized");
	}
	if (pVerticalDynamics == NULL) {
		_EXCEPTIONT("Model VerticalDynamics has not been initialized");
	}

	// Half a time step
	double dHalfDeltaT = 0.5 * dDeltaT;

	// Number of HorizontalDynamics substeps
	int nHorizontalDynamicsSubSteps =
		pHorizontalDynamics->GetSubStepAfterSubCycleCount();

	// Vertical timestep
	if (iSubStep == 0) {

		// Vertical timestep
		if (fFirstStep) {
			pVerticalDynamics->StepImplicit(0, 0, time, dHalfDeltaT);

		} else {
			pGrid->LinearCombineData(m_dCarryoverCombination, 0, DataType_State);
			pGrid->LinearCombineData(m_dCarryoverCombination, 0, DataType_Tracers);

			pVerticalDynamics->FilterNegativeTracers(0);
		}
	}
	
	// Forward Euler
	if (m_eExplicitDiscretization == ForwardEuler) {
		if (iSubStep == 0) {
			pGrid->CopyData(0, 4, DataType_State);
			pGrid->CopyData(0, 4, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(0, 4, time, dDeltaT);
			pVerticalDynamics->StepExplicit(0, 4, time, dDeltaT);

			return 4;
		}

	// Explicit fourth-order Runge-Kutta
	} else if (m_eExplicitDiscretization == RungeKutta4) {
		if (iSubStep == 0) {
			pGrid->CopyData(0, 1, DataType_State);
			pGrid->CopyData(0, 1, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(0, 1, time, dHalfDeltaT);
			pVerticalDynamics->StepExplicit(0, 1, time, dHalfDeltaT);

			return 1;

		} else if (iSubStep == 1) {
			pGrid->CopyData(0, 2, DataType_State);
			pGrid->CopyData(0, 2, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(1, 2, time, dHalfDeltaT);
			pVerticalDynamics->StepExplicit(1, 2, time, dHalfDeltaT);

			return 2;

		} else if (iSubStep == 2) {
			pGrid->CopyData(0, 3, DataType_State);
			pGrid->CopyData(0, 3, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(2, 3, time, dDeltaT);
			pVerticalDynamics->StepExplicit(2, 3, time, dDeltaT);

			return 3;

		} else if (iSubStep == 3) {
			pGrid->LinearCombineData(m_dRK4Combination, 4, DataType_State);
			pGrid->LinearCombineData(m_dRK4Combination, 4, DataType_Tracers);

			pHorizontalDynamics->StepExplicit(3, 4, time, dDeltaT / 6.0);
			pVerticalDynamics->StepExplicit(3, 4, time, dDeltaT / 6.0);

			return 4;
		}

	// Explicit strong stability preserving third-order Runge-Kutta
	} else if (m_eExplicitDiscretization == RungeKuttaSSP3) {
		if (iSubStep == 0) {
			pGrid->CopyData(0, 1, DataType_State);
			pGrid->CopyData(0, 1, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(0, 1, time, dDeltaT);
			pVerticalDynamics->StepExplicit(0, 1, time, dDeltaT);

			return 1;

		} else if (iSubStep == 1) {
			pGrid->LinearCombineData(m_dSSPRK3CombinationA, 2, DataType_State);
			pGrid->LinearCombineData(m_dSSPRK3CombinationA, 2, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(1, 2, time, 0.25 * dDeltaT);
			pVerticalDynamics->StepExplicit(1, 2, time, 0.25 * dDeltaT);

			return 2;

		} else if (iSubStep == 2) {
			pGrid->LinearCombineData(m_dSSPRK3CombinationB, 4, DataType_State);
			pGrid->LinearCombineData(m_dSSPRK3CombinationB, 4, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(2, 4, time, (2.0/3.0) * dDeltaT);
			pVerticalDynamics->StepExplicit(2, 4, time, (2.0/3.0) * dDeltaT);

			return 4;
		}

	// Explicit Kinnmark, Gray and Ullrich third-order five-stage Runge-Kutta
	} else if (m_eExplicitDiscretization == KinnmarkGrayUllrich35) {
		if (iSubStep == 0) {
			pGrid->CopyData(0, 1, DataType_State);
			pGrid->CopyData(0, 1, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(0, 1, time, dDeltaT / 5.0);
			pVerticalDynamics->StepExplicit(0, 1, time, dDeltaT / 5.0);

			return 1;

		} else if (iSubStep == 1) {
			pGrid->CopyData(0, 2, DataType_State);
			pGrid->CopyData(0, 2, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(1, 2, time, dDeltaT / 5.0);
			pVerticalDynamics->StepExplicit(1, 2, time, dDeltaT / 5.0);

			return 2;

		} else if (iSubStep == 2) {
			pGrid->CopyData(0, 3, DataType_State);
			pGrid->CopyData(0, 3, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(2, 3, time, dDeltaT / 3.0);
			pVerticalDynamics->StepExplicit(2, 3, time, dDeltaT / 3.0);

			return 3;

		} else if (iSubStep == 3) {
			pGrid->CopyData(0, 2, DataType_State);
			pGrid->CopyData(0, 2, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(3, 2, time, 2.0 * dDeltaT / 3.0);
			pVerticalDynamics->StepExplicit(3, 2, time, 2.0 * dDeltaT / 3.0);

			return 2;

		} else if (iSubStep == 4) {
			pGrid->LinearCombineData(
				m_dKinnmarkGrayUllrichCombination, 4, DataType_State);
			pGrid->LinearCombineData(
				m_dKinnmarkGrayUllrichCombination, 4, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(2, 4, time, 3.0 * dDeltaT / 4.0);
			pVerticalDynamics->StepExplicit(2, 4, time, 3.0 * dDeltaT / 4.0);

			return 4;
		}

	// Explicit strong stability preserving five-stage third-order Runge-Kutta
	} else if (m_eExplicitDiscretization == RungeKuttaSSPRK53) {
		if (iSubStep == 0) {
			const double dStepOne = 0.377268915331368;

			pGrid->CopyData(0, 1, DataType_State);
			pGrid->CopyData(0, 1, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(0, 1, time, dStepOne * dDeltaT);
			pVerticalDynamics->StepExplicit(0, 1, time, dStepOne * dDeltaT);

			return 1;

		} else if (iSubStep == 1) {
			const double dStepOne = 0.377268915331368;

			pGrid->CopyData(1, 2, DataType_State);
			pGrid->CopyData(1, 2, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(1, 2, time, dStepOne * dDeltaT);
			pVerticalDynamics->StepExplicit(1, 2, time, dStepOne * dDeltaT);

			return 2;

		} else if (iSubStep == 2) {
			const double dStepThree = 0.242995220537396;

			pGrid->LinearCombineData(m_dSSPRK53CombinationA, 3, DataType_State);
			pGrid->LinearCombineData(m_dSSPRK53CombinationA, 3, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(2, 3, time, dStepThree * dDeltaT);
			pVerticalDynamics->StepExplicit(2, 3, time, dStepThree * dDeltaT);

			return 3;

		} else if (iSubStep == 3) {
			const double dStepFour = 0.238458932846290;

			pGrid->LinearCombineData(m_dSSPRK53CombinationB, 0, DataType_State);
			pGrid->LinearCombineData(m_dSSPRK53CombinationB, 0, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(3, 0, time, dStepFour * dDeltaT);
			pVerticalDynamics->StepExplicit(3, 0, time, dStepFour * dDeltaT);

			return 0;

		} else if (iSubStep == 4) {
			const double dStepFive = 0.287632146308408;

			pGrid->LinearCombineData(m_dSSPRK53CombinationC, 4, DataType_State);
			pGrid->LinearCombineData(m_dSSPRK53CombinationC, 4, DataType_Tracers);
			pHorizontalDynamics->StepExplicit(0, 4, time, dStepFive * dDeltaT);
			pVerticalDynamics->StepExplicit(0, 4, time, dStepFive * dDeltaT);

			return 4;
		}

	// Fixers, filters and diffusion
	} else if (
		(iSubStep >= m_nExplicitSubSteps) &&
	    (iSubStep <  m_nExplicitSubSteps + nHorizontalDynamicsSubSteps)
	) {
		return pHorizontalDynamics->SubStepAfterSubCycle(
			4, 1, 2, time, dDeltaT, iSubStep - m_nExplicitSubSteps);

	// Vertical timestep
	} else if (
		iSubStep == m_nExplicitSubSteps + nHorizontalDynamicsSubSteps
	) {
		pGrid->CopyData(1, 0, DataType_State);
		pGrid->CopyData(1, 0, DataType_Tracers);
		pVerticalDynamics->StepImplicit(0, 0, time, 0.5 * dDeltaT);

		if (!fLastStep) {
			pGrid->LinearCombineData(m_dCarryoverFinal, 1, DataType_State);
			pGrid->LinearCombineData(m_dCarryoverFinal, 1, DataType_Tracers);
		}

#pragma message "Merge this vertical timestep in above"
		return (-1);

	}
	
	// Invalid substep
	_EXCEPTIONT("Invalid iSubStep");
}