public SiteViewModel( GeoRefData Area, IWellCollection  Wells)
    {
      area = Area;
      site = area.Geometry as XYPolygon;
      this.Wells = Wells;

      DisplayName = Area.Data[0].ToString();
      Samples = new ObservableCollection<Sample>();
    }
Beispiel #2
0
    public void SaveToShape(string ShapeFileName)
    {
      using (ShapeWriter sw = new ShapeWriter(ShapeFileName))
      {
        DataTable dt = new DataTable();
        dt.Columns.Add("LinkID", typeof(string));
        dt.Columns.Add("FromNode", typeof(string));
        dt.Columns.Add("ToNode", typeof(string));
        dt.Columns.Add("SpecifiedLength", typeof(double));

        foreach (var b in Links.Values)
        {
          GeoRefData grf = new GeoRefData();
          var l = new XYPolyline();
          l.Points.Add(new XYPoint(b.UpstreamNode.pfsnode.X, b.UpstreamNode.pfsnode.Y));
          l.Points.Add(new XYPoint(b.DownstreamNode.pfsnode.X, b.DownstreamNode.pfsnode.Y));
          grf.Geometry = l;
          grf.Data = dt.NewRow();
          grf.Data[0] = b.pfslink.LinkID;
          grf.Data[1] = b.pfslink.FromNode;
          grf.Data[2] = b.pfslink.ToNode;
          grf.Data[3] = b.pfslink.SpecifiedLength;
          sw.Write(grf);
        }
      }

      if (Branches != null && Branches.Count > 0)
      {
        using (ShapeWriter sw = new ShapeWriter(Path.Combine(Path.GetDirectoryName(ShapeFileName), Path.GetFileNameWithoutExtension(ShapeFileName)+"_branches.shp")))
        {
          DataTable dt = new DataTable();
          dt.Columns.Add("Name", typeof(string));
          dt.Columns.Add("Length", typeof(double));

          foreach (var b in Branches)
          {

            var line = new XYPolyline();
            line.Points.AddRange(b.Links.Select(p => p.UpstreamNode.Location));
            line.Points.Add(b.Links.Last().DownstreamNode.Location);
            GeoRefData grf = new GeoRefData();
            grf.Geometry = line; 
            grf.Data = dt.NewRow();
            grf.Data[0] = b.Name;
            grf.Data[1] = line.GetLength();
            
            sw.Write(grf);


          }



        }

      }
    }
