/// <summary> /// Estimates the errors of the fields with name /// <paramref name="fieldName"/> in the given /// <paramref name="timesteps"/> by computing the errors with respect /// to the solution on the finest corresponding grid by making use of /// <see cref="BoSSS.Solution.Statistic.DGFieldComparison.ComputeErrors"/>. /// The result is then grouped according to the polynomial degree of field /// <paramref name="fieldName"/>. /// </summary> /// <param name="timesteps"> /// The time-steps containing the fields whose errors should be /// estimated. /// </param> /// <param name="fieldName"> /// The name of the DG field whose error should be estimated. /// </param> /// <returns> /// A data set containing information about the grid resolution and the /// corresponding errors with respect to the finest corresponding grid, /// grouped by the polynomial degree. Obviously, the time-step /// associated with the finest grid for each polynomial degree has an /// estimated error of zero (by definition) and is thus excluded from /// the result. /// </returns> public static Plot2Ddata ToEstimatedGridConvergenceData(this IEnumerable <ITimestepInfo> timesteps, string fieldName) { Dictionary <string, double[][]> dataGroups = new Dictionary <string, double[][]>(); foreach (var group in timesteps.GroupBy(t => t.Fields.Find(fieldName).Basis.Degree)) { double[] resolution; Dictionary <string, double[]> errors; Guid[] tsiIds; DGFieldComparison.ComputeErrors( new string[] { fieldName }, group.ToArray(), out resolution, out errors, out tsiIds); Debug.Assert(errors.ContainsKey(fieldName)); Debug.Assert(errors[fieldName].Length == resolution.Length); double[][] resolutionsAndErrors = new double[2][] { resolution, errors[fieldName] }; dataGroups.Add(group.Key.ToString(), resolutionsAndErrors); } return(new Plot2Ddata(dataGroups.ToArray()).WithLogX().WithLogY()); }
/// <summary> /// Estimates the errors of the fields with name /// <paramref name="fieldName"/> in the given /// <paramref name="timesteps"/> by computing the errors with respect /// to the solution on the finest corresponding grid by making use of /// <see cref="BoSSS.Solution.Statistic.DGFieldComparison.ComputeErrors"/>. /// The result is then grouped according to the polynomial degree of field /// <paramref name="fieldName"/>. /// </summary> /// <param name="timesteps"> /// The time-steps containing the fields whose errors should be /// estimated. /// </param> /// <param name="fieldName"> /// The name of the DG field whose error should be estimated. /// </param> /// <param name="xAxis_Is_hOrDof"> /// - true: the x-axis (<see cref="Plot2Ddata.XYvalues.Abscissas"/>) is the grid resolution \f$ h \f$ /// - false: the x-axis (<see cref="Plot2Ddata.XYvalues.Abscissas"/>) is the number of degrees-of-freedom /// </param> /// <returns> /// A data set containing information about the grid resolution and the /// corresponding errors with respect to the finest corresponding grid, /// grouped by the polynomial degree. Obviously, the time-step /// associated with the finest grid for each polynomial degree has an /// estimated error of zero (by definition) and is thus excluded from /// the result. /// </returns> public static Plot2Ddata ToEstimatedGridConvergenceData(this IEnumerable <ITimestepInfo> timesteps, string fieldName, bool xAxis_Is_hOrDof = true) { Dictionary <string, double[][]> dataGroups = new Dictionary <string, double[][]>(); foreach (var group in timesteps.GroupBy(t => t.Fields.Find(fieldName).Basis.Degree)) { DGFieldComparison.ComputeErrors( new string[] { fieldName }, group.ToArray(), out double[] resolution, out Dictionary <string, int[]> DOFs, out Dictionary <string, double[]> errors, out Guid[] tsiIds);
/// <summary> /// Updates all columns related to convergence plots /// </summary> public void Update() { // Get all sessions which are successfully terminated // ================================================== var SuccSessions = owner.Sessions.Where(sess => sess.SuccessfulTermination == true).ToArray(); // Group the sessions according to polynomial degree // ================================================= System.Func <int[], int[], bool> eqFunc = (A, B) => ArrayTools.AreEqual(A, B); var comp = eqFunc.ToEqualityComparer(); var SessionGroups = SuccSessions.GroupBy(GetDGDegreeKey, comp).ToArray(); // Spatial convergence for each session group // ========================================== // intermediate result storage // 1st key: Field name // 2nd key: session name // value: error norm var Errors = new Dictionary <string, Dictionary <Guid, double> >(); foreach (IEnumerable <ISessionInfo> spatialSeries in SessionGroups) { if (spatialSeries.Count() <= 1) { continue; } ITimestepInfo[] tsiS = spatialSeries.Select(sess => sess.Timesteps.Last()).ToArray(); // find DG field identifications which are present in _all_ timesteps var commonFieldIds = new HashSet <string>(); foreach (var fi in tsiS[0].FieldInitializers) { string id = fi.Identification; bool containedInOthers = true; foreach (var tsi in tsiS.Skip(1)) { if (tsi.FieldInitializers.Where(fii => fii.Identification == id).Count() <= 0) { containedInOthers = false; } } if (containedInOthers) { commonFieldIds.Add(id); } } string[] fieldIds = commonFieldIds.ToArray(); // compute L2-errors DGFieldComparison.ComputeErrors(fieldIds, tsiS, out double[] hS, out var DOFs, out var ERRs, out var tsiIdS); // record errors foreach (var id in fieldIds) { Dictionary <Guid, double> err_id; if (!Errors.TryGetValue(id, out err_id)) { err_id = new Dictionary <Guid, double>(); Errors.Add(id, err_id); } for (int iGrd = 0; iGrd < hS.Length; iGrd++) { ITimestepInfo tsi = tsiS.Single(t => t.ID == tsiIdS[iGrd]); ISessionInfo sess = tsi.Session; err_id.Add(sess.ID, ERRs[id][iGrd]); } } } // Set L2 error columns in session table // ===================================== foreach (string fieldName in Errors.Keys) { string colName = "L2Error_" + fieldName; if (owner.AdditionalSessionTableColums.ContainsKey(colName)) { owner.AdditionalSessionTableColums.Remove(colName); } var ErrorsCol = Errors[fieldName]; owner.AdditionalSessionTableColums.Add(colName, delegate(ISessionInfo s) { object ret = 0.0; if (ErrorsCol.ContainsKey(s.ID)) { ret = ErrorsCol[s.ID]; } return(ret); }); } }
/// <summary> /// Addition/Subtraction of DG fields on different grids /// </summary> /// <param name="A"></param> /// <param name="scaleA"></param> /// <param name="B"></param> /// <param name="scaleB"></param> /// <returns> /// <paramref name="scaleA"/>*<paramref name="A"/> + <paramref name="scaleB"/>*<paramref name="B"/> /// </returns> static public DGField ScaledSummation(this DGField A, double scaleA, DGField B, double scaleB) { if (object.ReferenceEquals(A.GridDat, B.GridDat)) { // ++++++++++++++++++++++++++++ // both fields on the same grid // ++++++++++++++++++++++++++++ DGField sum; if (A.Basis.IsSubBasis(B.Basis)) { sum = (DGField)B.Clone(); sum.Scale(scaleB); sum.AccLaidBack(1.0, A); } else if (B.Basis.IsSubBasis(A.Basis)) { sum = (DGField)A.Clone(); sum.Scale(scaleA); sum.AccLaidBack(1.0, B); } else { throw new ApplicationException("can't add the two fields, because their basis are incompatible"); } sum.Identification = "(" + scaleA + "*" + A.Identification + "+" + scaleB + "*" + B.Identification + ")"; return(sum); } else { // ++++++++++++++++++++++++++ // fields on different grids // ++++++++++++++++++++++++++ DGField fine, coarse; double aF, aC; if (A.GridDat.CellPartitioning.TotalLength > B.GridDat.CellPartitioning.TotalLength) { fine = A; coarse = B; aF = scaleA; aC = scaleB; } else { coarse = A; fine = B; aC = scaleA; aF = scaleB; } DGFieldComparison.ComputeFine2CoarseMap( (Foundation.Grid.Classic.GridData)(fine.GridDat), (Foundation.Grid.Classic.GridData)(coarse.GridDat), out var Fine2CoarseMapS); DGField injected; if (coarse is ConventionalDGField) { ConventionalDGField _injected = new SinglePhaseField( new Basis(fine.GridDat, Math.Max(coarse.Basis.Degree, fine.Basis.Degree)), coarse.Identification); DGFieldComparison.InjectDGField(Fine2CoarseMapS, _injected, coarse as ConventionalDGField); injected = _injected; } else if (coarse is XDGField) { XDGField _injected = new XDGField( new XDGBasis((fine as XDGField).Basis.Tracker, Math.Max(coarse.Basis.Degree, fine.Basis.Degree)), coarse.Identification); DGFieldComparison.InjectXDGField(Fine2CoarseMapS, _injected, coarse as XDGField); injected = _injected; } else { throw new NotSupportedException(); } return(ScaledSummation(injected, aC, fine, aF)); } }
/// <summary> /// Estimates the errors of the fields with name /// <paramref name="fieldName"/> in the given /// <paramref name="timesteps"/> by computing the errors with respect /// to the solution on the finest corresponding grid by making use of /// <see cref="BoSSS.Solution.Statistic.DGFieldComparison.ComputeErrors"/>. /// The result is then grouped according to the polynomial degree of field /// <paramref name="fieldName"/>. /// </summary> /// <param name="timesteps"> /// The time-steps containing the fields whose errors should be /// estimated. /// </param> /// <param name="fieldName"> /// The name of the DG field whose error should be estimated. /// </param> /// <param name="xAxis_Is_hOrDof"> /// - true: the x-axis (<see cref="Plot2Ddata.XYvalues.Abscissas"/>) is the grid resolution \f$ h \f$ /// - false: the x-axis (<see cref="Plot2Ddata.XYvalues.Abscissas"/>) is the number of degrees-of-freedom /// </param> /// <param name="normType"> /// H1, L2, etc. /// </param> /// <returns> /// A data set containing information about the grid resolution and the /// corresponding errors with respect to the finest corresponding grid, /// grouped by the polynomial degree. Obviously, the time-step /// associated with the finest grid for each polynomial degree has an /// estimated error of zero (by definition) and is thus excluded from /// the result. /// </returns> public static Plot2Ddata ToEstimatedGridConvergenceData(this IEnumerable <ITimestepInfo> timesteps, string fieldName, bool xAxis_Is_hOrDof = true, NormType normType = NormType.L2_approximate) { Dictionary <string, double[][]> dataGroups = new Dictionary <string, double[][]>(); foreach (var group in timesteps.GroupBy(t => t.Fields.Find(fieldName).Basis.Degree)) { Dictionary <string, int[]> DOFs; Dictionary <string, double[]> errors; double[] resolution; Guid[] tsiIds; if (normType == NormType.L2_embedded) { DGFieldComparison.ComputeErrors( new string[] { fieldName }, group.ToArray(), out resolution, out DOFs, out errors, out tsiIds); } else if (normType == NormType.L2_approximate) { DGFieldComparisonNonEmb.ComputeErrors_L2( new string[] { fieldName }, group.ToArray(), out resolution, out DOFs, out errors, out tsiIds); } else if (normType == NormType.L2noMean_approximate) { DGFieldComparisonNonEmb.ComputeErrors_L2noMean( new string[] { fieldName }, group.ToArray(), out resolution, out DOFs, out errors, out tsiIds); } else if (normType == NormType.H1_approximate) { DGFieldComparisonNonEmb.ComputeErrors_H1( new string[] { fieldName }, group.ToArray(), out resolution, out DOFs, out errors, out tsiIds); } else { throw new NotImplementedException(); } double dim = timesteps.First().Grid.SpatialDimension; Debug.Assert(errors.ContainsKey(fieldName)); Debug.Assert(errors[fieldName].Length == resolution.Length); Debug.Assert(DOFs.ContainsKey(fieldName)); Debug.Assert(DOFs[fieldName].Length == resolution.Length); //double[][] resolutionsAndErrors = new double[2][] { // xAxis_Is_hOrDof ? resolution : DOFs[fieldName].Select(ix => (double)ix).ToArray(), // errors[fieldName] }; double[] xValues = xAxis_Is_hOrDof ? resolution : DOFs[fieldName].Select(ix => Math.Pow((double)ix, 1.0 / dim)).ToArray(); double[] yValues = errors[fieldName]; if (xAxis_Is_hOrDof == false) { // data is sorted according to mesh width, this may not be equal to sorting according to No. of. DOF Array.Sort(xValues, yValues); } dataGroups.Add(group.Key.ToString(), new double[2][] { xValues, yValues }); } return(new Plot2Ddata(dataGroups.ToArray()).WithLogX().WithLogY()); }