Exemple #1
0
        /// <summary>
        /// L2 error with respect to given reference solution. The quadrature
        /// is determined from the settings in <see cref="IBMControl"/>
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="referenceSolution"></param>
        /// <returns></returns>
        public static Query L2Error(string fieldName, Func <double[], double, double> referenceSolution)
        {
            return(delegate(IApplication <AppControl> app, double time) {
                IProgram <CNSControl> program = app as IProgram <CNSControl>;
                if (program == null)
                {
                    throw new Exception();
                }

                ImmersedSpeciesMap speciesMap = program.SpeciesMap as ImmersedSpeciesMap;
                IBMControl control = program.Control as IBMControl;
                if (speciesMap == null || control == null)
                {
                    throw new Exception(
                        "Query is only valid for immersed boundary runs");
                }

                SpeciesId species = speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName);
                int order = control.LevelSetQuadratureOrder;
                CellQuadratureScheme scheme = speciesMap.QuadSchemeHelper.GetVolumeQuadScheme(
                    species, true, speciesMap.SubGrid.VolumeMask);

                DGField dgField = app.IOFields.Single(f => f.Identification == fieldName);

                return dgField.L2Error(referenceSolution.Vectorize(time), order, scheme);
            });
        }
Exemple #2
0
        public static Query Integral(string fieldName)
        {
            return(delegate(IApplication <AppControl> app, double time) {
                IProgram <CNSControl> program = app as IProgram <CNSControl>;
                if (program == null)
                {
                    throw new Exception();
                }

                ImmersedSpeciesMap speciesMap = program.SpeciesMap as ImmersedSpeciesMap;
                IBMControl control = program.Control as IBMControl;
                if (speciesMap == null || control == null)
                {
                    throw new Exception(
                        "Query is only valid for immersed boundary runs");
                }

                SpeciesId species = speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName);
                int order = control.LevelSetQuadratureOrder;
                CellQuadratureScheme scheme = speciesMap.QuadSchemeHelper.GetVolumeQuadScheme(
                    species, true, speciesMap.SubGrid.VolumeMask);

                DGField dgField = app.IOFields.Single(f => f.Identification == fieldName);

                return DGField.IntegralOverEx(scheme, new Func((X, U, j) => (U[0])), 2, dgField);
            });
        }
Exemple #3
0
        /// <summary>
        /// Calculates a force along the zero iso surface of the Level-Set
        /// </summary>
        /// <param name="direction">Direction of the force projection, e.g. 0=x-axis, 1=y-axis</param>
        /// <returns></returns>
        static Query LiftOrDragForce(int direction)
        {
            return(delegate(IApplication <AppControl> app, double time) {
                IProgram <CNSControl> program = app as IProgram <CNSControl>;
                if (program == null)
                {
                    throw new Exception();
                }

                ImmersedSpeciesMap speciesMap = program.SpeciesMap as ImmersedSpeciesMap;
                IBMControl control = program.Control as IBMControl;
                if (speciesMap == null || control == null)
                {
                    throw new Exception(
                        "Query is only valid for immersed boundary runs");
                }
                DGField density = program.WorkingSet.Density;
                VectorField <DGField> momentum = program.WorkingSet.Momentum;
                DGField energy = program.WorkingSet.Energy;

                return XDGUtils.GetIntegralOverZeroLevelSet(
                    speciesMap.Tracker,
                    GetSurfaceForce(
                        density,
                        momentum,
                        energy,
                        speciesMap,
                        direction,
                        speciesMap.Tracker.Regions.GetCutCellMask()),
                    control.LevelSetQuadratureOrder,
                    speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName));
            });
        }
