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);
        }
Beispiel #2
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);
            });
        }
Beispiel #3
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="gridData"></param>
        /// <param name="config"></param>
        public IBMFieldSet(IGridData gridData, IBMControl config)
            : base(gridData, config)
        {
            this.config = config;

            if (config.RestartInfo == null && !config.FieldOptions.ContainsKey(IBMVariables.LevelSet))
            {
                throw new Exception(
                          "Field 'levelSet' is required for IBM applications");
            }

            LevelSet = new LevelSet(
                new Basis(gridData, config.FieldOptions[IBMVariables.LevelSet].Degree),
                IBMVariables.LevelSet);

            if (config.ContinuousLevelSet)
            {
                SpecFemBasis specFEMBasis = new SpecFemBasis((GridData)gridData, LevelSet.Basis.Degree);

                SpecFemField specFemField = new SpecFemField(specFEMBasis);
                specFemField.ProjectDGFieldMaximum(1.0, LevelSet);
                LevelSet.Clear();
                specFemField.AccToDGField(1.0, LevelSet);
            }

            LevelSetGradient = new DGField[CompressibleEnvironment.NumberOfDimensions];
            for (int d = 0; d < CompressibleEnvironment.NumberOfDimensions; d++)
            {
                LevelSetGradient[d] = DerivedFields[IBMVariables.LevelSetGradient[d]];
            }
        }
Beispiel #4
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);
            });
        }
Beispiel #5
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));
            });
        }
Beispiel #6
0
 /// <summary>
 /// Constructs a new operator factory which additionally implements
 /// the boundary conditions at immersed boundaries.
 /// </summary>
 /// <param name="control"></param>
 /// <param name="gridData"></param>
 /// <param name="workingSet"></param>
 /// <param name="speciesMap"></param>
 /// <param name="boundaryMap"></param>
 public IBMOperatorFactory(
     IBMControl control,
     GridData gridData,
     CNSFieldSet workingSet,
     ISpeciesMap speciesMap,
     IBoundaryConditionMap boundaryMap)
     : base(control, gridData, workingSet, speciesMap, boundaryMap)
 {
     this.immersedBoundaryFluxBuilders.Add(new BoundaryConditionSourceFluxBuilder(
                                               control, boundaryMap, speciesMap, convectiveFluxBuilder, diffusiveFluxBuilder));
 }
Beispiel #7
0
        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();
            }
        }
Beispiel #8
0
        /// <summary>
        /// Constructs a new species map using the level set information
        /// provided by <paramref name="levelSet"/>.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="levelSet"></param>
        /// <param name="material"></param>
        public ImmersedSpeciesMap(IBMControl control, LevelSet levelSet, Material material)
        {
            this.Control  = control;
            this.material = material;

            this.tracker = new LevelSetTracker(
                (GridData)levelSet.GridDat,
                Control.CutCellQuadratureType,
                0,
                new string[] { Control.VoidSpeciesName, Control.FluidSpeciesName },
                levelSet);
            tracker.Subscribe(this);
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        internal void BuildEvaluatorsAndMasks()
        {
            CellMask fluidCells = speciesMap.SubGrid.VolumeMask.Intersect(ABSubGrid.VolumeMask);

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


            IBMControl control = speciesMap.Control;
            SpeciesId  species = speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName);

            CellQuadratureScheme volumeScheme = speciesMap.QuadSchemeHelper.GetVolumeQuadScheme(
                species, true, fluidCells, control.LevelSetQuadratureOrder);


            // Does _not_ include agglomerated edges
            EdgeMask nonVoidEdges = speciesMap.QuadSchemeHelper.GetEdgeMask(species);

            nonVoidEdges = nonVoidEdges.Intersect(ABSubGrid.AllEdgesMask.ToGeometicalMask());
            EdgeQuadratureScheme edgeScheme = speciesMap.QuadSchemeHelper.GetEdgeQuadScheme(
                species, true, nonVoidEdges, control.LevelSetQuadratureOrder);

            this.m_Evaluator = new Lazy <IEvaluatorNonLin>(delegate() {
                this.Operator.EdgeQuadraturSchemeProvider   = g => edgeScheme;
                this.Operator.VolumeQuadraturSchemeProvider = g => volumeScheme;

                var opi = this.Operator.GetEvaluatorEx(
                    Mapping,
                    boundaryParameterMap,
                    Mapping);
                opi.ActivateSubgridBoundary(ABSubGrid.VolumeMask, subGridBoundaryTreatment: SubGridBoundaryModes.InnerEdgeLTS);
                return(opi);
            });

            // Evaluator for boundary conditions at level set zero contour
            CellQuadratureScheme boundaryVolumeScheme = speciesMap.QuadSchemeHelper.GetLevelSetquadScheme(
                0, cutCells, control.LevelSetQuadratureOrder);

            this.boundaryEvaluator = new Lazy <IEvaluatorNonLin>(delegate() {
                boundaryOperator.EdgeQuadraturSchemeProvider   = g => null; // Contains no boundary terms --> PROBLEM??????????
                boundaryOperator.VolumeQuadraturSchemeProvider = g => boundaryVolumeScheme;
                return(boundaryOperator.GetEvaluatorEx(
                           Mapping,
                           boundaryParameterMap,
                           Mapping));
            });
        }
