public async Task <IActionResult> Edit(int id, [Bind("Id,LakeId,Year,SurfaceFlow,SurfaceOutflow,UndergroundFlow,UndergroundOutflow,Precipitation,Evaporation")] WaterBalance waterBalance)
        {
            if (id != waterBalance.Id)
            {
                return(NotFound());
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(waterBalance);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!WaterBalanceExists(waterBalance.Id))
                    {
                        return(NotFound());
                    }
                    else
                    {
                        throw;
                    }
                }
                return(RedirectToAction(nameof(Index)));
            }
            return(View(waterBalance));
        }
        public async Task <IActionResult> Create([Bind("Id,LakeId,Year,SurfaceFlow,SurfaceOutflow,UndergroundFlow,UndergroundOutflow,Precipitation,Evaporation")] WaterBalance waterBalance)
        {
            if (ModelState.IsValid)
            {
                _context.Add(waterBalance);
                await _context.SaveChangesAsync();

                return(RedirectToAction(nameof(Index)));
            }
            return(View(waterBalance));
        }
        public async Task <IActionResult> Upload(bool FirstRowHeader, IFormFile File)
        {
            try
            {
                string sContentRootPath = _hostingEnvironment.WebRootPath;
                sContentRootPath = Path.Combine(sContentRootPath, "Uploads");
                DirectoryInfo di = new DirectoryInfo(sContentRootPath);
                foreach (FileInfo filed in di.GetFiles())
                {
                    try
                    {
                        filed.Delete();
                    }
                    catch
                    {
                    }
                }
                string path_filename = Path.Combine(sContentRootPath, Path.GetFileName(File.FileName));
                using (var stream = new FileStream(Path.GetFullPath(path_filename), FileMode.Create))
                {
                    await File.CopyToAsync(stream);
                }
                FileInfo fileinfo = new FileInfo(Path.Combine(sContentRootPath, Path.GetFileName(path_filename)));
                using (ExcelPackage package = new ExcelPackage(fileinfo))
                {
                    int start_row = 1;
                    if (FirstRowHeader)
                    {
                        start_row++;
                    }
                    List <WaterBalance> waterBalances = new List <WaterBalance>();
                    for (int i = start_row; ; i++)
                    {
                        if (package.Workbook.Worksheets.FirstOrDefault().Cells[i, 1].Value == null)
                        {
                            break;
                        }
                        WaterBalance waterBalance = new WaterBalance();

                        try
                        {
                            waterBalance.LakeId      = Convert.ToInt32(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 1].Value);
                            waterBalance.Year        = Convert.ToInt32(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 2].Value);
                            waterBalance.SurfaceFlow = package.Workbook.Worksheets.FirstOrDefault().Cells[i, 3].Value == null ?
                                                       (decimal?)null :
                                                       Convert.ToDecimal(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 3].Value);
                            waterBalance.SurfaceOutflow = package.Workbook.Worksheets.FirstOrDefault().Cells[i, 4].Value == null ?
                                                          (decimal?)null :
                                                          Convert.ToDecimal(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 4].Value);
                            waterBalance.UndergroundFlow = package.Workbook.Worksheets.FirstOrDefault().Cells[i, 5].Value == null ?
                                                           (decimal?)null :
                                                           Convert.ToDecimal(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 5].Value);
                            waterBalance.UndergroundOutflow = package.Workbook.Worksheets.FirstOrDefault().Cells[i, 6].Value == null ?
                                                              (decimal?)null :
                                                              Convert.ToDecimal(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 6].Value);
                            waterBalance.Precipitation = Convert.ToDecimal(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 7].Value);
                            waterBalance.Evaporation   = Convert.ToDecimal(package.Workbook.Worksheets.FirstOrDefault().Cells[i, 8].Value);
                        }
                        catch (Exception e)
                        {
                            ViewBag.Error = $"{_sharedLocalizer["Row"]} {i.ToString()}: " + e.Message + (e.InnerException == null ? "" : ": " + e.InnerException.Message);
                            break;
                        }

                        waterBalances.Add(waterBalance);
                        _context.Add(waterBalances.LastOrDefault());
                    }
                    if (string.IsNullOrEmpty(ViewBag.Error))
                    {
                        _context.SaveChanges();
                        ViewBag.Report = $"{_sharedLocalizer["UploadedCount"]}: {waterBalances.Count()}";
                    }
                }
                foreach (FileInfo filed in di.GetFiles())
                {
                    try
                    {
                        filed.Delete();
                    }
                    catch
                    {
                    }
                }
            }
            catch (Exception e)
            {
                if (File != null)
                {
                    ViewBag.Error = e.Message + (e.InnerException == null ? "" : ": " + e.InnerException.Message);
                }
            }
            return(View());
        }