Exemple #4
0
        public IBMABevolve(
            SpatialOperator standardOperator,
            SpatialOperator boundaryOperator,
            CoordinateMapping fieldsMap,
            CoordinateMapping parametersMap,
            ISpeciesMap ibmSpeciesMap,
            int explicitOrder,
            int levelSetQuadratureOrder,
            XQuadFactoryHelper.MomentFittingVariants momentFittingVariant,
            SubGrid sgrd,
            bool adaptive = false)
            : base(standardOperator, fieldsMap, parametersMap, explicitOrder, adaptive: adaptive, sgrd: sgrd)
        {
            speciesMap = ibmSpeciesMap as ImmersedSpeciesMap;
            if (speciesMap == null)
            {
                throw new ArgumentException(
                          "Only supported for species maps of type 'ImmersedSpeciesMap'",
                          "speciesMap");
            }

            this.boundaryOperator          = boundaryOperator;
            this.boundaryParameterMap      = parametersMap;
            agglomerationPatternHasChanged = true;
        }
        public IBMAdamsBashforth(
            SpatialOperator standardOperator,
            SpatialOperator boundaryOperator,
            CoordinateMapping fieldsMap,
            CoordinateMapping parametersMap,
            ISpeciesMap ibmSpeciesMap,
            IBMControl control,
            IList <TimeStepConstraint> timeStepConstraints)
            : base(standardOperator, fieldsMap, parametersMap, control.ExplicitOrder, timeStepConstraints, ibmSpeciesMap.SubGrid)    // TO DO: I SIMPLY REMOVED PARAMETERMAP HERE; MAKE THIS MORE PRETTY
        {
            speciesMap = ibmSpeciesMap as ImmersedSpeciesMap;
            if (this.speciesMap == null)
            {
                throw new ArgumentException(
                          "Only supported for species maps of type 'ImmersedSpeciesMap'",
                          "speciesMap");
            }

            this.boundaryOperator          = boundaryOperator;
            this.boundaryParameterMap      = parametersMap;
            agglomerationPatternHasChanged = true;

            // StarUp Phase needs also an IBM time stepper
            RungeKuttaScheme = new IBMSplitRungeKutta(
                standardOperator,
                boundaryOperator,
                fieldsMap,
                parametersMap,
                speciesMap,
                timeStepConstraints);
        }
Exemple #6
0
 /// <summary>
 /// ctor.
 /// </summary>
 public IBMMassMatrixFactory(ImmersedSpeciesMap speciesMap, CoordinateMapping mapping, string fluidSpeciesName, int quadOrder)
 {
     this.Mapping            = mapping;
     this.SpeciesMap         = speciesMap;
     this.m_FluidSpeciesName = fluidSpeciesName;
     this.m_quadOrder        = quadOrder;
     speciesMap.Tracker.Subscribe(this);
 }
