1212import logging
1313
1414from sqlalchemy import MetaData , select , Table , and_
15- # from tabulate import tabulate
15+ from tabulate import tabulate
1616import numpy as np
1717import yaml
1818
@@ -226,11 +226,16 @@ class AgroManagementDataProvider(list):
226226 :param campaign_year: Integer campaign year, maps to the YEAR column in the table.
227227 The campaign year usually refers to the year of the harvest. Thus for crops
228228 crossing calendar years, the start_date can be in the previous year.
229-
230- Note that by default the campaign_start_date is set equal to the crop_start_date which
231- means that the simulation starts when the crop starts. In some cases this is undesirable
232- and an earlier start date should be used. In that case use the `set_campaign_start_date(date)`
233- to update the campaign_start_date.
229+ :keyword campaign_start: Optional keyword that can be used to define the start of the
230+ campaign. Note that by default the campaign_start_date is set equal to the
231+ crop_start_date which means that the simulation starts when the crop starts.
232+ This default behaviour can be changed using this keyword. It can have multiple meanings:
233+ - if a date object is passed, the campaign is assumed to start on this date.
234+ - if an int/float is passed, the campaign_start_date is calculated as the
235+ crop_start_date minus the number of days provided by campaign_start.
236+
237+ For adjusting the campaign_start_Date, see also the `set_campaign_start_date(date)` method
238+ to update the campaign_start_date on an existing AgroManagementDataProvider.
234239 """
235240 agro_management_template = """
236241 - {campaign_start_date}:
@@ -240,13 +245,13 @@ class AgroManagementDataProvider(list):
240245 crop_start_type: {crop_start_type}
241246 crop_end_date: {crop_end_date}
242247 crop_end_type: {crop_end_type}
243- max_duration: {duration }
248+ max_duration: {max_duration }
244249 TimedEvents: null
245250 StateEvents: null
246251 - {campaign_end_date}: null
247252 """
248253
249- def __init__ (self , engine , grid_no , crop_no , campaign_year ):
254+ def __init__ (self , engine , grid_no , crop_no , campaign_year , campaign_start = None ):
250255 list .__init__ (self )
251256 self .grid_no = int (grid_no )
252257 self .crop_no = int (crop_no )
@@ -269,7 +274,6 @@ def __init__(self, engine, grid_no, crop_no, campaign_year):
269274 # Determine the start date/type. Only sowing|emergence is accepted by PCSE/WOFOST
270275 cgms11_start_type = str (row .start_type ).strip ()
271276 self .crop_start_date = check_date (row .start_date )
272- self .campaign_start_date = self .crop_start_date
273277 if cgms11_start_type == "FIXED_SOWING" :
274278 self .crop_start_type = "sowing"
275279 elif cgms11_start_type == "FIXED_EMERGENCE" :
@@ -278,6 +282,25 @@ def __init__(self, engine, grid_no, crop_no, campaign_year):
278282 msg = "Unsupported START_TYPE in CROP_CALENDAR table: %s" % row .start_type
279283 raise exc .PCSEError (msg )
280284
285+ # determine the campaign_start
286+ if campaign_start is None :
287+ self .campaign_start_date = self .crop_start_date
288+ elif isinstance (campaign_start , (int , float )):
289+ ndays = abs (int (campaign_start ))
290+ self .campaign_start_date = self .crop_start_date - datetime .timedelta (days = ndays )
291+ else :
292+ try :
293+ campaign_start = check_date (campaign_start )
294+ if campaign_start <= self .crop_start_date :
295+ self .campaign_start_date = self .crop_start_date
296+ else :
297+ msg = "Date (%s) specified by keyword 'campaign_start' in call to AgroManagementDataProvider " \
298+ "is later then crop_start_date defined in the CGMS database."
299+ raise exc .PCSEError (msg % campaign_start )
300+ except KeyError as e :
301+ msg = "Value (%s) of keyword 'campaign_start' not recognized in call to AgroManagementDataProvider."
302+ raise exc .PCSEError (msg % campaign_start )
303+
281304 # Determine crop end date/type and the end of the campaign
282305 self .crop_end_type = str (row .end_type ).strip ().lower ()
283306 if self .crop_end_type not in ["harvest" , "earliest" , "maturity" ]:
@@ -308,7 +331,7 @@ def _build_yaml_agromanagement(self):
308331 crop_start_type = self .crop_start_type ,
309332 crop_end_date = self .crop_end_date ,
310333 crop_end_type = self .crop_end_type ,
311- duration = self .max_duration ,
334+ max_duration = self .max_duration ,
312335 campaign_end_date = self .campaign_end_date
313336 )
314337 return input
@@ -584,7 +607,6 @@ class SoilDataIterator(list):
584607 (9050131, 625000000, 9000282, 50)
585608 (9050131, 625000000, 9000283, 50)
586609
587- Ignore this line
588610 """
589611
590612 # name of the table with Elementary Mapping Units
@@ -839,6 +861,10 @@ class SiteDataProvider(dict):
839861 Note that the parameter SSI (Initial surface storage) is
840862 set to zero
841863
864+ Moreover, the start date of the water balance is defined by the
865+ column GIVEN_STARTDATE_WATBAL. This value can be accessed as
866+ an attribute `start_date_waterbalance`.
867+
842868 """
843869
844870 def __init__ (self , engine , grid_no , crop_no , campaign_year , stu_no ):
@@ -874,6 +900,9 @@ def __init__(self, engine, grid_no, crop_no, campaign_year, stu_no):
874900 % (self .grid_no , self .crop_no , self .campaign_year , self .stu_no ))
875901 raise exc .PCSEError (msg )
876902
903+ # Start date water balance
904+ self .start_date_waterbalance = check_date (row .given_startdate_watbal )
905+
877906 # Derived global parameters from table SITE
878907 table_site = Table ('site' , metadata , autoload = True )
879908 r = select ([table_site ]).execute ()
0 commit comments