Beispiel #3
0
    public void Print()
    {

      LogThis("Writing output");
      if (AlldataFile != null)
        StateVariables.ToCSV(AlldataFile.FileName);


      if (ExcelTemplate != null)
      {
        Accumulated.ToExcelTemplate(ExcelTemplate.FileName, ExcelTemplate.ColumnNames[0]);
      }


      foreach (var detailed in DetailedParameterTimeSeries)
      {
        if (detailed.Flags.First())
          Accumulated.ToCSV(detailed.ColumnNames.First(), detailed.FileName);
        else
          StateVariables.ToCSV(detailed.ColumnNames.First(), detailed.FileName);
      }

      foreach (var detailed in DetailedCatchmentTimeSeries)
      {
        if (detailed.Flags.First())
          Accumulated.ToCSV((int)detailed.Parameters.First(), detailed.FileName);
        else
          StateVariables.ToCSV((int)detailed.Parameters.First(), detailed.FileName);
      }

      foreach (var statmap in StatisticsMap)
      {
        var Ctime = new DateTime((int)statmap.Parameters[0], (int)statmap.Parameters[2], 1);
        var sumend = new DateTime((int)statmap.Parameters[1], (int)statmap.Parameters[3], 1);

        if (statmap.Flags[2])
          StatWriter(statmap.FileName, "ObservedFlow", "M11Flow", Ctime, sumend, statmap.Flags[0], statmap.Flags[1]);
        else
          StatWriter(statmap.FileName, "ObservedNitrate", "DownStreamOutput", Ctime, sumend, statmap.Flags[0], statmap.Flags[1]);
      }


      foreach (var mapout in MapOutputFiles)
      {
        using (ShapeWriter sw = new ShapeWriter(mapout.FileName) { Projection = projection })
        {
          DataTable data;
          if (mapout.Flags[1])
            data = Accumulated;
          else
            data = StateVariables;

          foreach (var c in AllCatchments.Values)
          {
            GeoRefData gd = new GeoRefData() { Geometry = c.Geometry };
            gd.Data = data.NewRow();

            gd.Data[0] = c.ID;

            var Ctime = new DateTime((int)mapout.Parameters[0], (int)mapout.Parameters[2], 1);
            if (Ctime < Start)
              Ctime = Start;
            var sumend = new DateTime((int)mapout.Parameters[1], (int)mapout.Parameters[3], 1);
            if (sumend > End)
              sumend = End;
            for (int k = 2; k < data.Columns.Count; k++)
              gd.Data[k] = 0;
            
            while (Ctime < sumend)
            {
              var row = data.Rows.Find(new object[] { c.ID, Ctime });
              for (int k = 2; k < data.Columns.Count; k++)
                if (!row.IsNull(k) & data.Columns[k].DataType == typeof(double))
                  gd.Data[k] = (double)gd.Data[k] + (double)row[k];
              Ctime = Ctime.AddMonths(1);
            }
            if (mapout.Flags[0])
            {
              for (int k = 2; k < data.Columns.Count; k++)
                if (!gd.Data.IsNull(k))
                  gd.Data[k] = (double)gd.Data[k] / c.Geometry.GetArea();
            }
            sw.Write(gd);
          }
        }
      }
    }
Beispiel #4
0
    private void StatWriter(string FilenName, string ObsColumn, string SimColumn, DateTime Start, DateTime End, bool Yearly, bool SendFactorUpstream)
    {
      //        var obs = StateVariables.ExtractTimeSeries("ObservedNitrate");
      //      var sim = StateVariables.ExtractTimeSeries("DownStreamOutput");

      var obs = StateVariables.ExtractTimeSeries(ObsColumn);
      var sim = StateVariables.ExtractTimeSeries(SimColumn);


      using (ShapeWriter sw = new ShapeWriter(FilenName) { Projection = projection })
      {
        DataTable data = new DataTable();

        data.Columns.Add("ID15", typeof(int));
        data.Columns.Add("ME", typeof(double));
        data.Columns.Add("MAE", typeof(double));
        data.Columns.Add("RMSE", typeof(double));
        data.Columns.Add("FBAL", typeof(double));
        data.Columns.Add("R2", typeof(double));
        data.Columns.Add("bR2", typeof(double));
        data.Columns.Add("NValues", typeof(int));
        data.Columns.Add("OBS", typeof(double));
        data.Columns.Add("SIM", typeof(double));
        data.Columns.Add("BiasFactor", typeof(double));

        foreach (var kvp in obs)
        {
          ZoomTimeSeries obsr = new ZoomTimeSeries() { Accumulate = true };
          obsr.GetTs(TimeStepUnit.Month).AddRange(Start, kvp.Value.GetValues(Start, End));

          ZoomTimeSeries simr = new ZoomTimeSeries() { Accumulate = true };
          simr.GetTs(TimeStepUnit.Month).AddRange(Start, sim[kvp.Key].GetValues(Start, End));

          FixedTimeStepSeries obsreduced;
          FixedTimeStepSeries simreduced;
          if (Yearly)
          {
            obsreduced = obsr.GetTs(TimeStepUnit.Year);
            simreduced = simr.GetTs(TimeStepUnit.Year);
          }
          else
          {
            obsreduced = obsr.GetTs(TimeStepUnit.Month);
            simreduced = simr.GetTs(TimeStepUnit.Month);
          }

          double? me = obsreduced.ME(simreduced);
          if (me.HasValue)
          {
            GeoRefData gd = new GeoRefData() { Geometry = AllCatchments[kvp.Key].Geometry };
            gd.Data = data.NewRow();
            gd.Data[0] = kvp.Key;
            gd.Data[1] = me;
            gd.Data[2] = obsreduced.MAE(simreduced);
            gd.Data[3] = obsreduced.RMSE(simreduced);
            gd.Data[4] = obsreduced.FBAL(simreduced);
            gd.Data[5] = obsreduced.R2(simreduced);
            var bR2 = obsreduced.bR2(simreduced);
            if (bR2.HasValue)
              gd.Data[6] = bR2;
            else
              gd.Data[6] = DBNull.Value;

            double[] val1;
            double[] val2;
            obsreduced.AlignRemoveDeletevalues(simreduced, out val1, out val2);
            gd.Data[7] = val1.Count();
            gd.Data[8] = val1.Average();
            gd.Data[9] = val2.Average();
            gd.Data[10] = val1.Average() / val2.Average();
            sw.Write(gd);

            if(SendFactorUpstream)
              RecursiveWriter(AllCatchments[kvp.Key], sw, data, "BiasFactor", (double)gd.Data[10]);
            
          }
        }
      }
    }
    public override void DebugPrint(string Directory, Dictionary<int, Catchment> Catchments)
    {

      if (Reduction.Count == 0)
        return;

      DataTable dt = new DataTable();
      dt.Columns.Add("ID15", typeof(int));
      dt.Columns.Add("RedFactor", typeof(double));

      using (ShapeWriter sw = new ShapeWriter(Path.Combine(Directory, Name + "_factors")) { Projection = MainModel.projection })
      {
        foreach (var kvp in Reduction)
        {
          if (Catchments.ContainsKey(kvp.Key))
          {
            GeoRefData gd = new GeoRefData() { Geometry = Catchments[kvp.Key].Geometry };
            gd.Data = dt.NewRow();
            gd.Data[0] = kvp.Key;
            gd.Data["RedFactor"] = kvp.Value;
            sw.Write(gd);
          }
        }
      }
    }