Exemple #7
0
 /// <summary>
 /// <see cref="FluxBuilder"/>
 /// </summary>
 /// <param name="control"><see cref="FluxBuilder"/>
 /// <see cref="FluxBuilder"/>
 /// </param>
 /// <param name="boundaryMap"><see cref="FluxBuilder"/>
 /// <see cref="FluxBuilder"/>
 /// </param>
 /// <param name="speciesMap">
 /// <see cref="FluxBuilder"/>
 /// </param>
 public MovingFrameRusanovFluxBuilder(CNSControl control, CompressibleBoundaryCondMap boundaryMap, ISpeciesMap speciesMap)
     : base(control, boundaryMap, speciesMap)
 {
     this.ibmSpeciesMap = speciesMap as ImmersedSpeciesMap;
     if (ibmSpeciesMap == null)
     {
         throw new System.Exception();
     }
 }
        /// <summary>
        /// Constructs a new constraint
        /// </summary>
        /// <param name="config"></param>
        /// <param name="gridData"></param>
        /// <param name="workingSet"></param>
        /// <param name="speciesMap"></param>
        public IBMDiffusiveCFLConstraint(
            CNSControl config, GridData gridData, CNSFieldSet workingSet, ISpeciesMap speciesMap)
            : base(gridData, workingSet)
        {
            this.config     = config;
            this.speciesMap = speciesMap as ImmersedSpeciesMap;

            if (gridData.Grid.RefElements.Length > 1)
            {
                throw new NotImplementedException();
            }
        }
        public static IBMRungeKutta CreateRungeKuttaTimeStepper(
            this TimesteppingStrategies strategy,
            IBMControl control,
            OperatorFactory equationSystem,
            CNSFieldSet fieldSet,
            CoordinateMapping parameterMap,
            ISpeciesMap speciesMap,
            IList <TimeStepConstraint> timeStepConstraints)
        {
            ImmersedSpeciesMap ibmSpeciesMap = speciesMap as ImmersedSpeciesMap;

            if (ibmSpeciesMap == null)
            {
                throw new ArgumentException(
                          "Only supported for species maps of type 'ImmersedSpeciesMap'",
                          "speciesMap");
            }

            IBMOperatorFactory ibmFactory = equationSystem as IBMOperatorFactory;

            if (ibmFactory == null)
            {
                throw new Exception();
            }

            CoordinateMapping variableMap = new CoordinateMapping(fieldSet.ConservativeVariables);

            switch (strategy)
            {
            case TimesteppingStrategies.LieSplitting:
            case TimesteppingStrategies.StrangSplitting:
                return(new IBMSplitRungeKutta(
                           equationSystem.GetJoinedOperator().ToSpatialOperator(fieldSet),
                           ibmFactory.GetImmersedBoundaryOperator().ToSpatialOperator(fieldSet),
                           variableMap,
                           parameterMap,
                           ibmSpeciesMap,
                           timeStepConstraints));

            case TimesteppingStrategies.MovingFrameFlux:
                return(new IBMMovingFrameRungeKutta(
                           equationSystem.GetJoinedOperator().ToSpatialOperator(fieldSet),
                           ibmFactory.GetImmersedBoundaryOperator().ToSpatialOperator(fieldSet),
                           variableMap,
                           parameterMap,
                           ibmSpeciesMap,
                           timeStepConstraints));

            default:
                throw new System.NotImplementedException();
            }
        }
Exemple #10
0
 public IBMRungeKutta(
     SpatialOperator standardOperator,
     SpatialOperator boundaryOperator,
     CoordinateMapping fieldsMap,
     CoordinateMapping parametersMap,
     ImmersedSpeciesMap speciesMap,
     IList <TimeStepConstraint> timeStepConstraints)
     : base(RungeKutta.GetDefaultScheme(speciesMap.Control.ExplicitOrder), standardOperator, fieldsMap, parametersMap, timeStepConstraints, speciesMap.SubGrid)
 {
     this.speciesMap           = speciesMap;
     this.boundaryOperator     = boundaryOperator;
     this.boundaryParameterMap = parametersMap;
 }
        /// <summary>
        /// Constructs a constraint that respects the given
        /// <paramref name="speciesMap"/>
        /// </summary>
        /// <param name="config"></param>
        /// <param name="gridData"></param>
        /// <param name="workingSet"></param>
        /// <param name="speciesMap"></param>
        public IBMConvectiveCFLConstraint(
            CNSControl config, GridData gridData, CNSFieldSet workingSet, ISpeciesMap speciesMap)
            : base(gridData, workingSet)
        {
            this.config     = config;
            this.speciesMap = speciesMap as ImmersedSpeciesMap;

            if (speciesMap == null)
            {
                throw new ArgumentException(
                          "This type requires an instance of 'ImmersedSpeciesMap'",
                          "speciesMap");
            }
        }
        public IBMAdamsBashforthLTS(SpatialOperator standardOperator, SpatialOperator boundaryOperator, CoordinateMapping fieldsMap, CoordinateMapping boundaryParameterMap, ISpeciesMap ibmSpeciesMap, IBMControl control, IList <TimeStepConstraint> timeStepConstraints, int reclusteringInterval, bool fluxCorrection)
            : base(standardOperator, fieldsMap, boundaryParameterMap, control.ExplicitOrder, control.NumberOfSubGrids, true, timeStepConstraints, reclusteringInterval: reclusteringInterval, fluxCorrection: fluxCorrection, subGrid: ibmSpeciesMap.SubGrid)
        {
            this.speciesMap = ibmSpeciesMap as ImmersedSpeciesMap;
            if (this.speciesMap == null)
            {
                throw new ArgumentException(
                          "Only supported for species maps of type 'ImmersedSpeciesMap'",
                          "speciesMap");
            }
            this.standardOperator     = standardOperator;
            this.boundaryOperator     = boundaryOperator;
            this.boundaryParameterMap = boundaryParameterMap;
            this.fieldsMap            = fieldsMap;
            this.control = control;

            agglomerationPatternHasChanged = true;

            cutCells          = speciesMap.Tracker.Regions.GetCutCellMask();
            cutAndTargetCells = cutCells.Union(speciesMap.Agglomerator.AggInfo.TargetCells);

            // Normal LTS constructor
            NumberOfLocalTimeSteps = new List <int>(control.NumberOfSubGrids);

            clusterer = new Clusterer(this.gridData, this.TimeStepConstraints);

            CurrentClustering = clusterer.CreateClustering(control.NumberOfSubGrids, speciesMap.SubGrid);
            CurrentClustering = CalculateNumberOfLocalTS(CurrentClustering); // Might remove sub-grids when time step sizes are too similar

            ABevolver = new IBMABevolve[CurrentClustering.NumberOfClusters];

            for (int i = 0; i < ABevolver.Length; i++)
            {
                ABevolver[i] = new IBMABevolve(standardOperator, boundaryOperator, fieldsMap, boundaryParameterMap, speciesMap, control.ExplicitOrder, control.LevelSetQuadratureOrder, control.CutCellQuadratureType, sgrd: CurrentClustering.Clusters[i], adaptive: this.adaptive);
                ABevolver[i].OnBeforeComputeChangeRate += (t1, t2) => this.RaiseOnBeforeComputechangeRate(t1, t2);
            }

            GetBoundaryTopology();

#if DEBUG
            for (int i = 0; i < CurrentClustering.NumberOfClusters; i++)
            {
                Console.WriteLine("IBM AB LTS ctor: id=" + i + " -> sub-steps=" + NumberOfLocalTimeSteps[i] + " and elements=" + CurrentClustering.Clusters[i].GlobalNoOfCells);
            }
#endif

            // Start-up phase needs an IBM Runge-Kutta time stepper
            RungeKuttaScheme = new IBMSplitRungeKutta(standardOperator, boundaryOperator, fieldsMap, boundaryParameterMap, speciesMap, timeStepConstraints);
        }
