APSIMInitiative / APSIM710

APSIM
https://www.apsim.info
30 stars 47 forks source link

Crop Rotations in APSIM - Video #2039

Open sarahcleary opened 3 years ago

sarahcleary commented 3 years ago

Please use this issue to comment and/or ask questions in relation to the following videos: https://www.youtube.com/watch?v=W7IqQMQi8GQ https://www.youtube.com/watch?v=DX0xFo1SW4Q More information can be found here: https://www.apsim.info/support/apsim-training-manuals/crop-rotations-in-apsim/

If you're interested in receiving updates on new videos developed by the APSIM team, please email apsim@csiro.au

BrianCollinss commented 3 years ago

I appreicate all your efforts, specially Peter's. I have a general question. What is the difference between using a Rotation Manager and play with dates in a manager script (usually a Manager2) to plant/harvest crops in a spceific order? What benefits are there in using a Rotation Manager compared with the other approach?

peter-devoil commented 3 years ago

Hey Brian - "Less is More". If you can build a simulation with fewer components, then that's a good thing. The benefit from describing a rotation comes when somebody wants to extend your cropping system into a (eg flexible, opportunistic) more complicated one.

nishantsinha5 commented 3 years ago

Hello Peter Greeting Nice lecture...and good initiative from Team APSIM. Can you suggest to me how to simulate No-tillage with residue retention in APSIM...

peter-devoil commented 3 years ago

Hi Nishant, glad to hear of your interest.

By default apsim does nothing unless you tell it to, so if you don't specify a tillage management, it won't happen - thus no-till is the default behaviour, and your job is done.. :)

Seriously, there are many aspects to consider here. One of the reasons we use the "Reset XYZ on sowing" component is to avoid dealing with SOM pools, which can accumulate to obscene levels if left unattended - I once encountered an embarassing simulation with 60t of accumulated surface residues after only a few years. If you are simulating a continuous SOM pool, you must manage it by tilling (eg at sowing) to incorporate the residues - there are manager components in the toolbox that do this.

If your concern is the economics of weed management, you need to describe when the model should spray - here's a simple hydro-thermal time model attached that I've used in the past that simply models weed germination, and counts the number of consequent weeding events.

If the system you're working with doesnt immediately kill weeds, you can sow a weed module that consumes resources - the sample "Continuous maize and weeds" demonstrates the detail needed to implement the weed and crop components as intercrops.

In the same space, there is also a facility to model surface residues as two pools - one standing, the other lying on the surface - the standing pool doesnt contribute to the surface cover and thus doesnt affect runoff / infiltration calculation. There's a sample in the test dataset.

HTH

nishantsinha5 commented 3 years ago

Thank you Peter

With Regards

Dr Nishant K. Sinha

Scientist (Agricultural Physics)

ICAR-Indian Institute of Soil Science

*Email: @. @.>*

On Fri, May 21, 2021 at 5:47 AM Peter de Voil @.***> wrote:

Hi Nishant, glad to hear of your interest.

By default apsim does nothing unless you tell it to, so if you don't specify a tillage management, it won't happen - thus no-till is the default behaviour, and your job is done.. :)

Seriously, there are many aspects to consider here. One of the reasons we use the "Reset XYZ on sowing" component is to avoid dealing with SOM pools, which can accumulate to obscene levels if left unattended - I once encountered an embarassing simulation with 60t of accumulated surface residues after only a few years. If you are simulating a continuous SOM pool, you must manage it by tilling (eg at sowing) to incorporate the residues - there are manager components in the toolbox that do this.

If your concern is the economics of weed management, you need to describe when the model should spray - here's https://github.com/APSIMInitiative/APSIMClassic/files/6519273/weeds.zip a simple hydro-thermal time model attached that I've used in the past that simply models weed germination, and counts the number of consequent weeding events.

If the system you're working with doesnt immediately kill weeds, you can sow a weed module that consumes resources - the sample "Continuous maize and weeds" demonstrates the detail needed to implement the weed and crop components as intercrops.