Beispiel #6
0
 private void RecursiveWriter(Catchment c, ShapeWriter sw, DataTable data, string ColumnName, double Factor)
 {
   foreach (var upc in c.UpstreamConnections.Where(cc => cc.Measurements == null))
   {
     GeoRefData gd = new GeoRefData() { Geometry = upc.Geometry };
     gd.Data = data.NewRow();
     gd.Data[0] = upc.ID;
     gd.Data[ColumnName] = Factor;
     sw.Write(gd);
     RecursiveWriter(upc, sw, data,ColumnName, Factor);
   }
 }
Beispiel #7
0
    public void Calibrate(MainModel MW, DateTime CStart, DateTime CEnd)
    {
      dt.Columns.Add("ID15", typeof(int));
      dt.Columns.Add("No_iterations", typeof(int));
      dt.Columns.Add("LastError", typeof(double));
      dt.Columns.Add("GWRatio", typeof(double));
      dt.Columns.Add("IntRatio", typeof(double));
      dt.Columns.Add("MainRatio", typeof(double));

      dt.Columns.Add("RedFactor", typeof(double));
      dt.PrimaryKey = new DataColumn[]{ dt.Columns[0]};
      DateTime CurrentTime = CStart;


      string gwsourcename =MW.SourceModels.Single(s=>s.GetType()==typeof(GroundWaterSource)).Name;
      foreach (var item in MW.AllCatchments.Values)
      {
        var row = dt.NewRow();
        row[0] = item.ID;
        CurrentTime = CStart;

        double gwleach = 0;
        double gwsourec = 0;
        double gwConcDeg = 0;
        double intsource = 0;
        double intred = 0;
        double upstream = 0;
        double mainred = 0;

        while (CurrentTime < CEnd)
        {
          double IntMass = 0;
          var CurrentState = MW.StateVariables.Rows.Find(new object[] { item.ID, CurrentTime });

          gwleach += (double) CurrentState["Leaching"];

          gwsourec += (double)CurrentState[gwsourcename];
          IntMass = (double)CurrentState[gwsourcename];

          foreach (var conc in MW.InternalReductionModels.Where(s => s.GetType() == typeof(ConceptualSourceReducer) && ((ConceptualSourceReducer)s).SourceModelName == gwsourcename))
          {
            gwConcDeg += (double)CurrentState[conc.Name];
            IntMass -= (double)CurrentState[conc.Name];
          }

          foreach (var intsou in MW.SourceModels.Where(s => s.Name != gwsourcename))
          {
            intsource += (double)CurrentState[intsou.Name];
            IntMass += (double)CurrentState[intsou.Name];
          }

          foreach (var conc in MW.InternalReductionModels.Where(s => s.GetType() != typeof(ConceptualSourceReducer)))
          {
            intred += (double)CurrentState[conc.Name];
            IntMass -= (double)CurrentState[conc.Name];
          }

          foreach (var mainr in MW.MainStreamRecutionModels)
          {
            if (!CurrentState.IsNull(mainr.Name))
            {
              mainred += (double)CurrentState[mainr.Name];
              IntMass -= (double)CurrentState[mainr.Name];
            }
          }
          if (!CurrentState.IsNull("DownStreamOutput"))
          {
            IntMass = (double)CurrentState["DownStreamOutput"] - IntMass;

            upstream += IntMass;
          }
          CurrentTime = CurrentTime.AddMonths(1);
        }

        if (gwleach == 0)
          row["GWRatio"]=1;
        else
          row["GWRatio"] = (gwleach - gwsourec + gwConcDeg) / gwleach;
        row["IntRatio"] = intred / (gwsourec - gwConcDeg + intsource);
        row["MainRatio"] = mainred / (gwsourec - gwConcDeg + intsource - intred + upstream);

        dt.Rows.Add(row);
      }

      
       CurrentTime = CStart;

      this.MW = MW;
      List<Catchment> SortedCatchments = new List<Catchment>();

      ConceptualSourceReducer GWCor = new ConceptualSourceReducer();
      GWCor.Name = "Calibrator";
      GWCor.SourceModelName = "GroundWater";

      var LastConceptual = MW.InternalReductionModels.LastOrDefault(c => c.GetType() == typeof(ConceptualSourceReducer));
      if (LastConceptual == null)
        MW.InternalReductionModels.Insert(0, GWCor);
      else
        MW.InternalReductionModels.Insert(MW.InternalReductionModels.IndexOf(LastConceptual) + 1, GWCor);

      if (!MW.StateVariables.Columns.Contains(GWCor.Name))
        MW.StateVariables.Columns.Add(GWCor.Name, typeof(double));

      ConceptualSourceReducer IntCor = new ConceptualSourceReducer();
      IntCor.Name = "Calib_Int";
      MW.InternalReductionModels.Add(IntCor);
      if (!MW.StateVariables.Columns.Contains(IntCor.Name))
        MW.StateVariables.Columns.Add(IntCor.Name, typeof(double));

      ConceptualSourceReducer MainCor = new ConceptualSourceReducer();
      MainCor.Name = "Calib_Main";
      MW.MainStreamRecutionModels.Add(MainCor);
      if (!MW.StateVariables.Columns.Contains(MainCor.Name))
        MW.StateVariables.Columns.Add(MainCor.Name, typeof(double));

      foreach (var item in MW.EndCatchments)
      {
        GetCatchmentsWithObs(item, SortedCatchments);
      }
      foreach (var item in MW.AllCatchments.Values)
      {
        GWCor.Reduction.Add(item.ID, 0);
        IntCor.Reduction.Add(item.ID, 0);
        MainCor.Reduction.Add(item.ID, 0);
      }
      int totaliter = 0;
      foreach (var v in SortedCatchments)
      {
        List<double> Errors = new List<double>();
        double localdamp = DampingFactor;
        double currentreducer = 0;
        double Error = double.MaxValue;
        int itercount = 0;

        var row = dt.Rows.Find(v.ID);

        NewMessage("Calibrating " + v.ID);
        while (Math.Abs(Error) > AbsoluteConvergence & itercount < MaxNoOfIterations)
        {
          v.ObsNitrate = null;
          v.SimNitrate = null;
          double accgws = 0;
          double accs = 0;
          double accsink = 0;
          double accmainsink = 0;
          double obssum = 0;

          CurrentTime = CStart;
          while (CurrentTime < CEnd)
          {
            v.MoveInTime(CurrentTime);
            double obsn =v.Measurements.Nitrate.GetValue(CurrentTime, InterpolationMethods.DeleteValue);
            if (obsn != v.Measurements.Nitrate.DeleteValue)
            {
              obssum += obsn;
              accgws += AccumulateUpstream(GWCor.SourceModelName, v, CurrentTime);
              foreach (var s in MW.InternalReductionModels)
                accsink += AccumulateUpstream(s.Name, v, CurrentTime);
              foreach (var s in MW.SourceModels.Where(ss => ss.Name != GWCor.SourceModelName))
                accs += AccumulateUpstream(s.Name, v, CurrentTime);
              foreach (var s in MW.MainStreamRecutionModels)
                accmainsink += AccumulateUpstream(s.Name, v, CurrentTime);
            }
            CurrentTime = CurrentTime.AddMonths(1);
          }

          double[] sim;
          double[] obs;
          v.ObsNitrate.AlignRemoveDeletevalues(v.SimNitrate, out obs, out sim);
          double simerror = obs.Sum() - sim.Sum();


          Error = (accs + accgws - accsink - accmainsink) - obssum;

          if (itercount == 0 & double.IsNaN(Error))
          {
            NewMessage("Initial error is NAN. Could not calibrate " + v.ID);
            break;
          }


          
          currentreducer = Error / accgws * localdamp;

          Errors.Add(Error);
          NewMessage(Error.ToString());

          if (double.IsNaN(Error) || (itercount > 2 && Math.Abs(Error) > Errors.Skip(itercount - 3).Take(3).Select(e => Math.Abs(e)).Max()))
          {
            SendReducUpstream(v, GWCor.Reduction, currentreducer, "GWRatio", true);
            SendReducUpstream(v, IntCor.Reduction, InternalRatio * currentreducer, "IntRatio", true);
            SendReducUpstream(v, MainCor.Reduction, MainRatio * currentreducer, "MainRatio", true);

            NewMessage("Reduce damping and resetting reducer to first value");
            localdamp *= 0.5;
            currentreducer = Errors.First() / accgws * localdamp;

            Error = 2 * AbsoluteConvergence; //To make sure we do not NAN for testing in the next iteration.
          }

          SendReducUpstream(v, GWCor.Reduction, currentreducer, "GWRatio",false);
          SendReducUpstream(v, IntCor.Reduction, InternalRatio * currentreducer, "IntRatio", false);
          SendReducUpstream(v, MainCor.Reduction, MainRatio * currentreducer, "MainRatio", false);
          itercount++;
        }
        totaliter += itercount;

        row[0] = v.ID;
        row[1] = itercount;
        row[2] = Error;
        row["RedFactor"] = GWCor.Reduction[v.ID];

        NewMessage(v.ID + " calibrated in " + itercount + " iterations. Final error: " + Error + ". ReductionFactor: " + GWCor.Reduction[v.ID]);
      }
      NewMessage("Total number of model calls: " + totaliter);
      var outdir = Path.GetDirectoryName(MW.AlldataFile.FileName);
      GWCor.DebugPrint(outdir, MW.AllCatchments);
      IntCor.DebugPrint(outdir, MW.AllCatchments);
      MainCor.DebugPrint(outdir, MW.AllCatchments);

      using (ShapeWriter sw = new ShapeWriter(Path.Combine(outdir, "CalibrationResult")) { Projection = MainModel.projection })
      {
        for (int i = 0; i < dt.Rows.Count; i++)
        {
          GeoRefData gd = new GeoRefData() { Geometry = MW.AllCatchments[(int)dt.Rows[i][0]].Geometry };
          gd.Data = dt.Rows[i];
          sw.Write(gd);
        }
      }




    }