Exemple #13
0
        internal static double GetMassMatrixDeterminant(ImmersedSpeciesMap speciesMap, CoordinateMapping mapping, CellMask cellMask)
        {
            BlockMsrMatrix massMatrix = speciesMap.GetMassMatrixFactory(mapping).MassMatrix;
            Basis          maxBasis   = mapping.BasisS.ElementAtMax(b => b.Degree);

            MultidimensionalArray subMatrix = MultidimensionalArray.Create(
                cellMask.NoOfItemsLocally + 1, maxBasis.Length, cellMask.NoOfItemsLocally + 1, maxBasis.Length);
            int cellIndex = 0;

            foreach (Chunk chunk in cellMask)
            {
                for (int i = 0; i < chunk.Len; i++)
                {
                    //IMatrix block = massMatrix.GetBlock(i + chunk.i0);
                    MultidimensionalArray block = GetBlock(massMatrix, i + chunk.i0);
                    for (int j = 0; j < maxBasis.Length; j++)
                    {
                        for (int k = 0; k < maxBasis.Length; k++)
                        {
                            subMatrix[cellIndex, j, cellIndex, k] = block[j, k];
                        }
                    }

                    cellIndex++;
                }
            }

            for (int j = 0; j < maxBasis.Length; j++)
            {
                subMatrix[cellIndex, j, cellIndex, j] = 1.0;
            }

            subMatrix = subMatrix.ResizeShallow(
                (cellMask.NoOfItemsLocally + 1) * maxBasis.Length,
                (cellMask.NoOfItemsLocally + 1) * maxBasis.Length);

            return(subMatrix.cond());
        }