In the same space, there is also a facility to model surface residues as two pools - one standing, the other lying on the surface - the standing pool doesnt contribute to the surface cover and thus doesnt affect runoff / infiltration calculation. There's a sample in the test dataset https://github.com/APSIMInitiative/APSIMClassic/blob/master/Tests/SurfaceOM/SOM_Test.apsim .

HTH

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/APSIMInitiative/APSIMClassic/issues/2039#issuecomment-845564817, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABQUTR5WA27FMOJEAECCSZ3TOWRCDANCNFSM43JKZVZQ .

JJguri commented 3 years ago

I appreicate all your efforts, specially Peter's. I have a general question. What is the difference between using a Rotation Manager and play with dates in a manager script (usually a Manager2) to plant/harvest crops in a spceific order? What benefits are there in using a Rotation Manager compared with the other approach?

Hi @BrianCollinss could you please share an example of rotations using a Manager2 instead of the RotationManager? Thanks

JJguri commented 3 years ago

@peter-devoil can we add fixed date N fertilisation for specific years as a part of the Rotation Management?

peter-devoil commented 3 years ago

Of course.

You can use fixed dates, hook into crop sowing events, or even an operations schedule.

JJguri commented 3 years ago

@peter-devoil I have an issue when I want to create the rotation maize-wheat as double crop, i.e. both crops in the same year, wheat as cover crop and maize in summer. I am running a simulation for 6 years (1998-2003) which should have 3 wheat harvests and 3 maize harvests, instead I am getting 2 harvest for each crop (maize in 1998 and 2001 and wheat in 2000 and 2003). The issue is that the rotation manager is considering the fallow too. I need to sow the wheat right after the maize with no fallow (or reduced fallow). Here is the rotation manager: image

jbrider commented 3 years ago

You need to check your exit logic out of your fallows. It isn't exiting from there until the following year.

JJguri commented 3 years ago

I cannot figure out the issue yet. here is the sim (second sim MWMWMW) in the tree: v3.zip

peter-devoil commented 3 years ago
  1. set "must sow" to yes, otherwise you are waiting for soil water & rainfall criteria
  2. check the harvest date of crop A isn't after the sowing window of crop B
JJguri commented 3 years ago

This is working now, however I also found an issue related with the parameter Enter the end date of the pause, what is the exact meaning of this, it is the last day that the fallow can be? For a 3-year rotation, could I setup a harvest rule based on maturity for the first two years and fixed date for the last year?

JJguri commented 3 years ago

@peter-devoil. For a 3-year rotation, could I setup a harvest rule based on maturity for the first two years and fixed date for the last year?

peter-devoil commented 3 years ago

Of course. For the 1st two, use the "CanLeave" rule in the crop manager. For the last, a bit of logic that returns 1 on the date you want to harvest, 0 otherwise.

JJguri commented 3 years ago

@peter-devoil. I created the following updated code and a new property (harvest date = date4 in the code) for maize management but it provides an error in L61. Could you please have a look?

image

using System;
using ModelFramework;
using CSGeneral;

// Basic crop management: sowing & harvesting.
// Multi-paddock aware.
// This component DOES NOT require a trigger from the sequencer.
// It will assume it is the only crop in the system if it doesnt find a sequencer.
// If it does find a sequencer, it will do nothing until told to (via Enter/Leave).
public class Script 
{      
   [Link()]  public Simulation MySimulation;

   [Param()] private string crop;         // The module name of this crop
   [Param()] private string date1;         //Start of sowing window
   [Param()] private string date2;         //End of sowing window
   [Param()] private string date3;         //date to change to different cultivar
   [Param()] private string date4;         //date to harvest the 3rd summer crop
   [Param()] int esw_amount;
   [Param()] private string must_sow;

   [Input()] private DateTime today;

   // Rainfall accumulator
   [Param()] int rain_days;              //check for rain over this period
   [Param()] int rain_amount;            //this much rain over that period