Beispiel #8
0
    /// <summary>
    /// Writes a polyline shape file with the network
    /// </summary>
    /// <param name="shapefilename"></param>
    public void WriteToShape(string shapefilename)
    {
      using (ShapeWriter swc = new ShapeWriter(shapefilename + "_QHPoints"))
      {
        DataTable dat = new DataTable();
        dat.Columns.Add("BranchName", typeof(string));
        dat.Columns.Add("Chainage", typeof(double));
        dat.Columns.Add("Type", typeof(string));
        foreach (var b in nwkfile.MIKE_11_Network_editor.COMPUTATIONAL_SETUP.branchs)
        {
          foreach (var p in b.points.points)
          {
            GeoRefData gd = new GeoRefData();
            gd.Data = dat.NewRow();
            gd.Data["BranchName"] = b.name;
            gd.Data["Chainage"] = p.Par1;

            if(p.Par3 ==0)
            gd.Data["Type"] = "h";
            else
              gd.Data["Type"] = "q";

            var bran = Branches.FirstOrDefault(br => br.Name == b.name);
            if (bran != null)
            {
              gd.Geometry = bran.GetPointAtChainage(p.Par1);
              swc.Write(gd);
            }
          }
        }
      }

      ShapeWriter sw = new ShapeWriter(shapefilename);


      ShapeWriter swCsc = new ShapeWriter(shapefilename + "_CrossSections");
      DataTable dtCsc = new DataTable();
      dtCsc.Columns.Add("Name", typeof(string));
      dtCsc.Columns.Add("TopoID", typeof(string));
      dtCsc.Columns.Add("Chainage", typeof(double));

      DataTable dt = new DataTable();
      dt.Columns.Add("Name", typeof(string));
      dt.Columns.Add("TopoID", typeof(string));
      dt.Columns.Add("ChainageStart", typeof(double));
      dt.Columns.Add("ChainageEnd", typeof(double));

      foreach (var b in branches)
      {
        GeoRefData grf = new GeoRefData();
        grf.Geometry = b.Line;
        grf.Data=dt.NewRow();
        grf.Data[0] = b.Name;
        grf.Data[1] = b.TopoID;
        grf.Data[2] = b.ChainageStart;
        grf.Data[3] = b.ChainageEnd;
        sw.Write(grf);

        foreach (var Csc in b.CrossSections)
        {
          GeoRefData csc_data = new GeoRefData();
          csc_data.Geometry = Csc.Line;
          csc_data.Data = dtCsc.NewRow();
          csc_data.Data[0] = Csc.BranchName;
          csc_data.Data[1] = Csc.TopoID;
          csc_data.Data[2] = Csc.Chainage;

          swCsc.Write(csc_data);
        }
      }
      sw.Dispose();
      swCsc.Dispose();
    }
    private void SaveAndCalc(string filename)
    {
      using (ShapeWriter sw = new ShapeWriter(filename))
      {
        DataTable dt = new DataTable();
        dt.Columns.Add("Branch", typeof(string));
        dt.Columns.Add("TopoID", typeof(string));
        dt.Columns.Add("Chainage", typeof(double));
        dt.Columns.Add("DataType", typeof(string));
        dt.Columns.Add("Min", typeof(double));
        dt.Columns.Add("Max", typeof(double));
        dt.Columns.Add("Average", typeof(double));
        dt.Columns.Add("MedianMin", typeof(double));


        foreach (var p in res11file.Points)
        {
          double min = double.MaxValue; ;
          double max = double.MinValue;
          double middel =0;
          List<double> yearlymins = new List<double>();
          int CurrentYear = 0;
          for (int i = res11file.GetTimeStep(StartTime); i < res11file.GetTimeStep(EndTime); i++)
          {
            if (CurrentYear != res11file.TimeSteps[i].Year)
            {
              CurrentYear = res11file.TimeSteps[i].Year;
              yearlymins.Add(Double.MaxValue);
            }
            double d = p.GetData(i);
            min = Math.Min(min, d);
            max = Math.Max(max, d);
            middel += d;
            yearlymins[yearlymins.Count - 1] = Math.Min(yearlymins.Last(), d);
          }

          middel /= (res11file.GetTimeStep(EndTime) - res11file.GetTimeStep(StartTime));
          var drow = dt.NewRow();
          drow[0] = p.BranchName;
          drow[1] = p.TopoID;
          drow[2] = p.Chainage;
          drow[3] = p.pointType.ToString();
          drow[4] = min;
          drow[5] = max;
          drow[6] = middel;
          drow[7] = yearlymins.Sum(var => var) / yearlymins.Count;

          GeoRefData grf = new GeoRefData();
          grf.Geometry = p;
          grf.Data = drow;

          sw.Write(grf);
            
        }
      }
    }
    private void Print(Dictionary<int, Catchment> AllCatchments, string FileNameAttach)
    {
      //Get the output coordinate system
      ProjNet.CoordinateSystems.ICoordinateSystem projection;
      using (System.IO.StreamReader sr = new System.IO.StreamReader(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Default.prj")))
      {
        ProjNet.CoordinateSystems.CoordinateSystemFactory cs = new ProjNet.CoordinateSystems.CoordinateSystemFactory();
        projection = cs.CreateFromWkt(sr.ReadToEnd());
      }

      using (ShapeWriter sw = new ShapeWriter(Path.Combine(Path.GetDirectoryName(OutputFile.FileName),Path.GetFileNameWithoutExtension(OutputFile.FileName)+FileNameAttach)) { Projection = projection })
      {
        for (int i = 0; i < ReductionVariables.Rows.Count; i++)
        {
          GeoRefData gd = new GeoRefData() { Data = ReductionVariables.Rows[i], Geometry = AllCatchments[(int)ReductionVariables.Rows[i]["ID"]].Geometry };
          sw.Write(gd);
        }
      }
    }
    public void DebugPrint(string Directory)
    {
      //We need to process data for extra output while we have the particles
      {
        foreach (var c in Catchments.Where(ca => ca.EndParticles.Count >= 1))
        {
          if (c.EndParticles.Count >= 20)
          {
            c.ParticleBreakthroughCurves = new List<Tuple<double, double>>();
            MathNet.Numerics.Statistics.Percentile p = new MathNet.Numerics.Statistics.Percentile(c.EndParticles.Select(pa => pa.TravelTime));
            for (int i = 1; i < np; i++)
            {
              c.ParticleBreakthroughCurves.Add(new Tuple<double, double>(i / np * 100.0, p.Compute(i / np)));
            }
            //Also do oxidized breakthrough curves
            if (c.EndParticles.Count(pp => pp.Registration != 1) >= 20)
            {
              c.ParticleBreakthroughCurvesOxidized = new List<Tuple<double, double>>();
              p = new MathNet.Numerics.Statistics.Percentile(c.EndParticles.Where(pp => pp.Registration != 1).Select(pa => pa.TravelTime));
              for (int i = 1; i < np; i++)
              {
                c.ParticleBreakthroughCurvesOxidized.Add(new Tuple<double, double>(i / np * 100.0, p.Compute(i / np)));
              }
            }

          }
          DataRow row = DebugData.Rows.Find(c.ID);
          if (row == null)
          {
            row = DebugData.NewRow();
            row[0] = c.ID;
            DebugData.Rows.Add(row);
          }

          row["PartCount"] = c.EndParticles.Count;
          row["RedoxCount"] = c.EndParticles.Count(pp => pp.Registration == 1);
          row["RedoxRatio"] = c.EndParticles.Count(pp => pp.Registration == 1) / (double)c.EndParticles.Count;
          if (c.EndParticles.Count > 0)
          {
            row["Drain_to_River"] = c.EndParticles.Count(pa => pa.SinkType == SinkType.Drain_to_River) / (double)c.EndParticles.Count;
            row["Drain_to_Boundary"] = c.EndParticles.Count(pa => pa.SinkType == SinkType.Drain_to_Boundary) / (double)c.EndParticles.Count;
            row["Unsaturated_zone"] = c.EndParticles.Count(pa => pa.SinkType == SinkType.Unsaturated_zone) / (double)c.EndParticles.Count;
            row["River"] = c.EndParticles.Count(pa => pa.SinkType == SinkType.River) / (double)c.EndParticles.Count;
          }
          row["PartCount_start"] = c.StartParticles.Count;
        }
      }




      NewMessage("Writing breakthrough curves");

      var selectedCatchments = Catchments.Where(cc => cc.ParticleBreakthroughCurves != null);

      using (System.IO.StreamWriter sw = new System.IO.StreamWriter(Path.Combine(Directory, "BC.csv")))
      {
        StringBuilder headline = new StringBuilder();
        headline.Append("ID\tNumber of Particles");

        for (int i = 1; i < np; i++)
        {
          headline.Append("\t + " + (i / np * 100.0));
        }
        sw.WriteLine(headline);

        foreach (var c in selectedCatchments.Where(cc => cc.ParticleBreakthroughCurves != null))
        {
          StringBuilder line = new StringBuilder();
          line.Append(c.ID + "\t" + c.EndParticles.Count);
          foreach (var pe in c.ParticleBreakthroughCurves)
          {
            line.Append("\t" + pe.Item2);
          }
          sw.WriteLine(line);
        }
      }

      if (selectedCatchments.Count() > 0)
      {
        using (ShapeWriter sw = new ShapeWriter(Path.Combine(Directory, Name + "_debug.shp")))
        {
          foreach (var bc in selectedCatchments.First().ParticleBreakthroughCurves)
          {
            DebugData.Columns.Add(((int)bc.Item1).ToString(), typeof(double));
          }
          foreach (var bc in selectedCatchments.First().ParticleBreakthroughCurves)
          {
            DebugData.Columns.Add(((int)bc.Item1).ToString() + "Ox", typeof(double));
          }

          foreach (var c in selectedCatchments)
          {
            GeoRefData gd = new GeoRefData() { Geometry = c.Geometry };
            var row = DebugData.Rows.Find(c.ID);

            if (c.ParticleBreakthroughCurves != null)
              foreach (var bc in c.ParticleBreakthroughCurves)
                row[((int)bc.Item1).ToString()] = bc.Item2;

            if (c.ParticleBreakthroughCurvesOxidized != null)
              foreach (var bc in c.ParticleBreakthroughCurvesOxidized)
                row[((int)bc.Item1).ToString() + "Ox"] = bc.Item2;

            gd.Data = row;
            sw.Write(gd);
          }
        }
      }

      //selectedCatchments = Catchments.Where(cc => cc.EndParticles.Count > 0).ToList();

      //foreach (var c in selectedCatchments)
      //{
      //  DataTable dt = new DataTable();
      //  dt.Columns.Add("Part_Id", typeof(int));
      //  dt.Columns.Add("Sink", typeof(string));
      //  dt.Columns.Add("Reg", typeof(int));

      //  using (ShapeWriter sw = new ShapeWriter(Path.Combine(Directory, c.ID + "_particles.shp")) { Projection = MainModel.projection })
      //  {
      //    foreach (var p in c.EndParticles)
      //    {
      //      var row = dt.NewRow();
      //      row["Part_Id"] = p.ID;
      //      row["Sink"] = p.SinkType.ToString();
      //      row["Reg"] = p.Registration;
      //      sw.Write(new GeoRefData() { Geometry = new XYLine(p.XStart, p.YStart, p.X, p.Y), Data = row });

      //    }
      //  }
      //}
    }