Exemple #14
0
        internal static double GetMaxLocalMassMatrixDeterminant(ImmersedSpeciesMap speciesMap, CoordinateMapping mapping, CellMask cellMask, out int maxCondCell)
        {
            BlockMsrMatrix massMatrix = speciesMap.GetMassMatrixFactory(mapping).MassMatrix;
            Basis          maxBasis   = mapping.BasisS.ElementAtMax(b => b.Degree);

            MultidimensionalArray subMatrix = MultidimensionalArray.Create(
                maxBasis.Length, maxBasis.Length);

            double maxCond = 0.0;

            maxCondCell = -1;
            foreach (Chunk chunk in cellMask)
            {
                for (int i = 0; i < chunk.Len; i++)
                {
                    //IMatrix block = massMatrix.GetBlock(i + chunk.i0);
                    MultidimensionalArray block = GetBlock(massMatrix, i + chunk.i0);
                    for (int j = 0; j < maxBasis.Length; j++)
                    {
                        for (int k = 0; k < maxBasis.Length; k++)
                        {
                            subMatrix[j, k] = block[j, k];
                        }
                    }

                    double cond = subMatrix.cond();
                    if (cond > maxCond)
                    {
                        maxCond     = cond;
                        maxCondCell = i + chunk.i0;
                    }
                }
            }

            return(maxCond);
        }
 public IBMMovingFrameRungeKutta(SpatialOperator standardOperator, SpatialOperator boundaryOperator, CoordinateMapping fieldsMap, CoordinateMapping parametersMap, ImmersedSpeciesMap speciesMap, IList <TimeStepConstraint> timeStepConstraints)
     : base(standardOperator, boundaryOperator, fieldsMap, parametersMap, speciesMap, timeStepConstraints)
 {
     if (speciesMap.Control.DomainType != DomainTypes.MovingImmersedBoundary)
     {
         throw new Exception();
     }
 }
Exemple #16
0
 public IBMSplitRungeKutta(SpatialOperator standardOperator, SpatialOperator boundaryOperator, CoordinateMapping fieldsMap, CoordinateMapping parametersMap, ImmersedSpeciesMap speciesMap, IList <TimeStepConstraint> timeStepConstraints)
     : base(standardOperator, boundaryOperator, fieldsMap, parametersMap, speciesMap, timeStepConstraints)
 {
     if (speciesMap.Control.TimesteppingStrategy == TimesteppingStrategies.StrangSplitting)
     {
         throw new System.NotImplementedException();
     }
 }
Exemple #17
0
 public MovingFrameRusanovFlux(CompressibleControl config, IBoundaryConditionMap boundaryMap, IEulerEquationComponent equationComponent, ImmersedSpeciesMap speciesMap)
     : base(config, boundaryMap, equationComponent, speciesMap.GetMaterial(double.NaN))
 {
     this.levelSetVelocity = speciesMap.Control.LevelSetVelocity;
 }
