void _ReadBoreholeGeologies2(DGObjects objs)
        {
            if (objs.rawDataSet.Tables.Count <= 1)
                return;

            // method2: index the stratra info
            DataTable dt = objs.rawDataSet.Tables[1];
            Dictionary<int, List<BoreholeGeology>> strata_dict =
                new Dictionary<int, List<BoreholeGeology>>();

            // put the strata information into the dictionary
            foreach (DataRow row in dt.Rows)
            {
                if (IsDbNull(row, "StratumID") || IsDbNull(row, "ElevationOfStratumBottom"))
                {
                    string error = string.Format(
                        "Data table [{0}] error: [StratumID] or [ElevationOfStratumBottom] can't be null, [BoreholeID] = {1}."
                        + Environment.NewLine
                        + "This record is ignore. Checking data is strongly recommended.",
                        dt.TableName, row["BoreholeID"]);
                    ErrorReport.Report(error);
                    continue;
                }

                int bhID = ReadInt(row, "BoreholeID").Value;
                List<BoreholeGeology> geo = null;
                if (strata_dict.ContainsKey(bhID))
                    geo = strata_dict[bhID];
                else
                {
                    geo = new List<BoreholeGeology>();
                    strata_dict[bhID] = geo;
                }
                BoreholeGeology bg = new BoreholeGeology();
                bg.StratumID = ReadInt(row, "StratumID").Value;
                bg.Base = ReadDouble(row, "ElevationOfStratumBottom").Value;
                geo.Add(bg);
            }

            // sort the borehole geology
            foreach (var geo in strata_dict.Values)
            {
                geo.Sort((x,y) => x.StratumID.CompareTo(y.StratumID));
            }

            // add the geology to borehole
            foreach (Borehole bh in objs.values)
            {
                List<BoreholeGeology> geo = null;
                if (strata_dict.ContainsKey(bh.id))
                    geo = strata_dict[bh.id];
                else
                    continue;

                double top = bh.Top;
                foreach (var x in geo)
                {
                    x.Top = top;
                    top = x.Base;
                    bh.Geologies.Add(x);
                }
            }
        }
        // Fill borehole geology with zero-thickness strata
        // e.g.
        //      #1: Sequence of simple strata : (2,3,8) -> (2,3,4,5,6,7,8)
        //      where (4,5,6,7) are inserted strata with 0 thickness
        //      #2: Sequence of strata with lens: (2,3,2,8) -> (2,3,2,3,4,5,6,7,8)
        //      where (3,4,5,6,7) are inserted strata with 0 thickness
        //      In case #2, container stratum 2 will be returned.
        static bool FillBoreholeGeology(List<BoreholeGeology> bhGeos, int first, int last,
            List<int> containers)
        {
            bool simple = true;
            for (int i = first, index = 0; i <= last; ++i, ++index)
            {
                if (index == bhGeos.Count)
                    return simple;

                int current = bhGeos[index].StratumID;
                if (current > i)
                {
                    for (int j = i; j < current; ++j)
                    {
                        BoreholeGeology bhGeo = new BoreholeGeology();
                        bhGeo.StratumID = j;
                        bhGeo.Top = bhGeos[index].Top;
                        bhGeo.Base = bhGeo.Top;
                        bhGeos.Insert(index++, bhGeo);
                    }
                }
                else if (current < i)
                {
                    // for stratum lens, see #2
                    // move the next stratum (index+1)
                    index++;
                    int container = current;    // this is a stratum that contain lens
                    if (index < bhGeos.Count)
                    {
                        current = bhGeos[index].StratumID;
                        for (int j = container + 1; j < current; ++j)
                        {
                            BoreholeGeology bhGeo = new BoreholeGeology();
                            bhGeo.StratumID = j;
                            bhGeo.Top = bhGeos[index].Top;
                            bhGeo.Base = bhGeo.Top;
                            bhGeos.Insert(index++, bhGeo);
                        }
                    }
                    simple = false;
                    containers.Add(container);
                }
                i = current;
            }
            return simple;
        }
        void _ReadBoreholeGeologies(DGObjects objs)
        {
            // method1: maybe very slow because linq is slow.
            DataTable dt = objs.rawDataSet.Tables[1];
            foreach (Borehole bh in objs.values)
            {
                var rows = from row in dt.AsEnumerable()
                           where (int)row["BoreholeID"] == bh.id
                           orderby row["ElevationOfStratumBottom"] descending
                           select row;

                double top = bh.Top;
                foreach (DataRow x in rows)
                {
                    //if (x["StratumID"].GetType() == typeof(System.DBNull)
                    //    || x["ElevationOfStratumBottom"].GetType() == typeof(System.DBNull))
                    if (IsDbNull(x, "StratumID") || IsDbNull(x, "ElevationOfStratumBottom"))
                    {
                        string error = string.Format(
                            "Data table [{0}] error: [StratumID] or [ElevationOfStratumBottom] can't be null, [BoreholeID] = {1}."
                            + Environment.NewLine
                            + "This record is ignore. Checking data is strongly recommended.",
                            dt.TableName, x["BoreholeID"]);
                        ErrorReport.Report(error);
                        continue;
                    }
                    BoreholeGeology bg = new BoreholeGeology();
                    bg.StratumID = ReadInt(x, "StratumID").Value;
                    bg.Top = top;
                    bg.Base = ReadDouble(x, "ElevationOfStratumBottom").Value;

                    top = bg.Base;
                    bh.Geologies.Add(bg);
                }
            }
        }
        static void ExtendBorehole(Borehole bh, Borehole bhMax)
        {
            List<BoreholeGeology> bhGeos = bh.Geologies;
            List<BoreholeGeology> bhMaxGeos = bhMax.Geologies;

            double bhBase = bh.Base;
            double bhMaxBase = bhMax.Base;
            if (bhGeos.Count == 0 || bhBase <= bhMaxBase)
                return;
            int index;
            for (index = 0; index < bhMaxGeos.Count; ++index)
            {
                if (bhMaxGeos[index].Base <= bhBase)
                    break;
            }
            if (index == bhMaxGeos.Count)
                index--;
            if (bhGeos[bhGeos.Count - 1].StratumID == bhMaxGeos[index].StratumID)
                bhGeos[bhGeos.Count - 1].Base = bhMaxGeos[index].Base;
            else if (bhGeos[bhGeos.Count - 1].StratumID < bhMaxGeos[index].StratumID)
            {
                BoreholeGeology geo = new BoreholeGeology();
                geo.StratumID = bhMaxGeos[index].StratumID;
                geo.Top = bh.Base;
                geo.Base = bhMaxGeos[index].Base;
                bhGeos.Add(geo);
            }
            else
            {
                while (bhGeos[bhGeos.Count - 1].StratumID > bhMaxGeos[index].StratumID
                    && index < bhMaxGeos.Count - 1)
                    index++;
                bhGeos[bhGeos.Count - 1].Base = bhMaxGeos[index].Base;
            }

            for (int i = index + 1; i < bhMaxGeos.Count; ++i)
            {
                bhGeos.Add(bhMaxGeos[i]);
            }
            bh.Base = bhMaxBase;
        }