   // Daily rainfall from the system
   [Input] private double rain;
   private ManagerUtility.Tracker<double> rainTracker; 

   private bool inWindow = false;
   private bool endOfWindow = false;
   private bool ChangeCultivar = false;

   //initialise tracker, telling it how many days to track
   [EventHandler] public void OnInitialised()
   {
      rainTracker = new ManagerUtility.Tracker<double>(rain_days); 
   }

   // Daily tests common to all paddocks
   [EventHandler] public void OnPrepare()
   {
      bool startOfWindow = DateUtility.DatesEqual(date1, today);

      inWindow = DateUtility.WithinDates(date1, today, date2);
      ChangeCultivar = false;
      if(date3 != "na")  
         ChangeCultivar = DateUtility.WithinDates(date3, today, date2);
      endOfWindow = DateUtility.DatesEqual(date2, today);

      rainTracker.Add(rain);
      string currentPaddock = "";
      if (MySimulation.Get("currentPaddock", out currentPaddock) == false) 
      {
         // If there is no sequencer plugged in then we are it..
         if (canEnter > 0) {
            OnEnter();
         }
         if (canLeave > 0) or (today == date4) {
            OnLeave();
         }
      }
   }
   // Test whether we can sow a crop today
   // +ve number - yes
   // 0          - no
   // -ve number - no, out of scope (planting window)
   [Output, Description("Test whether we can sow a crop today")] public int canEnter  {
      get {
         bool isPossibleToday = false;

         string currentPaddock;
         MySimulation.Get("currentPaddock", out currentPaddock);
         //Console.WriteLine("1. '" + currentPaddock + "'");

         Component paddockModule;
         if (currentPaddock != "")
            paddockModule = (Component) MySimulation.LinkByName(currentPaddock);
         else
            paddockModule = (Component) MySimulation.ChildPaddocks[0];
         //Console.WriteLine("2. " + paddockModule.Name);

         Component cropModule = (Component) paddockModule.LinkByName( crop );

         //Console.WriteLine("3. " + cropModule.Name);
         string plantStatus = "";
         cropModule.Get("plant_status", out plantStatus);

         double esw = 0.0;
         Component soilModule = (Component) paddockModule.LinkByType("SoilWat");
         soilModule.Get("esw", out esw);
         if (plantStatus == "out" &&
             inWindow &&
             rainTracker.Sum() >= rain_amount &&
             esw > esw_amount) 
         {
             isPossibleToday = true;
         } 

         if (isPossibleToday)
            return 1;

         if (plantStatus == "out" && endOfWindow && must_sow == "yes")
            return 1;

         if (plantStatus == "out" && !inWindow)
            return -1;

         return 0;
      }
   }  
   [Output, Description("Test whether we have passed the end of the window")] public int pastWindow  {
      get {
         return( (DateUtility.CompareDates(date2, today) < 0) ? 1 : 0);
      }
   }

   // Sow a crop
   [Param()] private string cultivar1;
   [Param()] private string cultivar2;
   [Param()] private double density1;
   [Param()] private double depth1;
   [Param()] private double row_spacing1;
   [Param()] private string ftn1;
   [Param()] private string skiprow1;
   [Param()] private string tillageImplement;

   [EventHandler, Description("Sow the crop")] public void OnEnter()
   {
      Console.WriteLine(today + " Sowing Crop");
      SowType data = new SowType();
      data.Cultivar = cultivar1;
      if(ChangeCultivar)
         data.Cultivar = cultivar2;

      data.plants = density1;
      data.sowing_depth = depth1;
      data.row_spacing = row_spacing1;
      data.tiller_no_fertile = (ftn1 == "na") ? "" : ftn1;
      data.SkipRow = 0 ;
      if (skiprow1 == "single")
         data.SkipRow = 1;
      else if (skiprow1 == "double")
         data.SkipRow = 2;

      string currentPaddock;
      MySimulation.Get("currentPaddock", out currentPaddock);
      Component cropModule;
      if (currentPaddock != "")
         cropModule = (Component) MySimulation.LinkByName(currentPaddock + "." + crop);
      else 
         cropModule = (Component) MySimulation.ChildPaddocks[0].LinkByName(crop);

      cropModule.Publish("Sow", data);
      if (tillageImplement.ToLower() != "na") 
      {
         TillageType t = new TillageType();
         t.type = tillageImplement;
         Component paddockModule;
         if (currentPaddock != "")
            paddockModule = (Component) MySimulation.LinkByName(currentPaddock);
         else
            paddockModule = MySimulation.ChildPaddocks[0];
         paddockModule.Publish("tillage", t);
      }      
   }