Beispiel #4
0
        /// This alternative approach for obtaining ISRIC soil data need a little bit more work, but is largely complete
        /// There are still bits of the soil organic matter initialisation that should be enhanced.
        /// We probably don't really need two different ways to get to ISRIC data, but it may be interesting to see how the
        /// two compare. The initial motiviation was what appears to be an order-of-magnitude problem with soil carbon
        /// in the World Modellers version.
        /// <summary>
        /// Gets and ISRIC soil description directly from SoilGrids
        /// </summary>
        /// <returns>True if successful</returns>
        private IEnumerable <SoilFromDataSource> GetISRICSoils()
        {
            var    soils = new List <SoilFromDataSource>();
            string url   = "https://rest.soilgrids.org/query?lon=" +
                           longitudeEditBox.Text + "&lat=" + latitudeEditBox.Text;

            try
            {
                double[] bd       = new double[7];
                double[] coarse   = new double[7];
                double[] clay     = new double[7];
                double[] silt     = new double[7];
                double[] sand     = new double[7];
                double[] thetaSat = new double[7];
                double[] awc20    = new double[7];
                double[] awc23    = new double[7];
                double[] awc25    = new double[7];
                double[] thetaWwp = new double[7];
                double[] ocdrc    = new double[7];
                double[] phWater  = new double[7];
                double[] cationEC = new double[7];
                double[] texture  = new double[7];
                string   soilType = String.Empty;
                double   maxTemp  = 0.0;
                double   minTemp  = 0.0;
                double   ppt      = 0.0;
                double   bedrock  = 2500.0;

                string[]     textureClasses = new string[] { "Clay", "Silty Clay", "Sandy Clay", "Clay Loam", "Silty Clay Loam", "Sandy Clay Loam", "Loam", "Silty Loam", "Sandy Loam", "Silt", "Loamy Sand", "Sand", "NO DATA" };
                double[]     textureToAlb   = new double[] { 0.12, 0.12, 0.13, 0.13, 0.12, 0.13, 0.13, 0.14, 0.13, 0.13, 0.16, 0.19, 0.13 };
                double[]     textureToCN2   = new double[] { 73.0, 73.0, 73.0, 73.0, 73.0, 73.0, 73.0, 73.0, 68.0, 73.0, 68.0, 68.0, 73.0 };
                double[]     textureToSwcon = new double[] { 0.25, 0.3, 0.3, 0.4, 0.5, 0.5, 0.5, 0.5, 0.6, 0.5, 0.6, 0.75, 0.5 };
                MemoryStream stream         = WebUtilities.ExtractDataFromURL(url);
                stream.Position = 0;
                JsonTextReader reader = new JsonTextReader(new StreamReader(stream));
                while (reader.Read())
                {
                    if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("properties") && reader.Depth == 1)
                    {
                        reader.Read(); // Read the "start object" token
                        while (reader.Read())
                        {
                            if (reader.TokenType == JsonToken.PropertyName)
                            {
                                string   propName   = reader.Value.ToString();
                                double[] dest       = null;
                                double   multiplier = 1.0;
                                if (propName == "TAXNWRBMajor")
                                {
                                    soilType = reader.ReadAsString();
                                }
                                else if (propName == "TMDMOD_2011")
                                {
                                    maxTemp = 0.0;
                                    reader.Read();
                                    while (reader.Read() && reader.TokenType != JsonToken.EndObject)
                                    {
                                        if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("M"))
                                        {
                                            reader.Read(); // Read start of object token
                                            for (int i = 0; i < 12; i++)
                                            {
                                                reader.Read(); // Read a month name
                                                maxTemp += (double)reader.ReadAsDouble();
                                            }
                                            maxTemp /= 12.0;
                                        }
                                    }
                                }
                                else if (propName == "TMNMOD_2011")
                                {
                                    minTemp = 0.0;
                                    reader.Read();
                                    while (reader.Read() && reader.TokenType != JsonToken.EndObject)
                                    {
                                        if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("M"))
                                        {
                                            reader.Read(); // Read start of object token
                                            for (int i = 0; i < 12; i++)
                                            {
                                                reader.Read(); // Read a month name
                                                minTemp += (double)reader.ReadAsDouble();
                                            }
                                            minTemp /= 12.0;
                                        }
                                    }
                                }
                                else if (propName == "PREMRG")
                                {
                                    ppt = 0.0;
                                    reader.Read();
                                    while (reader.Read() && reader.TokenType != JsonToken.EndObject)
                                    {
                                        if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("M"))
                                        {
                                            reader.Read(); // Read start of object token
                                            for (int i = 0; i < 12; i++)
                                            {
                                                reader.Read(); // Read a month name
                                                ppt += (double)reader.ReadAsDouble();
                                            }
                                        }
                                    }
                                }
                                else if (propName == "BDTICM")  // Is this the best metric to use for find the "bottom" of the soil?
                                {
                                    reader.Read();
                                    while (reader.Read() && reader.TokenType != JsonToken.EndObject)
                                    {
                                        if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("M"))
                                        {
                                            reader.Read(); // Read start of object token
                                            reader.Read(); // Read property name (which ought to be BDTICM_M)
                                            bedrock = 10.0 * (double)reader.ReadAsDouble();
                                            reader.Skip();
                                        }
                                    }
                                }
                                else if (propName == "AWCh1")
                                {
                                    dest       = awc20;
                                    multiplier = 0.01;
                                }
                                else if (propName == "AWCh2")
                                {
                                    dest       = awc23;
                                    multiplier = 0.01;
                                }
                                else if (propName == "AWCh3")
                                {
                                    dest       = awc25;
                                    multiplier = 0.01;
                                }
                                else if (propName == "AWCtS")
                                {
                                    dest       = thetaSat;
                                    multiplier = 0.01;
                                }
                                else if (propName == "BLDFIE")
                                {
                                    dest       = bd;
                                    multiplier = 0.001;
                                }
                                else if (propName == "CECSOL")
                                {
                                    dest       = cationEC;
                                    multiplier = 1.0;
                                }
                                else if (propName == "CLYPPT")
                                {
                                    dest       = clay;
                                    multiplier = 1.0;
                                }
                                else if (propName == "CRFVOL")
                                {
                                    dest       = coarse;
                                    multiplier = 1.0;
                                }
                                else if (propName == "ORCDRC")
                                {
                                    dest       = ocdrc;
                                    multiplier = 0.1;
                                }
                                else if (propName == "PHIHOX")
                                {
                                    dest       = phWater;
                                    multiplier = 0.1;
                                }
                                else if (propName == "SLTPPT")
                                {
                                    dest       = silt;
                                    multiplier = 1.0;
                                }
                                else if (propName == "SNDPPT")
                                {
                                    dest       = sand;
                                    multiplier = 1.0;
                                }
                                else if (propName == "TEXMHT")
                                {
                                    dest       = texture;
                                    multiplier = 1.0;
                                }
                                else if (propName == "WWP")
                                {
                                    dest       = thetaWwp;
                                    multiplier = 0.01;
                                }

                                if (dest != null)
                                {
                                    reader.Read();
                                    while (reader.Read() && reader.TokenType != JsonToken.EndObject)
                                    {
                                        if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("M"))
                                        {
                                            while (reader.Read() && reader.TokenType != JsonToken.EndObject)
                                            {
                                                if (reader.TokenType == JsonToken.PropertyName)
                                                {
                                                    string tokenName = reader.Value.ToString();
                                                    if (tokenName.StartsWith("sl"))
                                                    {
                                                        int index = Int32.Parse(tokenName.Substring(2)) - 1;
                                                        dest[index] = (double)reader.ReadAsDouble() * multiplier;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    reader.Skip();
                                }
                            }
                        }
                    }
                }

                var          newSoil         = new Soil();
                Chemical     analysis        = new Chemical();
                Physical     waterNode       = new Physical();
                Organic      organicMatter   = new Organic();
                WaterBalance soilWater       = new WaterBalance();
                InitialWater initialWater    = new InitialWater();
                Sample       initialNitrogen = new Sample();
                SoilNitrogen soilN           = new SoilNitrogen();

                SoilCrop wheat = new SoilCrop();
                waterNode.Children.Add(wheat);
                wheat.Name = "WheatSoil";
                waterNode.ParentAllDescendants();

                Model nh4 = new SoilNitrogenNH4();
                nh4.Name = "NH4";
                soilN.Children.Add(nh4);
                Model no3 = new SoilNitrogenNO3();
                no3.Name = "NO3";
                soilN.Children.Add(no3);
                Model urea = new SoilNitrogenUrea();
                urea.Name = "Urea";
                soilN.Children.Add(urea);
                Model plantAvailNH4 = new SoilNitrogenPlantAvailableNH4();
                plantAvailNH4.Name = "PlantAvailableNH4";
                soilN.Children.Add(plantAvailNH4);
                Model plantAvailNO3 = new SoilNitrogenPlantAvailableNO3();
                plantAvailNO3.Name = "PlantAvailableNO3";
                soilN.Children.Add(plantAvailNO3);
                soilN.ParentAllDescendants();

                newSoil.Children.Add(waterNode);
                newSoil.Children.Add(soilWater);
                newSoil.Children.Add(soilN);
                newSoil.Children.Add(organicMatter);
                newSoil.Children.Add(analysis);
                newSoil.Children.Add(initialWater);
                newSoil.Children.Add(initialNitrogen);
                newSoil.Children.Add(new CERESSoilTemperature());
                newSoil.ParentAllDescendants();
                newSoil.OnCreated();

                newSoil.Name       = "Synthetic soil derived from ISRIC SoilGrids REST API";
                newSoil.DataSource = "ISRIC SoilGrids";
                newSoil.SoilType   = soilType;
                newSoil.Latitude   = Convert.ToDouble(latitudeEditBox.Text, System.Globalization.CultureInfo.InvariantCulture);
                newSoil.Longitude  = Convert.ToDouble(longitudeEditBox.Text, System.Globalization.CultureInfo.InvariantCulture);

                // ISRIC values are for "levels", not "intervals", so we need to convert to layers
                // Following Andrew Moore's lead on layer thickness and weightings.

                double[] thickness  = new double[] { 150.0, 150.0, 150.0, 150.0, 200.0, 200.0, 200.0, 200.0, 300.0, 300.0 };
                double[] depth      = new double[thickness.Length];
                int      layerCount = thickness.Length;
                for (int i = 0; i < thickness.Length; i++)
                {
                    depth[i] = thickness[i] + (i > 0 ? depth[i - 1] : 0.0);
                    if ((i > 0) && (layerCount == thickness.Length) && (bedrock < depth[i] + 20.0))
                    {
                        layerCount   = i + 1;
                        thickness[i] = Math.Min(thickness[i], Math.Max(0.0, bedrock - (depth[i] - thickness[i])));
                        if (i == 1)
                        {
                            thickness[i] = Math.Max(50.0, thickness[i]);
                        }
                        Array.Resize(ref thickness, layerCount);
                    }
                }

                analysis.Thickness      = thickness;
                waterNode.Thickness     = thickness;
                soilWater.Thickness     = thickness;
                organicMatter.Thickness = thickness;

                initialWater.Name          = "Initial water";
                initialWater.PercentMethod = InitialWater.PercentMethodEnum.FilledFromTop;
                initialWater.FractionFull  = 0.0;

                // Initialise nitrogen to 0.0
                initialNitrogen.Name = "Initial nitrogen";
                initialNitrogen.NH4  = new double[layerCount];
                initialNitrogen.NO3  = new double[layerCount];

                double tAvg = (maxTemp + minTemp) / 2.0;
                soilWater.CNCov      = 0.0;
                soilWater.CNRed      = 20.0;
                soilWater.SummerDate = newSoil.Latitude <= 0.0 ? "1-nov" : "1-may";
                soilWater.WinterDate = newSoil.Latitude <= 0.0 ? "1-apr" : "1-oct";
                soilWater.SummerCona = 6.0;
                soilWater.SummerU    = 6.0;
                soilWater.WinterCona = tAvg < 21.0 ? 2.5 : 6.0;
                soilWater.WinterU    = tAvg < 21.0 ? 4.0 : 6.0;
                soilWater.Salb       = textureToAlb[(int)Math.Round(texture[0] - 1)];
                soilWater.CN2Bare    = textureToCN2[(int)Math.Round(texture[0] - 1)];
                double[] swcon = new double[7];
                for (int i = 0; i < 7; i++)
                {
                    swcon[i] = textureToSwcon[(int)Math.Round(texture[i] - 1)];
                }
                soilWater.SWCON = ConvertLayers(swcon, layerCount);

                waterNode.BD     = ConvertLayers(bd, layerCount);
                waterNode.LL15   = ConvertLayers(thetaWwp, layerCount);
                waterNode.SAT    = ConvertLayers(thetaSat, layerCount);
                waterNode.AirDry = ConvertLayers(MathUtilities.Divide_Value(thetaWwp, 3.0), layerCount);
                double[] dul = new double[7];
                for (int i = 0; i < 7; i++)
                {
                    dul[i] = thetaWwp[i] + awc20[i];  // This could be made Moore complex
                }
                waterNode.DUL = ConvertLayers(dul, layerCount);

                waterNode.ParticleSizeSand = ConvertLayers(sand, layerCount);
                waterNode.ParticleSizeSilt = ConvertLayers(silt, layerCount);
                waterNode.ParticleSizeClay = ConvertLayers(clay, layerCount);
                // waterNode.Rocks = ConvertLayers(coarse, layerCount);
                analysis.PH = ConvertLayers(phWater, layerCount);
                // Obviously using the averaging in "ConvertLayers" for texture classes is not really correct, but should be OK as a first pass if we don't have sharply contrasting layers
                double[] classes  = ConvertLayers(texture, layerCount);
                string[] textures = new string[layerCount];
                for (int i = 0; i < layerCount; i++)
                {
                    textures[i] = textureClasses[(int)Math.Round(classes[i]) - 1];
                }


                double[] xf          = new double[layerCount];
                double[] kl          = new double[layerCount];
                double[] ll          = new double[layerCount];
                double   p1          = 1.4;
                double   p2          = 1.60 - p1;
                double   p3          = 1.80 - p1;
                double   topEffDepth = 0.0;
                double   klMax       = 0.06;
                double   depthKl     = 900.0;
                double   depthRoot   = 1900.0;

                for (int i = 0; i < layerCount; i++)
                {
                    xf[i] = 1.0 - (waterNode.BD[i] - (p1 + p2 * 0.01 * waterNode.ParticleSizeSand[i])) / p3;
                    xf[i] = Math.Max(0.1, Math.Min(1.0, xf[i]));
                    double effectiveThickness = thickness[i] * xf[i];
                    double bottomEffDepth     = topEffDepth + effectiveThickness;
                    double propMaxKl          = Math.Max(0.0, Math.Min(bottomEffDepth, depthKl) - topEffDepth) / effectiveThickness;
                    double propDecrKl         = Math.Max(Math.Max(0.0, Math.Min(bottomEffDepth, depthRoot) - topEffDepth) / effectiveThickness - propMaxKl, 0.0);
                    double propZeroKl         = 1.0 - propMaxKl - propDecrKl;
                    double ratioTopDepth      = Math.Max(0.0, Math.Min((depthRoot - topEffDepth) / (depthRoot - depthKl), 1.0));
                    double ratioBottomDepth   = Math.Max(0.0, Math.Min((depthRoot - bottomEffDepth) / (depthRoot - depthKl), 1.0));
                    double meanDecrRatio      = 0.5 * (ratioTopDepth + ratioBottomDepth);
                    double weightedRatio      = propMaxKl * 1.0 + propDecrKl * meanDecrRatio + propZeroKl * 0.0;
                    kl[i] = klMax * weightedRatio;
                    ll[i] = waterNode.LL15[i] + (waterNode.DUL[i] - waterNode.LL15[i]) * (1.0 - weightedRatio);
                    if (kl[i] <= 0.0)
                    {
                        xf[i] = 0.0;
                    }
                    topEffDepth = bottomEffDepth;
                }
                wheat.XF = xf;
                wheat.KL = kl;
                wheat.LL = ll;

                organicMatter.Carbon = ConvertLayers(ocdrc, layerCount);

                double rootWt = Math.Max(0.0, Math.Min(3000.0, 2.5 * (ppt - 100.0)));
                // For AosimX, root wt needs to be distributed across layers. This conversion logic is adapted from that used in UpgradeToVersion52
                double[] rootWtFraction = new double[layerCount];
                double   profileDepth   = depth[layerCount - 1];
                double   cumDepth       = 0.0;
                for (int layer = 0; layer < layerCount; layer++)
                {
                    double fracLayer = Math.Min(1.0, MathUtilities.Divide(profileDepth - cumDepth, thickness[layer], 0.0));
                    cumDepth += thickness[layer];
                    rootWtFraction[layer] = fracLayer * Math.Exp(-3.0 * Math.Min(1.0, MathUtilities.Divide(cumDepth, profileDepth, 0.0)));
                }
                // get the actuall FOM distribution through layers (adds up to one)
                double totFOMfraction = MathUtilities.Sum(rootWtFraction);
                for (int layer = 0; layer < thickness.Length; layer++)
                {
                    rootWtFraction[layer] /= totFOMfraction;
                }
                organicMatter.FOM = MathUtilities.Multiply_Value(rootWtFraction, rootWt);

                double[] fBiom = { 0.04,                                                                                                                             0.04 - 0.03 * (225.0 - 150.0) / (400.0 - 150.0),
                                   (400.0 - 300.0) / (450.0 - 300.0) * (0.04 - 0.03 * (350.0 - 150.0) / (400.0 - 150.0)) + (450.0 - 400.0) / (450.0 - 300.0) * 0.01,
                                   0.01,                                                                                                                                                                        0.01,0.01, 0.01, 0.01, 0.01, 0.01 };
                Array.Resize(ref fBiom, layerCount);
                double   inert_c = 0.95 * ocdrc[4];
                double[] fInert  = new double[7];
                for (int layer = 0; layer < 7; layer++)
                {
                    fInert[layer] = Math.Min(0.99, inert_c / ocdrc[layer]);
                }
                organicMatter.FInert      = ConvertLayers(fInert, layerCount); // Not perfect, but should be good enough
                organicMatter.FBiom       = fBiom;
                organicMatter.FOMCNRatio  = 40.0;
                organicMatter.SoilCNRatio = Enumerable.Repeat(11.0, layerCount).ToArray(); // Is there any good way to estimate this? ISRIC provides no N data

                newSoil.Children.Add(new CERESSoilTemperature());
                newSoil.OnCreated();

                soils.Add(new SoilFromDataSource()
                {
                    DataSource = "ISRIC",
                    Soil       = newSoil
                });
            }
            catch (Exception)
            {
            }
            return(soils);
        }