Exemple #18
0
        /// <summary>
        /// L2 error of some quantity derived from the state vector (e.g.,
        /// entropy) with respect to given reference solution. The quadrature
        /// is determined from the settings in <see cref="IBMControl"/>
        /// </summary>
        /// <param name="quantityOfInterest"></param>
        /// <param name="referenceSolution"></param>
        /// <returns></returns>
        public static Query L2Error(Func <StateVector, double> quantityOfInterest, Func <double[], double, double> referenceSolution)
        {
            return(delegate(IApplication <AppControl> app, double time) {
                IProgram <CNSControl> program = app as IProgram <CNSControl>;
                if (program == null)
                {
                    throw new Exception();
                }

                ImmersedSpeciesMap speciesMap = program.SpeciesMap as ImmersedSpeciesMap;
                IBMControl control = program.Control as IBMControl;
                if (speciesMap == null || control == null)
                {
                    throw new Exception(
                        "Query is only valid for immersed boundary runs");
                }

                SpeciesId species = speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName);
                int order = control.LevelSetQuadratureOrder;
                CellQuadratureScheme scheme = speciesMap.QuadSchemeHelper.GetVolumeQuadScheme(
                    species, true, speciesMap.SubGrid.VolumeMask);
                var composititeRule = scheme.Compile(program.GridData, order);
                IChunkRulePair <QuadRule>[] chunkRulePairs = composititeRule.ToArray();

                DGField density = program.WorkingSet.Density;
                VectorField <DGField> momentum = program.WorkingSet.Momentum;
                DGField energy = program.WorkingSet.Energy;

                // Construct dummy field since L2Error is currently only supported
                // for Field's; However, _avoid_ a projection.
                DGField dummy = new SinglePhaseField(new Basis(program.GridData, 0));
                Material material = speciesMap.GetMaterial(double.NaN);
                int index = 0;
                double value = dummy.LxError(
                    (ScalarFunctionEx) delegate(int j0, int Len, NodeSet nodes, MultidimensionalArray result) {
                    MultidimensionalArray input = program.GridData.GlobalNodes.GetValue_Cell(nodes, j0, Len);

                    Chunk chunk = chunkRulePairs[index].Chunk;
                    QuadRule rule = chunkRulePairs[index].Rule;

                    if (chunk.i0 != j0 || chunk.Len != Len)
                    {
                        throw new Exception();
                    }

                    if (rule.NoOfNodes != nodes.GetLength(0))
                    {
                        throw new Exception();
                    }

                    MultidimensionalArray rho = MultidimensionalArray.Create(chunk.Len, rule.NoOfNodes);
                    density.Evaluate(chunk.i0, chunk.Len, nodes, rho);

                    MultidimensionalArray[] m = new MultidimensionalArray[CNSEnvironment.NumberOfDimensions];
                    for (int d = 0; d < CNSEnvironment.NumberOfDimensions; d++)
                    {
                        m[d] = MultidimensionalArray.Create(chunk.Len, rule.NoOfNodes);
                        momentum[d].Evaluate(chunk.i0, chunk.Len, nodes, m[d]);
                    }

                    MultidimensionalArray rhoE = MultidimensionalArray.Create(chunk.Len, rule.NoOfNodes);
                    energy.Evaluate(chunk.i0, chunk.Len, nodes, rhoE);

                    double[] X = new double[CNSEnvironment.NumberOfDimensions];
                    Vector3D mVec = new Vector3D();
                    for (int i = 0; i < chunk.Len; i++)
                    {
                        for (int j = 0; j < rule.NoOfNodes; j++)
                        {
                            for (int d = 0; d < CNSEnvironment.NumberOfDimensions; d++)
                            {
                                X[d] = input[i, j, d];
                                mVec[d] = m[d][i, j];
                            }

                            StateVector state = new StateVector(material, rho[i, j], mVec, rhoE[i, j]);
                            double qoi = quantityOfInterest(state);

                            Debug.Assert(
                                !double.IsNaN(qoi),
                                "Encountered node with unphysical state"
                                + " (not able to determine quantity of interest)");

                            result[i, j] = qoi - referenceSolution(X, time);
                        }
                    }

                    index++;
                },
                    (X, a, b) => (a - b) * (a - b),
                    composititeRule);

                // No value is NaN, but the results. How can this be?
                // => All values around 0, but values in void region are a little
                // farther away from the exact solution
                // => weights in the void zone sum up to something slightly negative
                Debug.Assert(
                    value >= 0,
                    "Encountered unphysical norm even though individual values where valid."
                    + " This indicates a problem with cut-cell quadrature.");

                return Math.Sqrt(value);
            });
        }
