docs: private void doPrecalculateProcess() {
  		if (discreteProcess != null && discreteProcess.length != 0) {
  			return;
  		}
  
  		final int numberOfPaths			= this.getNumberOfPaths();
  		final int numberOfFactors		= this.getNumberOfFactors();
  		final int numberOfComponents	= this.getNumberOfComponents();
  
  		// Allocate Memory
  		discreteProcess			= new RandomVariable[getTimeDiscretization().getNumberOfTimeSteps() + 1][getNumberOfComponents()];
  		discreteProcessWeights	= new RandomVariable[getTimeDiscretization().getNumberOfTimeSteps() + 1];
  
  		// Set initial Monte-Carlo weights
  		discreteProcessWeights[0] = stochasticDriver.getRandomVariableForConstant(1.0 / numberOfPaths);
  
  		// Set initial value
  		RandomVariable[] initialState = getInitialState();
  		final RandomVariable[] currentState = new RandomVariable[numberOfComponents];
  		for (int componentIndex = 0; componentIndex < numberOfComponents; componentIndex++) {
  			currentState[componentIndex] = initialState[componentIndex];
  			discreteProcess[0][componentIndex] = applyStateSpaceTransform(componentIndex, currentState[componentIndex]);
  		}
  
  		/*
  		 * Evolve the process using an Euler scheme.
  		 * The evolution is performed multi-threadded.
  		 * Each component of the vector runs in its own thread.
  		 */
  		executor = Executors.newCachedThreadPool();
  
  		// Evolve process
  		for (int timeIndex2 = 1; timeIndex2 < getTimeDiscretization().getNumberOfTimeSteps()+1; timeIndex2++) {
  			final int timeIndex = timeIndex2;
  			// Generate process from timeIndex-1 to timeIndex
  			final double deltaT = getTime(timeIndex) - getTime(timeIndex - 1);
  
  			// Fetch drift vector
  			final RandomVariable[] drift;
  			try {
  				drift = getDrift(timeIndex - 1, discreteProcess[timeIndex - 1], null);
  			}
  			catch(Exception e) {
  				throw new RuntimeException("Drift calculaton failed at time index " + timeIndex + " (time=" + getTime(timeIndex - 1) + ") . See cause of this exception for details.", e);
  			}
  
  			// Fetch brownianIncrement vector
  			final RandomVariable[] brownianIncrement	= stochasticDriver.getIncrement(timeIndex - 1);
  
  			// Calculate new realization
  			ArrayList<Future<RandomVariable>> discreteProcessAtCurrentTimeIndex = new ArrayList<>(numberOfComponents);
  			for (int componentIndex2 = 0; componentIndex2 < numberOfComponents; componentIndex2++) {
  				final int componentIndex = componentIndex2;
  
  				final RandomVariable	driftOfComponent	= drift[componentIndex];
  
  				// Check if the component process has stopped to evolve
  				if (driftOfComponent == null) {
  					discreteProcessAtCurrentTimeIndex.add(componentIndex, null);
  					continue;
  				}
  
  				Callable<RandomVariable> worker = new  Callable<RandomVariable>() {
  					@Override
  					public RandomVariable call() {
  						if(scheme == Scheme.EULER_FUNCTIONAL) {
  							currentState[componentIndex] = applyStateSpaceTransformInverse(componentIndex, discreteProcess[timeIndex - 1][componentIndex]);
  						}
  
  						RandomVariable[]	factorLoadings		= getFactorLoading(timeIndex - 1, componentIndex, discreteProcess[timeIndex - 1]);
  
  						// Check if the component process has stopped to evolve
  						if (factorLoadings == null) {
  							return null;
  						}
  
  						// Apply drift
  						if(driftOfComponent != null) {
  							currentState[componentIndex] = currentState[componentIndex].addProduct(driftOfComponent, deltaT);
  						}
  
  						// Apply diffusion
  						currentState[componentIndex] = currentState[componentIndex].addSumProduct(factorLoadings, brownianIncrement);
  
  						// Transform the state space to the value space and return it.
  						return applyStateSpaceTransform(componentIndex, currentState[componentIndex]).cache();
  					}
  				};
  
  
  				/*
  				 * Optional multi-threadding (asyncronous calculation of the components)
  				 */
  				Future<RandomVariable> result = null;
  				try {
  					if(isUseMultiThreadding) {
  						result = executor.submit(worker);
  					} else {
  						result = new FutureWrapper<>(worker.call());
  					}
  				} catch (Exception e) {
  					throw new RuntimeException("Euler step failed at time index " + timeIndex + " (time=" + getTime(timeIndex) + "). See cause of this exception for details.", e);
  				}
  
  				// The following line will add the result of the calculation to the vector discreteProcessAtCurrentTimeIndex
  				discreteProcessAtCurrentTimeIndex.add(componentIndex, result);
  			}
  
  			// Fetch results and move to discreteProcess[timeIndex]
  			for (int componentIndex = 0; componentIndex < numberOfComponents; componentIndex++) {
  				try {
  					Future<RandomVariable> discreteProcessAtCurrentTimeIndexAndComponent = discreteProcessAtCurrentTimeIndex.get(componentIndex);
  					if(discreteProcessAtCurrentTimeIndexAndComponent != null) {
  						discreteProcess[timeIndex][componentIndex] = discreteProcessAtCurrentTimeIndexAndComponent.get().cache();
  					} else {
  						discreteProcess[timeIndex][componentIndex] = discreteProcess[timeIndex-1][componentIndex];
  					}
  				} catch (InterruptedException e) {
  					throw new RuntimeException("Euler step failed at time index " + timeIndex + " (time=" + getTime(timeIndex) + "). See cause of this exception for details.", e);
  				} catch (ExecutionException e) {
  					throw new RuntimeException("Euler step failed at time index " + timeIndex + " (time=" + getTime(timeIndex) + "). See cause of this exception for details.", e);
  				}
  			}
  
  			if (scheme == Scheme.PREDICTOR_CORRECTOR) {
  				// Apply corrector step to realizations at next time step
  
  				RandomVariable[] driftWithPredictor = getDrift(timeIndex - 1, discreteProcess[timeIndex], null);
  
  				for (int componentIndex = 0; componentIndex < getNumberOfComponents(); componentIndex++) {
  					RandomVariable driftWithPredictorOfComponent		= driftWithPredictor[componentIndex];
  					RandomVariable driftWithoutPredictorOfComponent	= drift[componentIndex];
  
  					if (driftWithPredictorOfComponent == null || driftWithoutPredictorOfComponent == null) {
  						continue;
  					}
  
  					// Calculated the predictor corrector drift adjustment
  					RandomVariable driftAdjustment = driftWithPredictorOfComponent.sub(driftWithoutPredictorOfComponent).div(2.0).mult(deltaT);
  
  					// Add drift adjustment
  					currentState[componentIndex] = currentState[componentIndex].add(driftAdjustment);
  
  					// Re-apply state space transform
  					discreteProcess[timeIndex][componentIndex] = applyStateSpaceTransform(componentIndex, currentState[componentIndex]);
  				} // End for(componentIndex)
  			} // End if(scheme == Scheme.PREDICTOR_CORRECTOR)
  
  			// Set Monte-Carlo weights
  			discreteProcessWeights[timeIndex] = discreteProcessWeights[timeIndex - 1];
  		} // End for(timeIndex)
  
  		try {
  			executor.shutdown();
  		}
  		catch(SecurityException e) {
  			// @TODO Improve exception handling here
  		}
  	}