   // Test whether we can harvest a crop today
   // +ve number - yes
   // 0          - no
   // -ve        - out of scope
   [Output] public int canLeave  
   {
      get 
      {
         string currentPaddock;
         MySimulation.Get("currentPaddock", out currentPaddock);

         string plantStatus = "";
         MySimulation.Get((currentPaddock != "" ? currentPaddock + "." : "") + crop + ".plant_status", out plantStatus);
         if (plantStatus == "out")
            return -1;

         string StageName = "";
         MySimulation.Get((currentPaddock != "" ? currentPaddock + "." : "") + crop + ".StageName", out StageName);
         if (StageName == "harvest_ripe" || plantStatus == "dead")
            return 1;
         return 0;
      }
   }

   [EventHandler] public void OnLeave()
   {
      Console.WriteLine(today + " Harvesting Crop");
      HarvestType hdata = new HarvestType();
      hdata.Remove = 0.0;
      string currentPaddock;
      MySimulation.Get("currentPaddock", out currentPaddock);
      Component cropModule;
      if (currentPaddock != "")
         cropModule = (Component) MySimulation.LinkByName(currentPaddock + "." + crop);
      else 
         cropModule = (Component) MySimulation.ChildPaddocks[0].LinkByName(crop);

      cropModule.Publish("harvest", hdata);

      KillCropType kdata = new KillCropType();
      kdata.KillFraction = 0.0F;
      cropModule.Publish("killcrop", kdata);
      cropModule.Publish("end_crop");
   } 
}

here the error: image

JJguri commented 3 years ago

Of course. For the 1st two, use the "CanLeave" rule in the crop manager. For the last, a bit of logic that returns 1 on the date you want to harvest, 0 otherwise.

@peter-devoil. Were are the harvest rules specified in the crop manager? I could not see this code in my example provided below?...

peter-devoil commented 3 years ago

