/// <summary> /// Computes the maximum admissible step-size according to /// GassnerEtAl2008, equation 67. /// </summary> /// <param name="i0"></param> /// <param name="Length"></param> /// <returns></returns> protected override double GetCFLStepSize(int i0, int Length) { int iKref = gridData.Cells.GetRefElementIndex(i0); int noOfNodesPerCell = base.EvaluationPoints[iKref].NoOfNodes; MultidimensionalArray levelSetValues = speciesMap.Tracker.DataHistories[0].Current.GetLevSetValues(base.EvaluationPoints[iKref], i0, Length); SpeciesId species = speciesMap.Tracker.GetSpeciesId(speciesMap.Control.FluidSpeciesName); var hMinArray = speciesMap.CellAgglomeration.CellLengthScales[species]; //var volFrac = speciesMap.QuadSchemeHelper.CellAgglomeration.CellVolumeFrac[species]; //var hMinGass = speciesMap.h_min; //var hMin = gridData.Cells.h_min; double cfl = double.MaxValue; for (int i = 0; i < Length; i++) { int cell = i0 + i; //double hmin = hMin[cell] * volFrac[cell]; double hmin = hMinArray[cell]; //double hmin = hMinGass[cell]; for (int node = 0; node < noOfNodesPerCell; node++) { if (levelSetValues[i, node].Sign() != (double)speciesMap.Control.FluidSpeciesSign) { continue; } Material material = speciesMap.GetMaterial(double.NaN); Vector3D momentum = new Vector3D(); for (int d = 0; d < CNSEnvironment.NumberOfDimensions; d++) { momentum[d] = momentumValues[d][i, node]; } StateVector state = new StateVector( material, densityValues[i, node], momentum, energyValues[i, node]); double coeff = Math.Max(4.0 / 3.0, config.EquationOfState.HeatCapacityRatio / config.PrandtlNumber); double cflhere = hmin * hmin / coeff / (state.GetViscosity(cell) / config.ReynoldsNumber); #if DEBUG if (double.IsNaN(cflhere)) { throw new Exception("Could not determine CFL number"); } #endif cfl = Math.Min(cfl, cflhere); } } int degree = workingSet.ConservativeVariables.Max(f => f.Basis.Degree); int twoNPlusOne = 2 * degree + 1; return(cfl * GetBetaMax(degree) / twoNPlusOne / twoNPlusOne / Math.Sqrt(CNSEnvironment.NumberOfDimensions)); //return cfl / twoNPlusOne / twoNPlusOne; }
public MovingFrameRusanovFlux(CompressibleControl config, IBoundaryConditionMap boundaryMap, IEulerEquationComponent equationComponent, ImmersedSpeciesMap speciesMap) : base(config, boundaryMap, equationComponent, speciesMap.GetMaterial(double.NaN)) { this.levelSetVelocity = speciesMap.Control.LevelSetVelocity; }
/// <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"); } } } }); }
/// <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); }); }
/// <summary> /// Mostly identical to /// <see cref="Convection.ConvectiveCFLConstraint.GetCFLStepSize"/>, /// but uses the level set to omit nodes in the void domain. /// </summary> /// <param name="i0"></param> /// <param name="Length"></param> /// <returns> /// <see cref="Convection.ConvectiveCFLConstraint.GetCFLStepSize"/> /// </returns> protected override double GetCFLStepSize(int i0, int Length) { int iKref = gridData.Cells.GetRefElementIndex(i0); int noOfNodesPerCell = base.EvaluationPoints[iKref].NoOfNodes; int D = gridData.Grid.SpatialDimension; Material material = speciesMap.GetMaterial(double.NaN); MultidimensionalArray levelSetValues = speciesMap.Tracker.DataHistories[0].Current.GetLevSetValues(base.EvaluationPoints[iKref], i0, Length); SpeciesId species = speciesMap.Tracker.GetSpeciesId(speciesMap.Control.FluidSpeciesName); //var hMinArray = speciesMap.QuadSchemeHelper.CellAgglomeration.CellLengthScales[species]; var volFrac = speciesMap.CellAgglomeration.CellVolumeFrac[species]; var hMin = gridData.Cells.h_min; double cfl = double.MaxValue; for (int i = 0; i < Length; i++) { int cell = i0 + i; // Option 1: Use volume fraction times traditional metric, such // that time-steps are identical to non-IBM cases when no // interface is present double hmin = hMin[cell] * volFrac[cell]; // Option 2: Use length scale "volume over surface", which seems // to be more robust for awkward cuts. However, yields // significantly smaller time-steps in uncut cells //double hmin = hMinArray[cell]; for (int node = 0; node < noOfNodesPerCell; node++) { if (levelSetValues[i, node].Sign() != (double)speciesMap.Control.FluidSpeciesSign) { continue; } Vector3D momentum = new Vector3D(); for (int d = 0; d < CNSEnvironment.NumberOfDimensions; d++) { momentum[d] = momentumValues[d][i, node]; } StateVector state = new StateVector( material, densityValues[i, node], momentum, energyValues[i, node]); double cflhere = hmin / (state.Velocity.Abs() + state.SpeedOfSound); #if DEBUG if (double.IsNaN(cflhere)) { throw new Exception("Could not determine CFL number"); throw new ArithmeticException("Could not determine CFL number"); } #endif cfl = Math.Min(cfl, cflhere); } } int degree = workingSet.ConservativeVariables.Max(f => f.Basis.Degree); int twoNPlusOne = 2 * degree + 1; return(cfl / twoNPlusOne); }