Beispiel #11
0
        internal void BuildEvaluatorsAndMasks()
        {
            CellMask fluidCells = speciesMap.SubGrid.VolumeMask.Intersect(ABSubGrid.VolumeMask);

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


            IBMControl control = speciesMap.Control;
            SpeciesId  species = speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName);

            CellQuadratureScheme volumeScheme = speciesMap.QuadSchemeHelper.GetVolumeQuadScheme(
                species, true, fluidCells, control.LevelSetQuadratureOrder);


            // Does _not_ include agglomerated edges
            EdgeMask nonVoidEdges = speciesMap.QuadSchemeHelper.GetEdgeMask(species);

            nonVoidEdges = nonVoidEdges.Intersect(ABSubGrid.AllEdgesMask);
            EdgeQuadratureScheme edgeScheme = speciesMap.QuadSchemeHelper.GetEdgeQuadScheme(
                species, true, nonVoidEdges, control.LevelSetQuadratureOrder);

            this.m_Evaluator = new Lazy <SpatialOperator.Evaluator>(() =>
                                                                    this.Operator.GetEvaluatorEx(
                                                                        Mapping,
                                                                        boundaryParameterMap,
                                                                        Mapping,
                                                                        edgeScheme,
                                                                        volumeScheme,
                                                                        ABSubGrid,
                                                                        subGridBoundaryTreatment: SpatialOperator.SubGridBoundaryModes.InnerEdgeLTS));

            // Evaluator for boundary conditions at level set zero contour
            CellQuadratureScheme boundaryVolumeScheme = speciesMap.QuadSchemeHelper.GetLevelSetquadScheme(
                0, cutCells, control.LevelSetQuadratureOrder);

            this.boundaryEvaluator = new Lazy <SpatialOperator.Evaluator>(() =>
                                                                          boundaryOperator.GetEvaluatorEx(
                                                                              Mapping,
                                                                              boundaryParameterMap,
                                                                              Mapping,
                                                                              null, // Contains no boundary terms --> PROBLEM??????????
                                                                              boundaryVolumeScheme));
        }
        private void BuildEvaluatorsAndMasks()
        {
            CellMask fluidCells = speciesMap.SubGrid.VolumeMask;

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

            IBMControl control = speciesMap.Control;
            SpeciesId  species = speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName);

            CellQuadratureScheme volumeScheme = speciesMap.QuadSchemeHelper.GetVolumeQuadScheme(
                species, true, fluidCells, control.LevelSetQuadratureOrder);

            // Does _not_ include agglomerated edges
            EdgeMask             nonVoidEdges = speciesMap.QuadSchemeHelper.GetEdgeMask(species);
            EdgeQuadratureScheme edgeScheme   = speciesMap.QuadSchemeHelper.GetEdgeQuadScheme(
                species, true, nonVoidEdges, control.LevelSetQuadratureOrder);

            this.m_Evaluator = new Lazy <IEvaluatorNonLin>(delegate() {
                this.Operator.EdgeQuadraturSchemeProvider   = g => edgeScheme;
                this.Operator.VolumeQuadraturSchemeProvider = g => volumeScheme;
                var opi = this.Operator.GetEvaluatorEx(
                    Mapping,
                    null, // TO DO: I SIMPLY REMOVE PARAMETERMAP HERE; MAKE THIS MORE PRETTY
                          //this.boundaryParameterMap, // TO DO: I SIMPLY REMOVE PARAMETERMAP HERE; MAKE THIS MORE PRETTY
                    Mapping);
                opi.ActivateSubgridBoundary(volumeScheme.Domain, subGridBoundaryTreatment: SubGridBoundaryModes.InnerEdgeLTS);
                //opi.ActivateSubgridBoundary(fluidCells, subGridBoundaryTreatment: SubGridBoundaryModes.InnerEdgeLTS);
                return(opi);
            });

            // Evaluator for boundary conditions at level set zero contour
            CellQuadratureScheme boundaryVolumeScheme = speciesMap.QuadSchemeHelper.GetLevelSetquadScheme(
                0, cutCells, control.LevelSetQuadratureOrder);

            this.boundaryEvaluator = new Lazy <IEvaluatorNonLin>(delegate() {
                boundaryOperator.EdgeQuadraturSchemeProvider   = g => null; // Contains no boundary terms
                boundaryOperator.VolumeQuadraturSchemeProvider = g => boundaryVolumeScheme;
                return(boundaryOperator.GetEvaluatorEx(
                           Mapping,
                           boundaryParameterMap,
                           Mapping));
            });
        }
        /// <summary>
        /// Constructs a new flux builder where the boundary conditions are
        /// evaluated using the convective fluxes defined by
        /// <paramref name="convectiveBuilder"/>.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="boundaryMap"></param>
        /// <param name="speciesMap"></param>
        /// <param name="convectiveBuilder"></param>
        /// <param name="diffusiveBuilder"></param>
        public BoundaryConditionSourceFluxBuilder(
            IBMControl control, BoundaryConditionMap boundaryMap, ISpeciesMap speciesMap, FluxBuilder convectiveBuilder, FluxBuilder diffusiveBuilder)
            : base(control, boundaryMap, speciesMap)
        {
            standardOperator = new Operator(control);

            if (convectiveBuilder != null)
            {
                convectiveBuilder.BuildFluxes(standardOperator);
            }

            if (diffusiveBuilder != null)
            {
                diffusiveBuilder.BuildFluxes(standardOperator);
            }

            string levelSetBoundaryType = control.LevelSetBoundaryTag;

            boundaryCondition = boundaryMap.GetBoundaryCondition(levelSetBoundaryType);
        }
