/// <summary> /// Creates a continuous version of a DG level set field /// </summary> /// <param name="DGLevelSet">The input DG level set field</param> /// <param name="jCells">Local cell indices</param> /// <returns>A continuous level set <see cref="SinglePhaseField"/> with one order higher than the input</returns> public static SinglePhaseField ContinuousLevelSet(SinglePhaseField DGLevelSet, double[] jCells) { Console.WriteLine(String.Format("Continuity projection of field {0} started...", DGLevelSet.Identification)); // Init IGridData gridData = DGLevelSet.GridDat; SinglePhaseField continuousLevelSet = new SinglePhaseField(new Basis(gridData, DGLevelSet.Basis.Degree + 1), DGLevelSet.Identification + "_cont"); // Create narrow band List <int> allNeighbours = new List <int>(); foreach (int cell in jCells) { gridData.GetCellNeighbours(cell, GetCellNeighbours_Mode.ViaVertices, out int[] cellNeighbours, out int[] connectingEntities); allNeighbours.Add(cell); allNeighbours.AddRange(cellNeighbours); } List <int> narrowBandCells = allNeighbours.Distinct().ToList(); BitArray bitMaskNarrowBand = new BitArray(gridData.iLogicalCells.NoOfLocalUpdatedCells); foreach (int cell in narrowBandCells) { bitMaskNarrowBand[cell] = true; } CellMask narrowBand = new CellMask(gridData, bitMaskNarrowBand); // Create positive mask BitArray bitMaskPos = new BitArray(gridData.iLogicalCells.NoOfLocalUpdatedCells); for (int i = 0; i < gridData.iLogicalCells.NoOfLocalUpdatedCells; i++) { if (DGLevelSet.GetMeanValue(i) > 0 && !bitMaskNarrowBand[i]) { bitMaskPos[i] = true; } } CellMask posMask = new CellMask(gridData, bitMaskPos); // Continuity projection ContinuityProjection continuityProjection = new ContinuityProjection(continuousLevelSet.Basis, DGLevelSet.Basis, gridData, ContinuityProjectionOption.ConstrainedDG); continuityProjection.MakeContinuous(DGLevelSet, continuousLevelSet, narrowBand, posMask); Console.WriteLine("finished"); return(continuousLevelSet); }
/// <summary> /// Computes the new level set field at time <paramref name="Phystime"/> + <paramref name="dt"/>. /// This is a 'driver function' which provides a universal interface to the various level set evolution algorithms. /// It also acts as a callback to the time stepper (see <see cref="m_BDF_Timestepper"/> resp. <see cref="m_RK_Timestepper"/>), /// i.e. it matches the signature of /// <see cref="BoSSS.Solution.XdgTimestepping.DelUpdateLevelset"/>. /// </summary> /// <param name="Phystime"></param> /// <param name="dt"></param> /// <param name="CurrentState"> /// The current solution (velocity and pressure), since the complete solution is provided by the time stepper, /// only the velocity components(supposed to be at the beginning) are used. /// </param> /// <param name="underrelax"> /// </param> public double DelUpdateLevelSet(DGField[] CurrentState, double Phystime, double dt, double underrelax, bool incremental) { using (new FuncTrace()) { //dt *= underrelax; int D = base.Grid.SpatialDimension; int iTimestep = hack_TimestepIndex; DGField[] EvoVelocity = CurrentState.GetSubVector(0, D); // ======================================================== // Backup old level-set, in order to compute the residual // ======================================================== SinglePhaseField LsBkUp = new SinglePhaseField(this.LevSet.Basis); LsBkUp.Acc(1.0, this.LevSet); CellMask oldCC = LsTrk.Regions.GetCutCellMask(); // ==================================================== // set evolution velocity, but only on the CUT-cells // ==================================================== #region Calculate density averaged Velocity for each cell ConventionalDGField[] meanVelocity = GetMeanVelocityFromXDGField(EvoVelocity); #endregion // =================================================================== // backup interface properties (mass conservation, surface changerate) // =================================================================== #region backup interface props double oldSurfVolume = 0.0; double oldSurfLength = 0.0; double SurfChangerate = 0.0; if (this.Control.CheckInterfaceProps) { oldSurfVolume = XNSEUtils.GetSpeciesArea(this.LsTrk, LsTrk.GetSpeciesId("A")); oldSurfLength = XNSEUtils.GetInterfaceLength(this.LsTrk); SurfChangerate = EnergyUtils.GetSurfaceChangerate(this.LsTrk, meanVelocity, this.m_HMForder); } #endregion // ==================================================== // perform level-set evolution // ==================================================== #region level-set evolution // set up for Strang splitting SinglePhaseField DGLevSet_old; if (incremental) { DGLevSet_old = this.DGLevSet.Current.CloneAs(); } else { DGLevSet_old = this.DGLevSet[0].CloneAs(); } // set up for underrelaxation SinglePhaseField DGLevSet_oldIter = this.DGLevSet.Current.CloneAs(); //PlotCurrentState(hack_Phystime, new TimestepNumber(new int[] { hack_TimestepIndex, 0 }), 2); // actual evolution switch (this.Control.Option_LevelSetEvolution) { case LevelSetEvolution.None: throw new ArgumentException("illegal call"); case LevelSetEvolution.FastMarching: { NarrowMarchingBand.Evolve_Mk2( dt, this.LsTrk, DGLevSet_old, this.DGLevSet.Current, this.DGLevSetGradient, meanVelocity, this.ExtensionVelocity.Current.ToArray(), //new DGField[] { LevSetSrc }, this.m_HMForder, iTimestep); //FastMarchReinitSolver = new FastMarchReinit(DGLevSet.Current.Basis); //CellMask Accepted = LsTrk.Regions.GetCutCellMask(); //CellMask ActiveField = LsTrk.Regions.GetNearFieldMask(1); //CellMask NegativeField = LsTrk.Regions.GetSpeciesMask("A"); //FastMarchReinitSolver.FirstOrderReinit(DGLevSet.Current, Accepted, NegativeField, ActiveField); break; } case LevelSetEvolution.Fourier: { Fourier_Timestepper.moveLevelSet(dt, meanVelocity); if (incremental) { Fourier_Timestepper.updateFourierLevSet(); } Fourier_LevSet.ProjectToDGLevelSet(this.DGLevSet.Current, this.LsTrk); break; } case LevelSetEvolution.Prescribed: { this.DGLevSet.Current.Clear(); this.DGLevSet.Current.ProjectField(1.0, this.Control.Phi.Vectorize(Phystime + dt)); break; } case LevelSetEvolution.ScalarConvection: { var LSM = new LevelSetMover(EvoVelocity, this.ExtensionVelocity, this.LsTrk, XVelocityProjection.CutCellVelocityProjectiontype.L2_plain, this.DGLevSet, this.BcMap); int check1 = this.ExtensionVelocity.PushCount; int check2 = this.DGLevSet.PushCount; this.DGLevSet[1].Clear(); this.DGLevSet[1].Acc(1.0, DGLevSet_old); LSM.Advect(dt); if (check1 != this.ExtensionVelocity.PushCount) { throw new ApplicationException(); } if (check2 != this.DGLevSet.PushCount) { throw new ApplicationException(); } break; } case LevelSetEvolution.ExtensionVelocity: { DGLevSetGradient.Clear(); DGLevSetGradient.Gradient(1.0, DGLevSet.Current); ExtVelMover.Advect(dt); // Fast Marching: Specify the Domains first // Perform Fast Marching only on the Far Field if (this.Control.AdaptiveMeshRefinement) { int NoCells = ((GridData)this.GridData).Cells.Count; BitArray Refined = new BitArray(NoCells); for (int j = 0; j < NoCells; j++) { if (((GridData)this.GridData).Cells.GetCell(j).RefinementLevel > 0) { Refined[j] = true; } } CellMask Accepted = new CellMask(this.GridData, Refined); CellMask AcceptedNeigh = Accepted.AllNeighbourCells(); Accepted = Accepted.Union(AcceptedNeigh); CellMask ActiveField = Accepted.Complement(); CellMask NegativeField = LsTrk.Regions.GetSpeciesMask("A"); FastMarchReinitSolver.FirstOrderReinit(DGLevSet.Current, Accepted, NegativeField, ActiveField); } else { CellMask Accepted = LsTrk.Regions.GetNearFieldMask(1); CellMask ActiveField = Accepted.Complement(); CellMask NegativeField = LsTrk.Regions.GetSpeciesMask("A"); FastMarchReinitSolver.FirstOrderReinit(DGLevSet.Current, Accepted, NegativeField, ActiveField); } //SubGrid AcceptedGrid = new SubGrid(Accepted); //ReInitPDE.ReInitialize(Restriction: AcceptedGrid); //CellMask ActiveField = Accepted.Complement(); //CellMask NegativeField = LsTrk.Regions.GetSpeciesMask("A"); //FastMarchReinitSolver.FirstOrderReinit(DGLevSet.Current, Accepted, NegativeField, ActiveField); //ReInitPDE.ReInitialize(); break; } default: throw new ApplicationException(); } // performing underrelaxation if (underrelax < 1.0) { this.DGLevSet.Current.Scale(underrelax); this.DGLevSet.Current.Acc((1.0 - underrelax), DGLevSet_oldIter); } //PlotCurrentState(hack_Phystime, new TimestepNumber(new int[] { hack_TimestepIndex, 1 }), 2); #endregion // ====================== // postprocessing // ======================= if (this.Control.ReInitPeriod > 0 && hack_TimestepIndex % this.Control.ReInitPeriod == 0) { Console.WriteLine("Filtering DG-LevSet"); SinglePhaseField FiltLevSet = new SinglePhaseField(DGLevSet.Current.Basis); FiltLevSet.AccLaidBack(1.0, DGLevSet.Current); Filter(FiltLevSet, 2, oldCC); DGLevSet.Current.Clear(); DGLevSet.Current.Acc(1.0, FiltLevSet); Console.WriteLine("FastMarchReInit performing FirstOrderReInit"); FastMarchReinitSolver = new FastMarchReinit(DGLevSet.Current.Basis); CellMask Accepted = LsTrk.Regions.GetCutCellMask(); CellMask ActiveField = LsTrk.Regions.GetNearFieldMask(1); CellMask NegativeField = LsTrk.Regions.GetSpeciesMask("A"); FastMarchReinitSolver.FirstOrderReinit(DGLevSet.Current, Accepted, NegativeField, ActiveField); } #region ensure continuity // make level set continuous CellMask CC = LsTrk.Regions.GetCutCellMask4LevSet(0); CellMask Near1 = LsTrk.Regions.GetNearMask4LevSet(0, 1); CellMask PosFF = LsTrk.Regions.GetLevelSetWing(0, +1).VolumeMask; ContinuityEnforcer.MakeContinuous(this.DGLevSet.Current, this.LevSet, Near1, PosFF); if (this.Control.Option_LevelSetEvolution == LevelSetEvolution.FastMarching) { CellMask Nearband = Near1.Union(CC); this.DGLevSet.Current.Clear(Nearband); this.DGLevSet.Current.AccLaidBack(1.0, this.LevSet, Nearband); //ContinuityEnforcer.SetFarField(this.DGLevSet.Current, Near1, PosFF); } //PlotCurrentState(hack_Phystime, new TimestepNumber(new int[] { hack_TimestepIndex, 2 }), 2); #endregion for (int d = 0; d < D; d++) { this.XDGvelocity.Velocity[d].UpdateBehaviour = BehaveUnder_LevSetMoovement.AutoExtrapolate; } // =============== // tracker update // =============== this.LsTrk.UpdateTracker(Phystime + dt, incremental: true); // update near field (in case of adaptive mesh refinement) if (this.Control.AdaptiveMeshRefinement && this.Control.Option_LevelSetEvolution == LevelSetEvolution.FastMarching) { Near1 = LsTrk.Regions.GetNearMask4LevSet(0, 1); PosFF = LsTrk.Regions.GetLevelSetWing(0, +1).VolumeMask; ContinuityEnforcer.SetFarField(this.DGLevSet.Current, Near1, PosFF); ContinuityEnforcer.SetFarField(this.LevSet, Near1, PosFF); } // ================================================================== // check interface properties (mass conservation, surface changerate) // ================================================================== if (this.Control.CheckInterfaceProps) { double currentSurfVolume = XNSEUtils.GetSpeciesArea(this.LsTrk, LsTrk.GetSpeciesId("A")); double massChange = ((currentSurfVolume - oldSurfVolume) / oldSurfVolume) * 100; Console.WriteLine("Change of mass = {0}%", massChange); double currentSurfLength = XNSEUtils.GetInterfaceLength(this.LsTrk); double actualSurfChangerate = (currentSurfLength - oldSurfLength) / dt; Console.WriteLine("Interface divergence = {0}", SurfChangerate); Console.WriteLine("actual surface changerate = {0}", actualSurfChangerate); } // ================== // compute residual // ================== var newCC = LsTrk.Regions.GetCutCellMask(); LsBkUp.Acc(-1.0, this.LevSet); double LevSetResidual = LsBkUp.L2Norm(newCC.Union(oldCC)); return(LevSetResidual); } }
/// <summary> /// setUp for the Level set initialization (Level-set algorithm, continuity, conservation) /// </summary> protected void InitLevelSet() { using (new FuncTrace()) { // check level-set if (this.LevSet.L2Norm() == 0) { throw new NotSupportedException("Level set is not initialized - norm is 0.0 - ALL cells will be cut, no gradient can be defined!"); } // tracker needs to be updated to get access to the cut-cell mask this.LsTrk.UpdateTracker(0.0); // ============================== // level-set initialization // ============================== //PlotCurrentState(0.0, new TimestepNumber(new int[] { 0, 0 }), 3); #region Initialize Level Set Evolution Algorithm switch (this.Control.Option_LevelSetEvolution) { case LevelSetEvolution.Fourier: InitFourier(); break; case LevelSetEvolution.None: if (this.Control.AdvancedDiscretizationOptions.SST_isotropicMode == SurfaceStressTensor_IsotropicMode.Curvature_Fourier) { Fourier_LevSet = FourierLevelSetFactory.Build(this.Control.FourierLevSetControl); Fourier_LevSet.ProjectToDGLevelSet(this.DGLevSet.Current, this.LsTrk); } else { goto default; } break; case LevelSetEvolution.ExtensionVelocity: { // Create ExtensionVelocity Motion Algorithm this.DGLevSet.Current.Clear(); this.DGLevSet.Current.AccLaidBack(1.0, this.LevSet); DGLevSetGradient.Gradient(1.0, DGLevSet.Current); //VectorField<SinglePhaseField> VectorExtVel = ExtensionVelocity.Current; base.RegisterField(ExtensionVelocity.Current); //ReInitPDE = new EllipticReInit(this.LsTrk, this.Control.ReInitControl, DGLevSet.Current); FastMarchReinitSolver = new FastMarchReinit(DGLevSet.Current.Basis); // full initial reinitialization //ReInitPDE.ReInitialize(Restriction: LsTrk.Regions.GetNearFieldSubgrid(1)); CellMask Accepted = LsTrk.Regions.GetNearFieldMask(1); CellMask ActiveField = Accepted.Complement(); CellMask NegativeField = LsTrk.Regions.GetSpeciesMask("A"); FastMarchReinitSolver.FirstOrderReinit(DGLevSet.Current, Accepted, NegativeField, ActiveField); //ReInitPDE.ReInitialize(); // setup extension velocity mover switch (this.Control.TimeSteppingScheme) { case TimeSteppingScheme.RK_CrankNic: case TimeSteppingScheme.CrankNicolson: { //do not instantiate rksch, use bdf instead bdfOrder = -1; break; } case TimeSteppingScheme.RK_ImplicitEuler: case TimeSteppingScheme.ImplicitEuler: { //do not instantiate rksch, use bdf instead bdfOrder = 1; break; } default: { if (this.Control.TimeSteppingScheme.ToString().StartsWith("BDF")) { //do not instantiate rksch, use bdf instead bdfOrder = Convert.ToInt32(this.Control.TimeSteppingScheme.ToString().Substring(3)); break; } else { throw new NotImplementedException(); } } } ExtVelMover = new ExtensionVelocityBDFMover(LsTrk, DGLevSet.Current, DGLevSetGradient, new VectorField <DGField>(XDGvelocity.Velocity.ToArray()), Control.EllipticExtVelAlgoControl, BcMap, bdfOrder, ExtensionVelocity.Current, new double[2] { Control.PhysicalParameters.rho_A, Control.PhysicalParameters.rho_B }); break; } case LevelSetEvolution.FastMarching: case LevelSetEvolution.Prescribed: case LevelSetEvolution.ScalarConvection: default: // evolution algorithms need a signed-distance level-set: // do some reinit at startup //BoSSS.Solution.LevelSetTools.Advection.NarrowMarchingBand.CutCellReinit(this.LsTrk, this.DGLevSet.Current); // apply only the minimal necessary change this.DGLevSet.Current.Clear(); this.DGLevSet.Current.AccLaidBack(1.0, this.LevSet); //FastMarchReinitSolver = new FastMarchReinit(DGLevSet.Current.Basis); break; } //PlotCurrentState(0.0, new TimestepNumber(new int[] { 0, 1 }), 3); #endregion // ========================================= // Enforcing the continuity of the level-set // ========================================= ContinuityEnforcer = new ContinuityProjection( ContBasis: this.LevSet.Basis, DGBasis: this.DGLevSet.Current.Basis, gridData: GridData, Option: Control.LSContiProjectionMethod ); //var CC = this.LsTrk.Regions.GetCutCellMask4LevSet(0); var Near1 = this.LsTrk.Regions.GetNearMask4LevSet(0, 1); var Near = this.LsTrk.Regions.GetNearMask4LevSet(0, this.Control.LS_TrackerWidth); var PosFF = this.LsTrk.Regions.GetLevelSetWing(0, +1).VolumeMask; if (this.Control.Option_LevelSetEvolution != LevelSetEvolution.ExtensionVelocity) { ContinuityEnforcer.SetFarField(this.DGLevSet.Current, Near1, PosFF); } ContinuityEnforcer.MakeContinuous(this.DGLevSet.Current, this.LevSet, Near, PosFF); //PlotCurrentState(0.0, new TimestepNumber(new int[] { 0, 2 }), 3); this.LsTrk.UpdateTracker(0.0); } }