Exemple #19
0
        /// <summary>
        /// Defines the force which is integrated over an immersed boundary,
        /// called by <see cref="IBMQueries.LiftOrDragForce"/>
        /// </summary>
        /// <param name="density"></param>
        /// <param name="momentum"></param>
        /// <param name="energy"></param>
        /// <param name="speciesMap"></param>
        /// <param name="direction">Direction of the force projection, e.g. 0=x-axis, 1=y-axis</param>
        /// <param name="cutCellMask">Cells intersected by the interface</param>
        /// <returns></returns>
        static ScalarFunctionEx GetSurfaceForce(
            DGField density, VectorField <DGField> momentum, DGField energy, ImmersedSpeciesMap speciesMap, int direction, CellMask cutCellMask)
        {
            return(delegate(int j0, int Len, NodeSet nodes, MultidimensionalArray result) {
                int noOfNodes = nodes.GetLength(0);
                int D = nodes.SpatialDimension;

                double Reynolds = speciesMap.Control.ReynoldsNumber;
                double Mach = speciesMap.Control.MachNumber;
                double gamma = speciesMap.Control.EquationOfState.HeatCapacityRatio;

                double MachScaling = gamma * Mach * Mach;


                MultidimensionalArray rho = MultidimensionalArray.Create(Len, noOfNodes);
                density.Evaluate(j0, Len, nodes, rho);

                MultidimensionalArray[] m = new MultidimensionalArray[CNSEnvironment.NumberOfDimensions];
                for (int d = 0; d < CNSEnvironment.NumberOfDimensions; d++)
                {
                    m[d] = MultidimensionalArray.Create(Len, noOfNodes);
                    momentum[d].Evaluate(j0, Len, nodes, m[d]);
                }

                MultidimensionalArray rhoE = MultidimensionalArray.Create(Len, noOfNodes);
                energy.Evaluate(j0, Len, nodes, rhoE);

                MultidimensionalArray gradRho = MultidimensionalArray.Create(Len, noOfNodes, D);
                density.EvaluateGradient(j0, Len, nodes, gradRho);

                MultidimensionalArray gradM = MultidimensionalArray.Create(Len, noOfNodes, D, D);
                for (int d = 0; d < D; d++)
                {
                    momentum[d].EvaluateGradient(
                        j0,
                        Len,
                        nodes,
                        gradM.ExtractSubArrayShallow(-1, -1, d, -1),
                        0,
                        0.0);
                }

                MultidimensionalArray normals = speciesMap.Tracker.DataHistories[0].Current.GetLevelSetNormals(nodes, j0, Len);

                Vector3D mVec = new Vector3D();
                for (int i = 0; i < Len; i++)
                {
                    for (int j = 0; j < noOfNodes; j++)
                    {
                        for (int d = 0; d < CNSEnvironment.NumberOfDimensions; d++)
                        {
                            mVec[d] = m[d][i, j];
                        }

                        Material material = speciesMap.GetMaterial(double.NaN);
                        StateVector state = new StateVector(material, rho[i, j], mVec, rhoE[i, j]);

                        double mu = 0.0;
                        if (Reynolds != 0.0)
                        {
                            mu = state.GetViscosity(j0 + j) / Reynolds;
                        }

                        double[,] gradU = new double[D, D];
                        for (int d1 = 0; d1 < D; d1++)
                        {
                            for (int d2 = 0; d2 < D; d2++)
                            {
                                // Apply chain rule
                                gradU[d1, d2] = (gradM[i, j, d1, d2] - state.Momentum[d1] / state.Density * gradRho[i, j, d2]) / state.Density;
                            }
                        }
                        double divU = gradU[0, 0] + gradU[1, 1];

                        switch (direction)
                        {
                        // Attention: Changed sign, because normal vector is pointing inwards, not outwards!
                        case 0:                                                                                // x-Direction
                            result[i, j, 0] = -state.Pressure / MachScaling * normals[i, j, 0]
                                              + mu * (2.0 * gradU[0, 0] - 2.0 / 3.0 * divU) * normals[i, j, 0] //tau_11 * n_1
                                              + mu * (gradU[0, 1] + gradU[1, 0]) * normals[i, j, 1];           //tau_12 * n_2
                            break;

                        case 1:                                                                                 // y-Direction
                            result[i, j, 0] = -state.Pressure / MachScaling * normals[i, j, 1]
                                              + mu * (gradU[0, 1] + gradU[1, 0]) * normals[i, j, 0]             //tau_12 * n_1
                                              + mu * (2.0 * gradU[1, 1] - 2.0 / 3.0 * divU) * normals[i, j, 1]; //tau_22*n_2
                            break;

                        default:
                            throw new ArgumentException("Lift and Drag currently only in 2D implemented");
                        }
                    }
                }
            });
        }