Beispiel #5
0
        /// <summary>Convert a table of soils data into a list of soils.</summary>
        public static List <Soil> ToSoils(DataTable table)
        {
            var soils = new List <Soil>();

            // Loop through all blocks of rows in datatable, create a
            // soil and store soil in correct location in the AllSoils XML.
            int row = 0;

            while (row < table.Rows.Count)
            {
                // Find the end of this soil i.e. the row that has a different value for 'Name'
                // to the current row.
                int endRow = row + 1;
                while (endRow < table.Rows.Count &&
                       table.Rows[endRow]["Name"].ToString() == table.Rows[row]["Name"].ToString())
                {
                    endRow++;
                }
                int numLayers = endRow - row;

                var soil = new Soil();
                soil.Name              = table.Rows[row]["Name"].ToString();
                soil.Country           = GetStringValue(table, row, "Country");
                soil.State             = GetStringValue(table, row, "State");
                soil.Region            = GetStringValue(table, row, "Region");
                soil.NearestTown       = GetStringValue(table, row, "NearestTown");
                soil.Site              = GetStringValue(table, row, "Site");
                soil.ApsoilNumber      = GetStringValue(table, row, "APSoilNumber");
                soil.SoilType          = GetStringValue(table, row, "Texture");
                soil.LocalName         = GetStringValue(table, row, "LocalName");
                soil.ASCOrder          = GetStringValue(table, row, "ASC_Order");
                soil.ASCSubOrder       = GetStringValue(table, row, "ASC_Sub-order");
                soil.Latitude          = GetDoubleValue(table, row, "Latitude");
                soil.Longitude         = GetDoubleValue(table, row, "Longitude");
                soil.LocationAccuracy  = GetStringValue(table, row, "LocationAccuracy");
                soil.YearOfSampling    = GetStringValue(table, row, "YearOfSampling");
                soil.DataSource        = GetStringValue(table, row, "DataSource");
                soil.Comments          = GetStringValue(table, row, "Comments");
                soil.NaturalVegetation = GetStringValue(table, row, "NaturalVegetation");
                soil.RecordNumber      = GetIntegerValue(table, row, "RecordNo");

                var physical = new Physical();
                soil.Children.Add(physical);
                physical.Thickness                = MathUtilities.RemoveMissingValuesFromBottom(GetDoubleValues(table, "Thickness (mm)", row, numLayers));
                physical.BD                       = GetDoubleValues(table, "BD", row, numLayers);
                physical.BDMetadata               = GetCodeValues(table, "BDCode", row, numLayers);
                physical.SAT                      = GetDoubleValues(table, "SAT (mm/mm)", row, numLayers);
                physical.SATMetadata              = GetCodeValues(table, "SATCode", row, numLayers);
                physical.DUL                      = GetDoubleValues(table, "DUL (mm/mm)", row, numLayers);
                physical.DULMetadata              = GetCodeValues(table, "DULCode", row, numLayers);
                physical.LL15                     = GetDoubleValues(table, "LL15 (mm/mm)", row, numLayers);
                physical.LL15Metadata             = GetCodeValues(table, "LL15Code", row, numLayers);
                physical.AirDry                   = GetDoubleValues(table, "Airdry (mm/mm)", row, numLayers);
                physical.AirDryMetadata           = GetCodeValues(table, "AirdryCode", row, numLayers);
                physical.KS                       = GetDoubleValues(table, "KS (mm/day)", row, numLayers);
                physical.KSMetadata               = GetCodeValues(table, "KSCode", row, numLayers);
                physical.Rocks                    = GetDoubleValues(table, "Rocks (%)", row, numLayers);
                physical.RocksMetadata            = GetCodeValues(table, "RocksCode", row, numLayers);
                physical.Texture                  = GetStringValues(table, "Texture", row, numLayers);
                physical.TextureMetadata          = GetCodeValues(table, "TextureCode", row, numLayers);
                physical.ParticleSizeSand         = GetDoubleValues(table, "ParticleSizeSand (%)", row, numLayers);
                physical.ParticleSizeSandMetadata = GetCodeValues(table, "ParticleSizeSandCode", row, numLayers);
                physical.ParticleSizeSilt         = GetDoubleValues(table, "ParticleSizeSilt (%)", row, numLayers);
                physical.ParticleSizeSiltMetadata = GetCodeValues(table, "ParticleSizeSiltCode", row, numLayers);
                physical.ParticleSizeClay         = GetDoubleValues(table, "ParticleSizeClay (%)", row, numLayers);
                physical.ParticleSizeClayMetadata = GetCodeValues(table, "ParticleSizeClayCode", row, numLayers);

                var soilWater = new WaterBalance();
                soilWater.ResourceName = "WaterBalance";
                soil.Children.Add(soilWater);
                soilWater.Thickness   = physical.Thickness;
                soilWater.SummerU     = GetDoubleValue(table, row, "SummerU");
                soilWater.SummerCona  = GetDoubleValue(table, row, "SummerCona");
                soilWater.WinterU     = GetDoubleValue(table, row, "WinterU");
                soilWater.WinterCona  = GetDoubleValue(table, row, "WinterCona");
                soilWater.SummerDate  = GetStringValue(table, row, "SummerDate");
                soilWater.WinterDate  = GetStringValue(table, row, "WinterDate");
                soilWater.Salb        = GetDoubleValue(table, row, "Salb");
                soilWater.DiffusConst = GetDoubleValue(table, row, "DiffusConst");
                soilWater.DiffusSlope = GetDoubleValue(table, row, "DiffusSlope");
                soilWater.CN2Bare     = GetDoubleValue(table, row, "Cn2Bare");
                soilWater.CNRed       = GetDoubleValue(table, row, "CnRed");
                soilWater.CNCov       = GetDoubleValue(table, row, "CnCov");
                soilWater.SWCON       = GetDoubleValues(table, "SWCON (0-1)", row, numLayers);

                var organic = new Organic();
                soil.Children.Add(organic);
                organic.Thickness      = physical.Thickness;
                organic.FOMCNRatio     = GetDoubleValue(table, row, "RootCN");
                organic.FOM            = MathUtilities.CreateArrayOfValues(GetDoubleValue(table, row, "RootWt"), numLayers);
                organic.SoilCNRatio    = GetDoubleValues(table, "SoilCN", row, numLayers);
                organic.FBiom          = GetDoubleValues(table, "FBIOM (0-1)", row, numLayers);
                organic.FInert         = GetDoubleValues(table, "FINERT (0-1)", row, numLayers);
                organic.Carbon         = GetDoubleValues(table, "OC", row, numLayers);
                organic.CarbonMetadata = GetCodeValues(table, "OCCode", row, numLayers);

                var chemical = new Chemical();
                soil.Children.Add(chemical);
                chemical.Thickness   = physical.Thickness;
                chemical.EC          = GetDoubleValues(table, "EC (1:5 dS/m)", row, numLayers);
                chemical.ECMetadata  = GetCodeValues(table, "ECCode", row, numLayers);
                chemical.PH          = GetDoubleValues(table, "PH", row, numLayers);
                chemical.PHMetadata  = GetCodeValues(table, "PHCode", row, numLayers);
                chemical.CL          = GetDoubleValues(table, "CL (mg/kg)", row, numLayers);
                chemical.CLMetadata  = GetCodeValues(table, "CLCode", row, numLayers);
                chemical.ESP         = GetDoubleValues(table, "ESP (%)", row, numLayers);
                chemical.ESPMetadata = GetCodeValues(table, "ESPCode", row, numLayers);

                // Add in some necessary models.
                var soilTemp = new CERESSoilTemperature();
                soilTemp.Name = "Temperature";
                soil.Children.Add(soilTemp);
                var nutrient = new Nutrients.Nutrient();
                nutrient.ResourceName = "Nutrient";
                soil.Children.Add(nutrient);
                var initialWater = new InitialWater();
                soil.Children.Add(initialWater);

                // crops
                foreach (DataColumn Col in table.Columns)
                {
                    if (Col.ColumnName.ToLower().Contains(" ll"))
                    {
                        var nameBits = Col.ColumnName.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        if (nameBits.Length == 3)
                        {
                            string   cropName = nameBits[0];
                            SoilCrop crop     = new SoilCrop();
                            crop.Name       = cropName + "Soil";
                            crop.LL         = GetDoubleValues(table, cropName + " ll (mm/mm)", row, numLayers);
                            crop.LLMetadata = GetCodeValues(table, cropName + " llCode", row, numLayers);
                            crop.KL         = GetDoubleValues(table, cropName + " kl (/day)", row, numLayers);
                            crop.XF         = GetDoubleValues(table, cropName + " xf (0-1)", row, numLayers);
                            if (MathUtilities.ValuesInArray(crop.LL) ||
                                MathUtilities.ValuesInArray(crop.KL))
                            {
                                physical.Children.Add(crop);
                            }
                        }
                    }
                }

                soils.Add(soil);

                row += numLayers;
            }

            return(soils);
        }