It's not python :) Try:

         if (canLeave > 0  || today == date4) {

The harvest rules come in 2 parts: a test (canLeave) and an action (Leave). The rotation manager gets the test, and if satisfied, calls the action. The code you want for fixed sowing date is very similar to the pastWindow function you have there. The test would be "pastWindow" (or whatever you call it), the action would be "Leave".

JJguri commented 3 years ago

Using this

if (canLeave > 0  || today == date4) {

I got this error. I think we need to change the way to call date4. date4 is ddmmyyyy format

     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                      APSIM  Fatal  Error
                      -------------------
     Operator '==' cannot be applied to operands of type 'System.DateTime' and 'string'. Line number: 61
     Component name: Maize Management

     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
peter-devoil commented 3 years ago

Try if (canLeave > 0 || DateUtility.CompareDates(date4, today))

JJguri commented 3 years ago

if (canLeave > 0 || DateUtility.CompareDates(date4, today))

------- Maize Management Initialisation ---------------------------------------
     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                      APSIM  Fatal  Error
                      -------------------
     Operator '||' cannot be applied to operands of type 'bool' and 'int'. Line number: 61
     Component name: Maize Management

     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
peter-devoil commented 3 years ago

Argh. Try 2: if (canLeave > 0 || (DateUtility.CompareDates(date4, today) == 0)

JJguri commented 3 years ago

it works now but still the crop is not harvested at the indicated date (15/09/2002). Maybe we need to add an extra code in the "// Test whether we can harvest a crop today" section? image

JJguri commented 3 years ago

this code works!

using System;
using ModelFramework;
using CSGeneral;

// Basic crop management: sowing & harvesting.
// Multi-paddock aware.
// This component DOES NOT require a trigger from the sequencer.
// It will assume it is the only crop in the system if it doesnt find a sequencer.
// If it does find a sequencer, it will do nothing until told to (via Enter/Leave).
public class Script 
{      
   [Link()]  public Simulation MySimulation;

   [Param()] private string crop;         // The module name of this crop
   [Param()] private string date1;         //Start of sowing window
   [Param()] private string date2;         //End of sowing window
   [Param()] private string date3;         //date to change to different cultivar
   [Param] private DateTime date4;         //date to harvest the 3rd summer crop
   [Param()] int esw_amount;
   [Param()] private string must_sow;

   [Input()] private DateTime today;

   // Rainfall accumulator
   [Param()] int rain_days;              //check for rain over this period
   [Param()] int rain_amount;            //this much rain over that period

   // Daily rainfall from the system
   [Input] private double rain;
   private ManagerUtility.Tracker<double> rainTracker; 

   private bool inWindow = false;
   private bool endOfWindow = false;
   private bool ChangeCultivar = false;

   //initialise tracker, telling it how many days to track
   [EventHandler] public void OnInitialised()
   {
      rainTracker = new ManagerUtility.Tracker<double>(rain_days); 
   }

   // Daily tests common to all paddocks
   [EventHandler] public void OnPrepare()
   {
      bool startOfWindow = DateUtility.DatesEqual(date1, today);

      inWindow = DateUtility.WithinDates(date1, today, date2);
      ChangeCultivar = false;
      if(date3 != "na")  
         ChangeCultivar = DateUtility.WithinDates(date3, today, date2);
      endOfWindow = DateUtility.DatesEqual(date2, today);

      rainTracker.Add(rain);
      string currentPaddock = "";
      if (MySimulation.Get("currentPaddock", out currentPaddock) == false) 
      {
         // If there is no sequencer plugged in then we are it..
         if (canEnter > 0) {
            OnEnter();
         }
         if (canLeave > 0) {
            OnLeave();
         }
      }
   }
   // Test whether we can sow a crop today
   // +ve number - yes
   // 0          - no
   // -ve number - no, out of scope (planting window)
   [Output, Description("Test whether we can sow a crop today")] public int canEnter  {
      get {
         bool isPossibleToday = false;

         string currentPaddock;
         MySimulation.Get("currentPaddock", out currentPaddock);
         //Console.WriteLine("1. '" + currentPaddock + "'");

         Component paddockModule;
         if (currentPaddock != "")
            paddockModule = (Component) MySimulation.LinkByName(currentPaddock);
         else
            paddockModule = (Component) MySimulation.ChildPaddocks[0];
         //Console.WriteLine("2. " + paddockModule.Name);

         Component cropModule = (Component) paddockModule.LinkByName( crop );

         //Console.WriteLine("3. " + cropModule.Name);
         string plantStatus = "";
         cropModule.Get("plant_status", out plantStatus);

         double esw = 0.0;
         Component soilModule = (Component) paddockModule.LinkByType("SoilWat");
         soilModule.Get("esw", out esw);
         if (plantStatus == "out" &&
             inWindow &&
             rainTracker.Sum() >= rain_amount &&
             esw > esw_amount) 
         {
             isPossibleToday = true;
         } 

         if (isPossibleToday)
            return 1;

         if (plantStatus == "out" && endOfWindow && must_sow == "yes")
            return 1;

         if (plantStatus == "out" && !inWindow)
            return -1;

         return 0;
      }
   }  
   [Output, Description("Test whether we have passed the end of the window")] public int pastWindow  {
      get {
         return( (DateUtility.CompareDates(date2, today) < 0) ? 1 : 0);
      }
   }

   // Sow a crop
   [Param()] private string cultivar1;
   [Param()] private string cultivar2;
   [Param()] private double density1;
   [Param()] private double depth1;
   [Param()] private double row_spacing1;
   [Param()] private string ftn1;
   [Param()] private string skiprow1;
   [Param()] private string tillageImplement;

   [EventHandler, Description("Sow the crop")] public void OnEnter()
   {
      Console.WriteLine(today + " Sowing Crop");
      SowType data = new SowType();
      data.Cultivar = cultivar1;
      if(ChangeCultivar)
         data.Cultivar = cultivar2;

      data.plants = density1;
      data.sowing_depth = depth1;
      data.row_spacing = row_spacing1;
      data.tiller_no_fertile = (ftn1 == "na") ? "" : ftn1;
      data.SkipRow = 0 ;
      if (skiprow1 == "single")
         data.SkipRow = 1;
      else if (skiprow1 == "double")
         data.SkipRow = 2;

      string currentPaddock;
      MySimulation.Get("currentPaddock", out currentPaddock);
      Component cropModule;
      if (currentPaddock != "")
         cropModule = (Component) MySimulation.LinkByName(currentPaddock + "." + crop);
      else 
         cropModule = (Component) MySimulation.ChildPaddocks[0].LinkByName(crop);

      cropModule.Publish("Sow", data);
      if (tillageImplement.ToLower() != "na") 
      {
         TillageType t = new TillageType();
         t.type = tillageImplement;
         Component paddockModule;
         if (currentPaddock != "")
            paddockModule = (Component) MySimulation.LinkByName(currentPaddock);
         else
            paddockModule = MySimulation.ChildPaddocks[0];
         paddockModule.Publish("tillage", t);
      }      
   }

   // Test whether we can harvest a crop today
   // +ve number - yes
   // 0          - no
   // -ve        - out of scope
   [Output] public int canLeave  
   {
      get 
      {
         string currentPaddock;
         MySimulation.Get("currentPaddock", out currentPaddock);

         string plantStatus = "";
         MySimulation.Get((currentPaddock != "" ? currentPaddock + "." : "") + crop + ".plant_status", out plantStatus);
         if (plantStatus == "out")
            return -1;

         string StageName = "";
         MySimulation.Get((currentPaddock != "" ? currentPaddock + "." : "") + crop + ".StageName", out StageName);
         if (StageName == "harvest_ripe" || plantStatus == "dead" || today == date4)
            return 1;
         return 0;
      }
   }

   [EventHandler] public void OnLeave()
   {
      Console.WriteLine(today + " Harvesting Crop");
      HarvestType hdata = new HarvestType();
      hdata.Remove = 0.0;
      string currentPaddock;
      MySimulation.Get("currentPaddock", out currentPaddock);
      Component cropModule;
      if (currentPaddock != "")
         cropModule = (Component) MySimulation.LinkByName(currentPaddock + "." + crop);
      else 
         cropModule = (Component) MySimulation.ChildPaddocks[0].LinkByName(crop);

      cropModule.Publish("harvest", hdata);

      KillCropType kdata = new KillCropType();
      kdata.KillFraction = 0.0F;
      cropModule.Publish("killcrop", kdata);
      cropModule.Publish("end_crop");
   } 
}
JJguri commented 3 years ago

@peter-devoil I got the following error for a rotation simulation. Do you know which could be the source of it?

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                      APSIM  Fatal  Error
                      -------------------
     paddock.Soybean Management The property canEnter is not a gettable property. (Exception has been thrown by the target of an invocation.)
      Error in readFromPropertyList(). Property: 9
     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                      APSIM  Fatal  Error
                      -------------------
     std::exception
     Component name: paddock
     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
peter-devoil commented 3 years ago

There was an exception whenthe "canEnter" code was called. Bad date? incorrect crop module? It will be somewhere in the parameters for the soybean manager

JJguri commented 3 years ago

@peter-devoil I have an issue with date formatting in a rotation simulation. I setup a date (date4) to fix the wheat harvest as fixed date following this code:

using System;
using ModelFramework;
using CSGeneral;

// Basic crop management: sowing & harvesting.
// Multi-paddock aware.
// This component DOES NOT require a trigger from the sequencer.
// It will assume it is the only crop in the system if it doesnt find a sequencer.
// If it does find a sequencer, it will do nothing until told to (via Enter/Leave).
public class Script 
{      
   [Link()]  public Simulation MySimulation;

   [Param()] private string crop;         // The module name of this crop
   [Param()] private string date1;         //Start of sowing window
   [Param()] private string date2;         //End of sowing window
   [Param()] private string date3;         //date to change to different cultivar
   [Param] private DateTime date4;         //date to harvest the last crop of the rotation
   [Param()] int esw_amount;
   [Param()] private string must_sow;

   [Input()] private DateTime today;

   // Rainfall accumulator
   [Param()] int rain_days;              //check for rain over this period
   [Param()] int rain_amount;            //this much rain over that period

   // Daily rainfall from the system
   [Input] private double rain;
   private ManagerUtility.Tracker<double> rainTracker; 

   private bool inWindow = false;
   private bool endOfWindow = false;
   private bool ChangeCultivar = false;

   //initialise tracker, telling it how many days to track
   [EventHandler] public void OnInitialised()
   {
      rainTracker = new ManagerUtility.Tracker<double>(rain_days); 
   }

   // Daily tests common to all paddocks
   [EventHandler] public void OnPrepare()
   {
      bool startOfWindow = DateUtility.DatesEqual(date1, today);

      inWindow = DateUtility.WithinDates(date1, today, date2);
      ChangeCultivar = false;
      if(date3 != "na")  
         ChangeCultivar = DateUtility.WithinDates(date3, today, date2);
      endOfWindow = DateUtility.DatesEqual(date2, today);

      rainTracker.Add(rain);
      string currentPaddock = "";
      if (MySimulation.Get("currentPaddock", out currentPaddock) == false) 
      {
         // If there is no sequencer plugged in then we are it..
         if (canEnter > 0) {
            OnEnter();
         }
         if (canLeave > 0) {
            OnLeave();
         }
      }
   }
   // Test whether we can sow a crop today
   // +ve number - yes
   // 0          - no
   // -ve number - no, out of scope (planting window)
   [Output, Description("Test whether we can sow a crop today")] public int canEnter  {
      get {
         bool isPossibleToday = false;

         string currentPaddock;
         MySimulation.Get("currentPaddock", out currentPaddock);
         //Console.WriteLine("1. '" + currentPaddock + "'");

         Component paddockModule;
         if (currentPaddock != "")
            paddockModule = (Component) MySimulation.LinkByName(currentPaddock);
         else
            paddockModule = (Component) MySimulation.ChildPaddocks[0];
         //Console.WriteLine("2. " + paddockModule.Name);

         Component cropModule = (Component) paddockModule.LinkByName( crop );

         //Console.WriteLine("3. " + cropModule.Name);
         string plantStatus = "";
         cropModule.Get("plant_status", out plantStatus);

         double esw = 0.0;
         Component soilModule = (Component) paddockModule.LinkByType("SoilWat");
         soilModule.Get("esw", out esw);
         if (plantStatus == "out" &&
             inWindow &&
             rainTracker.Sum() >= rain_amount &&
             esw > esw_amount) 
         {
             isPossibleToday = true;
         } 

         if (isPossibleToday)
            return 1;

         if (plantStatus == "out" && endOfWindow && must_sow == "yes")
            return 1;

         if (plantStatus == "out" && !inWindow)
            return -1;

         return 0;
      }
   }  
   [Output, Description("Test whether we have passed the end of the window")] public int pastWindow  {
      get {
         return( (DateUtility.CompareDates(date2, today) < 0) ? 1 : 0);
      }
   }

   // Sow a crop
   [Param()] private string cultivar;
   [Param()] private double density;
   [Param()] private double depth;
   [Param()] private double row_spacing;
   [Param()] private string ftn;
   [Param()] private string skiprow;
   [Param()] private string tillageImplement;

   [EventHandler, Description("Sow the crop")] public void OnEnter()
   {
      Console.WriteLine(today + " Sowing Crop");
      SowType data = new SowType();
      data.Cultivar = cultivar;
      if(ChangeCultivar)
         data.Cultivar = cultivar;

      data.plants = density;
      data.sowing_depth = depth;
      data.row_spacing = row_spacing;
      data.tiller_no_fertile = (ftn == "na") ? "" : ftn;
      data.SkipRow = 0 ;
      if (skiprow == "single")
         data.SkipRow = 1;
      else if (skiprow == "double")
         data.SkipRow = 2;

      string currentPaddock;
      MySimulation.Get("currentPaddock", out currentPaddock);
      Component cropModule;
      if (currentPaddock != "")
         cropModule = (Component) MySimulation.LinkByName(currentPaddock + "." + crop);
      else 
         cropModule = (Component) MySimulation.ChildPaddocks[0].LinkByName(crop);

      cropModule.Publish("Sow", data);
      if (tillageImplement.ToLower() != "na") 
      {
         TillageType t = new TillageType();
         t.type = tillageImplement;
         Component paddockModule;
         if (currentPaddock != "")
            paddockModule = (Component) MySimulation.LinkByName(currentPaddock);
         else
            paddockModule = MySimulation.ChildPaddocks[0];
         paddockModule.Publish("tillage", t);
      }      
   }

   // Test whether we can harvest a crop today
   // +ve number - yes
   // 0          - no
   // -ve        - out of scope
   [Output] public int canLeave  
   {
      get 
      {
         string currentPaddock;
         MySimulation.Get("currentPaddock", out currentPaddock);

         string plantStatus = "";
         MySimulation.Get((currentPaddock != "" ? currentPaddock + "." : "") + crop + ".plant_status", out plantStatus);
         if (plantStatus == "out")
            return -1;

         string StageName = "";
         MySimulation.Get((currentPaddock != "" ? currentPaddock + "." : "") + crop + ".StageName", out StageName);
         if (StageName == "harvest_ripe" || plantStatus == "dead" || today == date4)
            return 1;
         return 0;
      }
   }

   [EventHandler] public void OnLeave()
   {
      Console.WriteLine(today + " Harvesting Crop");
      HarvestType hdata = new HarvestType();
      hdata.Remove = 0.0;
      string currentPaddock;
      MySimulation.Get("currentPaddock", out currentPaddock);
      Component cropModule;
      if (currentPaddock != "")
         cropModule = (Component) MySimulation.LinkByName(currentPaddock + "." + crop);
      else 
         cropModule = (Component) MySimulation.ChildPaddocks[0].LinkByName(crop);

      cropModule.Publish("harvest", hdata);

      KillCropType kdata = new KillCropType();
      kdata.KillFraction = 0.0F;
      cropModule.Publish("killcrop", kdata);
      cropModule.Publish("end_crop");
   } 
}

The date of harvest is retrieved automaticly from our FluroSense platform and should be 1-May-2020 (which is in the format yyyy-mm-dd), however it is harvesting the crop on 5-Jan-2020 (see below). Although in the UI it seems to be the right date:

image

it is harvesting the crop in the wrong one (5-Jan-2020).

image

Then I checked the clock format in the same sum file and it considers the rigth date format (dd-mm-yyyy). Could you help me with this?

image

peter-devoil commented 3 years ago

Try:

By default, the .net runtime thinks you're in the center of the universe - en-US.

JJguri commented 3 years ago

@peter-devoil I changed date4 to string ([Param()] private string date4; //date to harvest the last crop of the rotation) and added

today == DateTime.ParseExact(date4, "dd/MM/yyyy", CultureInfo.InvariantCulture))

in the harvest rule but I got this error: image

JJguri commented 3 years ago

@peter-devoil I solved it adding using System.Globalization; at the top of the script

peter-devoil commented 3 years ago

Excellent.