Beispiel #14
0
        protected void UpdateEvaluatorsAndMasks()
        {
            CellMask fluidCells = speciesMap.SubGrid.VolumeMask;

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

            IBMControl control = speciesMap.Control;
            SpeciesId  species = speciesMap.Tracker.GetSpeciesId(control.FluidSpeciesName);

            CellQuadratureScheme volumeScheme = speciesMap.QuadSchemeHelper.GetVolumeQuadScheme(
                species, true, fluidCells, control.LevelSetQuadratureOrder);

            // Does _not_ include agglomerated edges
            EdgeMask             nonVoidEdges = speciesMap.QuadSchemeHelper.GetEdgeMask(species);
            EdgeQuadratureScheme edgeScheme   = speciesMap.QuadSchemeHelper.GetEdgeQuadScheme(
                species, true, nonVoidEdges, control.LevelSetQuadratureOrder);

            this.m_Evaluator = new Lazy <IEvaluatorNonLin>(delegate() {
                var opi = this.Operator.GetEvaluatorEx(
                    Mapping,
                    boundaryParameterMap,
                    Mapping,
                    edgeScheme,
                    volumeScheme);
                opi.ActivateSubgridBoundary(volumeScheme.Domain.ToLogicalMask(), subGridBoundaryTreatment: SpatialOperator.SubGridBoundaryModes.InnerEdgeLTS);
                return(opi);
            });

            // Evaluator for boundary conditions at level set zero contour
            CellQuadratureScheme boundaryVolumeScheme = speciesMap.QuadSchemeHelper.GetLevelSetquadScheme(
                0, cutCells, control.LevelSetQuadratureOrder);

            this.boundaryEvaluator = new Lazy <IEvaluatorNonLin>(() =>
                                                                 boundaryOperator.GetEvaluatorEx(
                                                                     Mapping,
                                                                     boundaryParameterMap,
                                                                     Mapping,
                                                                     null, // Contains no boundary terms
                                                                     boundaryVolumeScheme));
        }
Beispiel #15
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="gridData"></param>
        /// <param name="config"></param>
        public IBMFieldSet(GridData gridData, IBMControl config)
            : base(gridData, config)
        {
            this.config = config;

            if (config.RestartInfo == null && !config.FieldOptions.ContainsKey(IBMVariables.LevelSet))
            {
                throw new Exception(
                          "Field 'levelSet' is required for IBM applications");
            }

            LevelSet = new LevelSet(
                new Basis(gridData, config.FieldOptions[IBMVariables.LevelSet].Degree),
                IBMVariables.LevelSet);

            LevelSetGradient = new DGField[CNSEnvironment.NumberOfDimensions];
            for (int d = 0; d < CNSEnvironment.NumberOfDimensions; d++)
            {
                LevelSetGradient[d] = DerivedFields[IBMVariables.LevelSetGradient[d]];
            }
        }
Beispiel #16
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);
            });
        }