PK]BL>hϵpycgms/Wofost71_PP.conf# -*- coding: utf-8 -*- # Copyright (c) 2004-2014 Alterra, Wageningen-UR # Allard de Wit (allard.dewit@wur.nl), April 2014 """PCSE configuration file for WOFOST Potential Production simulation in PCSE identical to the FORTRAN WOFOST 7.1 This configuration file defines the soil and crop components that should be used for potential production simulation. """ from pcse.soil.classic_waterbalance import WaterbalancePP from pcse.crop.wofost import Wofost from pcse.agromanager import AgroManager # Module to be used for water balance SOIL = WaterbalancePP # Module to be used for the crop simulation itself CROP = Wofost # Module to use for AgroManagement actions AGROMANAGEMENT = AgroManager # variables to save at OUTPUT signals # Set to an empty list if you do not want any OUTPUT OUTPUT_VARS = ["DVS","LAI","TAGP", "TWSO", "TWLV", "TWST", "TWRT", "TRA", "RD", "SM", "WWLOW"] # interval for OUTPUT signals, either "daily"|"dekadal"|"monthly"|"weekly" # For daily output you change the number of days between successive # outputs using OUTPUT_INTERVAL_DAYS. For dekadal and monthly # output this is ignored. OUTPUT_INTERVAL = "dekadal" OUTPUT_INTERVAL_DAYS = 1 # Weekday: Monday is 0 and Sunday is 6 OUTPUT_WEEKDAY = 0 # Summary variables to save at CROP_FINISH signals # Set to an empty list if you do not want any SUMMARY_OUTPUT SUMMARY_OUTPUT_VARS = ["DVS","LAIMAX","TAGP", "TWSO", "TWLV", "TWST", "TWRT", "CTRAT", "RD", "DOS", "DOE", "DOA", "DOM", "DOH", "DOV"] # Summary variables to save at TERMINATE signals # Set to an empty list if you do not want any TERMINAL_OUTPUT TERMINAL_OUTPUT_VARS = [] PK]BL>>pycgms/Wofost71_WLP_FD.conf# -*- coding: utf-8 -*- # Copyright (c) 2004-2014 Alterra, Wageningen-UR # Allard de Wit (allard.dewit@wur.nl), April 2014 """PCSE configuration file for WOFOST Water-limited Production simulation in PCSE identical to the FORTRAN WOFOST 7.1 This configuration file defines the soil and crop components that should be used for water-limited production simulation for freely draining soils. """ from pcse.soil.classic_waterbalance import WaterbalanceFD from pcse.crop.wofost import Wofost from pcse.agromanager import AgroManager # Module to be used for water balance SOIL = WaterbalanceFD # Module to be used for the crop simulation itself CROP = Wofost # Module to use for AgroManagement actions AGROMANAGEMENT = AgroManager # variables to save at OUTPUT signals # Set to an empty list if you do not want any OUTPUT OUTPUT_VARS = ["DVS","LAI","TAGP", "TWSO", "TWLV", "TWST", "TWRT", "TRA", "RD", "SM", "WWLOW"] # interval for OUTPUT signals, either "daily"|"dekadal"|"monthly"|"weekly" # For daily output you change the number of days between successive # outputs using OUTPUT_INTERVAL_DAYS. For dekadal and monthly # output this is ignored. OUTPUT_INTERVAL = "dekadal" OUTPUT_INTERVAL_DAYS = 1 # Weekday: Monday is 0 and Sunday is 6 OUTPUT_WEEKDAY = 0 # Summary variables to save at CROP_FINISH signals # Set to an empty list if you do not want any SUMMARY_OUTPUT SUMMARY_OUTPUT_VARS = ["DVS","LAIMAX","TAGP", "TWSO", "TWLV", "TWST", "TWRT", "CTRAT", "RD", "DOS", "DOE", "DOA", "DOM", "DOH", "DOV"] # Summary variables to save at TERMINATE signals # Set to an empty list if you do not want any TERMINAL_OUTPUT TERMINAL_OUTPUT_VARS = ["WTRAT", "EVST", "EVWT", "TSR", "RAINT", "TOTINF", "TOTIRR", "PERCT", "LOSST"] PKGMs00pycgms/__init__.py"""The PyCGMS package provides a python implementation of the crop simulation system embedded in the Crop Growth Monitoring System (CGMS). Under the hood, the actual crop simulations are carried out by the WOFOST implementation in [PCSE] which provides a fully open source implementation of many crop simulation models developed in Wageningen. PyCGMS was designed to be compatible with all versions of the CGMS database and can therefore also run on legacy CGMS implementations. """ from . import runner __version__ = "0.1.0" def start(): runner.main() PKGM4Y Y pycgms/data_providers.pyimport sqlalchemy as sa from .runner import db from pcse.db import cgms8, cgms12, cgms14 # Note: you cannot import the dataproviders from the right CGMS version here # already because db.version will only be available AFTER this module has # been imported. def get_agromanagement(engine, grid, crop, year): if db.version == 8: agro = cgms8.AgroManagementDataProvider(engine, crop_no=crop, grid_no=grid, campaign_year=year) elif db.version == 12: agro = cgms12.AgroManagementDataProvider(engine, crop_no=crop, grid_no=grid, campaign_year=year) else: agro = cgms14.AgroManagementDataProvider(engine, idgrid=grid, idcrop_parametrization=crop, campaign_year=year) return agro def get_weatherdata(engine, grid, start, end): if db.version == 8: wdp = cgms8.GridWeatherDataProvider(engine, grid_no=grid, start_date=start, end_date=end, use_cache=False) elif db.version == 12: wdp = cgms12.WeatherObsGridDataProvider(engine, grid_no=grid, start_date=start, end_date=end, use_cache=False) else: wdp = cgms14.WeatherObsGridDataProvider(engine, idgrid=grid, start_date=start, end_date=end, use_cache=False) return wdp def get_cropdata(engine, grid, crop, year): if db.version == 8: cropd = cgms8.CropDataProvider(engine, crop_no=crop, grid_no=grid, campaign_year=year) elif db.version == 12: cropd = cgms12.CropDataProvider(engine, crop_no=crop, grid_no=grid, campaign_year=year) else: cropd = cgms14.CropDataProvider(engine, idgrid=grid, idcrop_parametrization=crop) return cropd def get_soiliterator(engine, grid): if db.version == 8: soild = cgms8.SoilDataIterator(engine, grid_no=grid) elif db.version == 12: soild = cgms12.SoilDataIterator(engine, grid_no=grid) else: soild = cgms14.SoilDataIterator(engine, idgrid=grid) return soild def get_sitedata(engine, grid, crop, year, stu): if db.version == 8: sited = cgms8.SiteDataProvider(engine, grid_no=grid, crop_no=crop, campaign_year=year, stu_no=stu) elif db.version == 12: sited = cgms12.SiteDataProvider(engine, grid_no=grid, crop_no=crop, campaign_year=year, stu_no=stu) else: sited = cgms14.SiteDataProvider(engine, idgrid=grid, idcrop_parametrization=crop, campaign_year=year, idstu=stu) return sited def get_suitability(engine, crop): if db.version == 8: suitability = cgms8.STU_Suitability(engine, crop_no=crop) elif db.version == 12: suitability = cgms12.STU_Suitability(engine, crop_no=crop) else: suitability = cgms14.STU_Suitability(engine, idcrop_parametrization=crop) return suitability def get_grids(engine, crop, year): meta = sa.MetaData(engine) if db.version in (8, 12): tbl = sa.Table('crop_calendar', meta, autoload=True) s = sa.select([tbl.c.grid_no], sa.and_(tbl.c.crop_no==crop, tbl.c.year==year)).distinct() rows = s.execute() grids = [r.grid_no for r in rows] else: tbl = sa.Table('crop_spatializations', meta, autoload=True) s = sa.select([tbl.c.idgrid], tbl.c.idcrop_parametrization==crop).distinct() rows = s.execute() grids = [r.idgrid for r in rows] return grids PKGMMz]44pycgms/runner.pyclass C(object): "A container class" pass import os import shutil import argparse import datetime as dt import sqlalchemy as sa import pandas as pd import numpy as np import pcse from pcse.engine import CGMSEngine from pcse.util import WOFOST71SiteDataProvider, DummySoilDataProvider from pcse.base_classes import ParameterProvider # Placeholder for DB version, needed by data_providers db = C() from . import data_providers as dp soil_identifiers = {8: ("smu_no", "smu_area", "stu_no", "stu_perc"), 12: ("smu_no", "smu_area", "stu_no", "stu_perc"), 14: ("idsmu", "smu_area", "idstu", "stu_perc")} # Parameters with date types that cannot be averaged date_type_variables = ["DOS", "DOE", "DOV", "DOA", "DOM", "DOH"] def valid_date_type(arg_date_str): """custom argparse *date* type for user dates values given from the command line""" try: return dt.datetime.strptime(arg_date_str, "%Y-%m-%d") except ValueError: msg = "Given Date ({0}) not valid! Expected format, YYYY-MM-DD!".format(arg_date_str) raise argparse.ArgumentTypeError(msg) def create_parser(): parser = argparse.ArgumentParser(description='Run a gridded WOFOST simulation on a CGMS database.') parser.add_argument('--db_version', dest='db_version', action='store', default=None, choices=[8, 12, 14], help='Type of CGMS DB to use (either 8, 12 or 14).', required=True, type=int ) parser.add_argument('--dsn', dest='dsn', action='store', default=None, type=str, required=True, help="SQLAlchemy connection URL for CGMS DB to connect to. See also " "http://docs.sqlalchemy.org/en/latest/core/engines.html" ) parser.add_argument('--crop', dest='crop', action='store', default=None, type=int, required=True, help="Run simulations for given crop number." ) parser.add_argument('--year', dest='year', action='store', default=None, type=int, required=True, help="Run simulations for given year. " "The year refers to the year in the crop_calendar table which " "usually indicates the year where sowing of emergence takes place." ) parser.add_argument('--grid', dest='grid', action='store', default=None, type=int, help="Run simulations for given grid. Optional, by default all grids will " "be simulated where the crop is defined." ) parser.add_argument('--run_till', dest='run_till', action='store', default=None, type=valid_date_type, metavar="yyyy-mm-dd", help="Run simulations up till this date. This is useful for " "simulations in the current year where not all weather data are " "available up till the end of the simulation." ) parser.add_argument('--aggr_level', dest='aggr_level', action='store', default='stu', choices=["stu", "smu", "grid"], help='Aggregation level for output, default is "stu"', required=False, type=str ) parser.add_argument('--output', dest='output', action='store', type=str, metavar="OUT_PATH", required = True, help="Store simulation results at this location." ) parser.add_argument('--output_type', dest='output_type', action='store', default='csv', choices=["csv", "xls", "hdf5", "json"], help='Type of output file to write', required=False, type=str ) parser.add_argument('--use_isw_date', dest='use_isw_date', action='store', default=False, help='If True the start_date from the table INITIAL_SOIL_WATER will be used as ' 'campaign_start_date, default False.', required=False, type=bool ) return parser def get_preceeding_dekad(c): """Finds the dekad as the first dekad preceding the cdate :param c: a date object :return: the date representing the preceeding dekad """ if c.day < 10: prec_dekad = dt.date(c.year, c.month, 1) - dt.timedelta(days=1) elif c.day < 20: prec_dekad = dt.date(c.year, c.month, 10) else: prec_dekad = dt.date(c.year, c.month, 20) return prec_dekad def weighted_avg(group, col_name, weight_name): """Compute the weighted average for col_name from the grouped dataframe using weight_name as weights. """ d = group[col_name].values w = group[weight_name].values try: return np.average(d, weights=w) except Exception as e: pass def group_dataframe(df, groupby, excluding, weightby): """Compute weighted averages on the columns of dataframe 'df' using weights from 'weight_column', grouped by columns 'groupby' and excluding columns 'excluding' :param df: A Pandas DataFrame :param groupby: list of columns to group by :param exclude: list of columns to exclude :param weightby: column to use as weights :return: a new DataFrame with weighted averages """ results = {} grp = df.groupby(groupby) for colname in df.columns: if colname in excluding: continue results[colname] = grp.apply(weighted_avg, colname, weightby) return pd.DataFrame(results).reset_index() def main(): parser = create_parser() args = parser.parse_args() if None in [args.crop, args.year, args.dsn, args.db_version]: parser.print_help() return db.version = args.db_version # labels for soil columns which differ across database versions lbl_smu, lbl_smu_area, lbl_stu, lbl_stu_perc = soil_identifiers[args.db_version] engine = sa.create_engine(args.dsn) if args.grid is None: grids = dp.get_grids(engine, args.cropd, args.year) else: grids = [args.grid,] for grid in grids: agro = dp.get_agromanagement(engine, grid, args.crop, args.year) # Fix the campaign start/end onto dekad boundaries start_dekad = get_preceeding_dekad(agro.campaign_start_date) end_dekad = get_preceeding_dekad(agro.campaign_end_date) agro.set_campaign_start_date(start_dekad) agro.campaign_end_date = end_dekad # We want to pull 180 days of additional weather data to allow water balance initialization # with the --use-isw option start_date_weather = start_dekad - dt.timedelta(days=180) weatherdata = dp.get_weatherdata(engine, grid, start=start_date_weather, end=end_dekad) # Fetch or define crop, soil and site data cropd = dp.get_cropdata(engine, grid, args.crop, args.year) sited = WOFOST71SiteDataProvider(WAV=100) soild = DummySoilDataProvider() parameters = ParameterProvider(cropdata=cropd, soildata=soild, sitedata=sited) # Run WOFOST potential production and convert output to Pandas DataFrame mconf = os.path.join(os.path.dirname(__file__), "Wofost71_PP.conf") wofost = CGMSEngine(parameters, weatherdata, agro, config=mconf) # Run till end of the campaign year or date provided by --run_until if args.run_till is not None: wofost.run_till(args.run_till) else: wofost.run_till(agro.campaign_end_date) df_simyield_pp = pd.DataFrame(wofost.get_output()) df_simyield_pp_summary = pd.DataFrame(wofost.get_summary_output()) # First add grid number and simulation type to the dataframe df_simyield_pp_summary["grid"] = grid df_simyield_pp["grid"] = grid df_simyield_pp_summary["sim_type"] = "pp" df_simyield_pp["sim_type"] = "pp" # Pull in soil data for water-limited run soil_iterator = dp.get_soiliterator(engine, grid) suitable_stu = dp.get_suitability(engine, args.crop) # Placeholders for simulation_results at stu level df_simyield_wlp = None df_simyield_wlp_summary = None # Run water-limited simulation results for smu_no, area, stu_no, percentage, soild in soil_iterator: # Check if this is a suitable STU if stu_no not in suitable_stu: print("Skipping stu: %s" % stu_no) continue print("Processing grid: %i, smu: %i, stu: %i" % (grid, smu_no, stu_no)) sited = dp.get_sitedata(engine, grid, args.crop, args.year, stu_no) if args.use_isw_date: agro.set_campaign_start_date(sited.start_date_waterbalance) parameters = ParameterProvider(cropdata=cropd, soildata=soild, sitedata=sited) mconf = os.path.join(os.path.dirname(__file__), "Wofost71_WLP_FD.conf") wofost = CGMSEngine(parameters, weatherdata, agro, config=mconf) # Run till end of the campaign year or date provided by --run_until if args.run_till is not None: wofost.run_till(args.run_till) else: wofost.run_till(agro.campaign_end_date) # Get output df = pd.DataFrame(wofost.get_output()) # remove simulation days before start_dekad due to soil moisture initialization ix = df.day >= start_dekad df = df[ix] # Add soil identifiers for weighted averaging df[lbl_smu] = smu_no df[lbl_stu] = stu_no df[lbl_smu_area] = area df[lbl_stu_perc] = percentage if df_simyield_wlp is None: df_simyield_wlp = df else: df_simyield_wlp = pd.concat([df_simyield_wlp, df]) # Get summary output df_summary = pd.DataFrame(wofost.get_summary_output()) df_summary[lbl_smu] = smu_no df_summary[lbl_stu] = stu_no df_summary[lbl_smu_area] = area df_summary[lbl_stu_perc] = percentage if df_simyield_wlp_summary is None: df_simyield_wlp_summary = df_summary else: df_simyield_wlp_summary = pd.concat([df_simyield_wlp_summary, df_summary]) # Start aggregating simulation results # First add grid number and simulation type to the dataframes df_simyield_wlp_summary["grid"] = grid df_simyield_wlp["grid"] = grid # First aggregate all STU's into SMU's by using the 'stu_perc' percentages as weights if args.aggr_level in ("smu", "grid"): df_simyield_wlp = \ group_dataframe(df_simyield_wlp, groupby=["grid", "day", lbl_smu], weightby=lbl_stu_perc, excluding=["grid", "day", lbl_smu, lbl_stu, lbl_stu_perc]) df_simyield_wlp_summary = \ group_dataframe(df_simyield_wlp_summary, groupby=["grid", lbl_smu], weightby=lbl_stu_perc, excluding=["grid", lbl_smu, lbl_stu, lbl_stu_perc] + date_type_variables) # Next aggregate all SMU's to the grid level by using the 'smu_area' as weights if args.aggr_level == 'grid': df_simyield_wlp = \ group_dataframe(df_simyield_wlp, groupby=["grid", "day"], weightby=lbl_smu_area, excluding=["grid", "day", lbl_smu, lbl_smu_area]) df_simyield_wlp_summary = \ group_dataframe(df_simyield_wlp_summary, groupby=["grid"], weightby=lbl_smu_area, excluding=["grid", lbl_smu, lbl_smu_area]) df_simyield_wlp_summary["sim_type"] = "wlp" df_simyield_wlp["sim_type"] = "wlp" # combine potential (pp) and water-limited production (wlp) in a single dataframe df_simyield = pd.concat([df_simyield_pp, df_simyield_wlp], sort=True) df_simyield_summary = pd.concat([df_simyield_pp_summary, df_simyield_wlp_summary], sort=True) # Write timeseries output file fname_ts = "{grid}_{crop}_{year}.{type}".format(grid=grid, crop=args.crop, year=args.year, type=args.output_type) fname_sum = "{grid}_{crop}_{year}_summary.{type}".format(grid=grid, crop=args.crop, year=args.year, type=args.output_type) fname_ts = os.path.join(args.output, fname_ts) fname_sum = os.path.join(args.output, fname_sum) if args.output_type == "csv": df_simyield.to_csv(fname_ts, header=True, index=False) df_simyield_summary.to_csv(fname_sum, header=True, index=False) elif args.output_type == "xls": df_simyield.to_excel(fname_ts, index=False) df_simyield_summary.to_excel(fname_sum, index=False) elif args.output_type == "hdf5": df_simyield.to_hdf(fname_ts, mode="w", complevel=9, complib="blosc") df_simyield_summary.to_hdf(fname_sum, mode="w", complevel=9, complib="blosc") elif args.output_type == "json": df_simyield.to_json(fname_ts, orient="records", date_format="iso", lines=True) df_simyield_summary.to_json(fname_sum, orient="records", date_format="iso", lines=True) PK!H I$''pycgms-0.1.0.dist-info/entry_points.txtN+I/N.,()*LN-PV%E%\\PK!Hd BUcpycgms-0.1.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,rzd&Y)r$[)T&UD"PK!Hrʦ[ pycgms-0.1.0.dist-info/METADATAYms6_ioDN4ӻSl'uخexX$$eg E9/Nidg]@U-3Ye6X;щ,XTt^{Ѵ) iWcqPlTT2s%*ku\/L)tQPe-kf&jlKESa cʍGmb$?.3bmѱNU䧓ӫh@IKL+]CUHw1 qGʥVWdpߔ5^*HC=wY~.3@ݫT䒘«ƉXChrcT&&MU*Z kStz$&[ap)Y˒bx: r'//ώֆ3kV.Xrg>7dwt~kUnx]߿_Eg!ÇʱKS(Z4D_s+WB̌ .LkcI,3*J4hF#)iӅG`j7pmJ XB >2祈HHD|z#^_̭Nn,䞫ilVRh6E ] _W^ךJr knS^DDH6e+UҵXg'>l-TIB)"_趁X69嚪2h#P @/'g[ YH--/WnpĊ*dt`e3KTxb ^(땗Ě^ {ΌN$65 0%G'* '2=)KA*LIo/oL*T5#;9 If*ϱI5}'bV [W@̵%|[{XMX5h@ph @@Dm?]dQP5)IEf|f 5Z z>@J.pvfCF(˅P<[RX=ް& zvǑ[ sEhS^yXR˪A`K%P{P˄_sQ87Һ-~jjb!5xC!Ӧ|"WrƬ20N̚fcGSTҽoߞPv 7{agd*BD[gU#@yzkaQ%)F M YS>&z 6E&.tA 0Df;R_x a-Ww>/JՆ[@9ɏE[ݬ9d7'J9L֣Vs<"#>4,*D0T#-#%PɃ٫V)Gл%5 Ζ+-@YȈЇǜ6]zQ9sAl4P:6/(02m,3 GѠuv&Lz,0Cީ,bjBGu& uw1 Y" |7 m5^[S4Y4HbLkv J L!^JL'sțo~u0a3‰)Ȑ5`y! aK8HS}ўqZRkMH2 f'nef5U)~"d咳i1ZJ`w4 ~-ů_pޏ[ Έp1 q=\mvM`՟ؕ`zoܚO}spr6?!NBxw~tpC$5 aQ }r>INNff@$U^^$g%4P?Rw?x`^>:SDe4)q9=LWЯ JA-dzfS >`% oKMA1&ϺdҞpq n=xC#~4[fQ;ⵖ2Ӆ*0T\'>Y˦ .h\z bc#U@Q?1sd ڟ9X`ʴXt"&Cϧ< -dGҞٸmJ)?@%'wht=FW#YqU.SGW_ZP `e$JΏuć󭕘 yVcJvGhcsڤb/!|Ѓv|N5XfR˺ӽgyY oz|_u6?ґϞI?X/2HAEH| <+h&.l.w \K898'ӣ ;t););Y+< Tu`\6\% ץG_sG|zow7Im#N0a,? RrEItɁId{q|[iJ3ڴXN^HRk /˴A“\*G{ww˝|DozH M۟Iׯ辐^v)hR\''#L70?~ި7ϊp9ѥ~@erC PK!HEpycgms-0.1.0.dist-info/RECORDuI@@}DE/J@d2+ɬޏkNqWt,k bV@r+͙5[,9( ڴs%$h>cZ,{q{,єɨjƥ߈V:{&ȱ aBu#w#U+ҊĦ`[$2os {Y>N?<)^%(Y9!Ma<2K0qA(@1×}햡0dKetxt? Wp`H@zgþhϵpycgms/Wofost71_PP.confPK]BL>>pycgms/Wofost71_WLP_FD.confPKGMs00apycgms/__init__.pyPKGM4Y Y pycgms/data_providers.pyPKGMMz]44Ppycgms/runner.pyPK!H I$''ASpycgms-0.1.0.dist-info/entry_points.txtPK!Hd BUcSpycgms-0.1.0.dist-info/WHEELPK!Hrʦ[ 9Tpycgms-0.1.0.dist-info/METADATAPK!HE_pycgms-0.1.0.dist-info/RECORDPK a