Example #1
0
 def calc_liveweight(site, herb_class, available_forage):
     supp_available = 0
     DOY = 150
     prop_legume = 0
     herb_class.calc_distance_walked(site.S, herb_class.stocking_density,
                                     available_forage)
     max_intake = herb_class.calc_max_intake()
     ZF = herb_class.calc_ZF()
     HR = forage_u.calc_relative_height(available_forage)
     diet = forage_u.diet_selection_t2(ZF, HR, prop_legume, supp_available,
                                       max_intake, herb_class.FParam,
                                       available_forage)
     diet_interm = forage_u.calc_diet_intermediates(diet, herb_class,
                                                    prop_legume, DOY, site)
     reduced_max_intake = forage_u.check_max_intake(diet, diet_interm,
                                                    herb_class, max_intake)
     if reduced_max_intake < max_intake:
         diet = forage_u.diet_selection_t2(ZF, HR, prop_legume,
                                           supp_available,
                                           reduced_max_intake,
                                           herb_class.FParam,
                                           available_forage)
         diet_interm = forage_u.calc_diet_intermediates(
             diet, herb_class, prop_legume, DOY, site)
     delta_W = forage_u.calc_delta_weight(diet_interm, herb_class)
     return delta_W
Example #2
0
def one_step(FParam,
             DOY,
             herd,
             available_forage,
             prop_legume,
             supp_available,
             supp,
             intake=None):
    """One step of the forage model, if available forage does not change."""

    row = []
    max_intake = herd.calc_max_intake(FParam)

    if herd.Z < FParam.CR7:
        ZF = 1. + (FParam.CR7 - herd.Z)
    else:
        ZF = 1.

    if intake is not None:  # if forage intake should be forced
        diet = forage.Diet()
        diet.If = intake
        diet.DMDf = available_forage[0].digestibility  # this is super hack-y
        diet.CPIf = intake * available_forage[
            0].crude_protein  # and only works with one type of available forage
        diet.Is = supp.DMO  # also force intake of all supplement offered
        diet_interm = forage.calc_diet_intermediates(FParam, diet, supp, herd,
                                                     site, prop_legume, DOY)
    else:
        diet = forage.diet_selection_t2(ZF, prop_legume, supp_available, supp,
                                        max_intake, FParam, available_forage,
                                        force_supp)
        diet_interm = forage.calc_diet_intermediates(FParam, diet, supp, herd,
                                                     site, prop_legume, DOY)
        reduced_max_intake = forage.check_max_intake(FParam, diet, diet_interm,
                                                     herd, max_intake)
        if reduced_max_intake < max_intake:
            diet = forage.diet_selection_t2(ZF, prop_legume, supp_available,
                                            supp, reduced_max_intake, FParam,
                                            available_forage, force_supp)
            diet_interm = forage.calc_diet_intermediates(
                FParam, diet, supp, herd, site, prop_legume, DOY)

    delta_W = forage.calc_delta_weight(FParam, diet, diet_interm, supp, herd)
    #delta_W_step = forage.convert_daily_to_step(delta_W)
    herd.update(FParam, delta_W, forage.find_days_per_step())

    row.append(max_intake)
    row.append(diet.If)
    row.append(diet.Is)
    row.append(diet.CPIf)
    row.append(diet_interm.MEItotal)
    row.append(delta_W)
    return row
Example #3
0
def one_step(FParam, DOY, herd, available_forage, prop_legume, supp_available,
    supp):
    """One step of the forage model, if available forage does not change."""

    row = []
    row.append(available_forage[0].label)
    max_intake = herd.calc_max_intake(FParam)

    if herd.Z < FParam.CR7:
        ZF = 1. + (FParam.CR7 - herd.Z)
    else:
        ZF = 1.

    diet = forage.diet_selection_t2(ZF, prop_legume, supp_available, supp,
        max_intake, FParam, available_forage)
    diet_interm = forage.calc_diet_intermediates(FParam, diet, supp, herd, site,
        prop_legume, DOY)
    reduced_max_intake = forage.check_max_intake(FParam, diet, diet_interm, herd,
        max_intake)
    if reduced_max_intake < max_intake:
        diet = forage.diet_selection_t2(ZF, prop_legume, supp_available, supp,
            reduced_max_intake, FParam, available_forage)
        diet_interm = forage.calc_diet_intermediates(FParam, diet, supp, herd, site,
            prop_legume, DOY)
    delta_W = forage.calc_delta_weight(FParam, diet, diet_interm, supp, herd)
    
    row.append(herd.W)
    row.append(max_intake)
    row.append(reduced_max_intake)
    row.append(diet.If)
    row.append(diet.CPIf)
    row.append(diet_interm.MEItotal)
    row.append(delta_W)
    
    #delta_W_step = forage.convert_daily_to_step(delta_W)
    herd.update(FParam, delta_W, forage.find_days_per_step())
    return row
Example #4
0
def diet_selection(forage_args, live_gm2, dead_gm2):
    """Perform diet selection on forage, as a metric of forage quality. Return
    diet selected by an individual animal from the forage available as
    described by live_gm2 and dead_gm2.  Assume that digestibility of forage
    is calculated from crude protein, and crude protein is specified in the
    grass csv (i.e. fixed)."""
    
    forage_u.set_time_step('month')
    herbivore_list = []
    herbivore_input = (pd.read_csv(forage_args['herbivore_csv']).to_dict(
                       orient='records'))
    for h_class in herbivore_input:
        herd = forage_u.HerbivoreClass(h_class)
        herd.update()
        herbivore_list.append(herd)
    stocking_density_dict = forage_u.populate_sd_dict(herbivore_list)
    total_SD = forage_u.calc_total_stocking_density(herbivore_list)
    grass_list = (pd.read_csv(forage_args['grass_csv'])).to_dict(orient='records')
    assert len(grass_list) == 1, "Must be one grass type"
    grass_list[0]['green_gm2'] = live_gm2
    grass_list[0]['dead_gm2'] = dead_gm2
    available_forage = forage_u.calc_feed_types(grass_list)
    for feed_type in available_forage:
        feed_type.calc_digestibility_from_protein(
                                             forage_args['digestibility_flag'])  # TODO n_mult??
    assert len(herbivore_list) == 1, "must be one herbivore type"
    herb_class = herbivore_list[0]
    herb_class.calc_distance_walked(1., total_SD, available_forage)
    max_intake = herb_class.calc_max_intake()
    ZF = herb_class.calc_ZF()
    HR = forage_u.calc_relative_height(available_forage)
    diet = forage_u.diet_selection_t2(ZF, HR, 0., 0, max_intake,
                                    herb_class.FParam,
                                    available_forage,
                                    herb_class.f_w, herb_class.q_w)
    diet_interm = forage_u.calc_diet_intermediates(diet, herb_class,
                                                 0., 150)
    reduced_max_intake = forage_u.check_max_intake(diet, diet_interm,
                                                 herb_class,
                                                 max_intake)
    if reduced_max_intake < max_intake:
        diet = forage_u.diet_selection_t2(ZF, HR, 0., 0, max_intake,
                                        herb_class.FParam,
                                        available_forage,
                                        herb_class.f_w,
                                        herb_class.q_w)
    return diet
Example #5
0
def different_diets(available_forage, herbivore_csv, weight_1):
    """Create different diets for two hypothetical animal types.  The weight
    ratio describes the ratio of """

    time_step = 'month'
    forage_u.set_time_step(time_step)
    total_SD = 2
    prop_legume = 0
    DOY = 100
    supp_available = 0
    site = forage_u.SiteInfo(1., -3.25)

    supp_csv = "C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/Forage_model/model_inputs/Shem_et_al_1995_supp.csv"
    supp_list = (pandas.read_csv(supp_csv)).to_dict(orient='records')
    supp_info = supp_list[0]
    supp = forage_u.Supplement(FreerParam.FreerParamCattle('indicus'),
                               supp_info['digestibility'],
                               supp_info['kg_per_day'], supp_info['M_per_d'],
                               supp_info['ether_extract'],
                               supp_info['crude_protein'],
                               supp_info['rumen_degradability'])
    herbivore_input = (pandas.read_csv(herbivore_csv)).to_dict(
        orient='records')
    herbivore_list = []
    for h_class in herbivore_input:
        herd = forage_u.HerbivoreClass(h_class)
        herd.update()
        herbivore_list.append(herd)

    avail_weights = [weight_1, 0]
    qual_weights = [0, weight_1]

    diet_dict = {}
    for idx in xrange(len(herbivore_list)):
        herb_class = herbivore_list[idx]
        f_w = avail_weights[idx]
        rq_w = qual_weights[idx]
        herb_class.calc_distance_walked(total_SD, site.S, available_forage)
        max_intake = herb_class.calc_max_intake()

        ZF = herb_class.calc_ZF()
        HR = forage_u.calc_relative_height(available_forage)
        diet = diet_selection_t2(ZF, HR, prop_legume, supp_available, supp,
                                 max_intake, herb_class.FParam,
                                 available_forage, idx, f_w, rq_w)
        diet_interm = forage_u.calc_diet_intermediates(diet, supp, herb_class,
                                                       site, prop_legume, DOY)
        # if herb_class.type != 'hindgut_fermenter':
        # reduced_max_intake = forage_u.check_max_intake(diet,
        # diet_interm,
        # herb_class,
        # max_intake)
        # if reduced_max_intake < max_intake:
        # diet = forage_u.diet_selection_t2(ZF, HR,
        # args[u'prop_legume'],
        # supp_available, supp,
        # reduced_max_intake,
        # herb_class.FParam,
        # available_forage)
        diet_dict[herb_class.label] = diet
    return diet_dict
    # forage_u.reduce_demand(diet_dict, stocking_density_dict,
    # available_forage)
    diet_interm = forage_u.calc_diet_intermediates(diet, supp, herb_class,
                                                   site, prop_legume, DOY)
    delta_W = forage_u.calc_delta_weight(diet_interm, herb_class)
    delta_W_step = forage_u.convert_daily_to_step(delta_W)
    herb_class.update(delta_weight=delta_W_step,
                      delta_time=forage_u.find_days_per_step())
def launch_model(herb_csv, grass_list, outdir):
    f_args = {
        'latitude': 0.02759,
        'prop_legume': 0.0,
        'steepness': 1.,
        'DOY': 1,
        'start_year': 2015,
        'start_month': 1,
        'num_months': 1,
        'mgmt_threshold': 0.,
        'century_dir':
        'C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/CENTURY4.6/Century46_PC_Jan-2014',
        'outdir': outdir,
        'template_level': 'GH',
        'fix_file': 'drytrpfi.100',
        'user_define_protein': 1,
        'user_define_digestibility': 0,
        'herbivore_csv': herb_csv,
        'supp_csv':
        "C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/Forage_model/model_inputs/Rubanza_et_al_2005_supp.csv",
        'restart_yearly': 0,
        'diet_verbose': 1,
    }
    now_str = datetime.now().strftime("%Y-%m-%d--%H_%M_%S")
    if not os.path.exists(f_args['outdir']):
        os.makedirs(f_args['outdir'])
    forage.write_inputs_log(f_args, now_str)
    forage.set_time_step('month')  # current default, enforced by CENTURY
    add_event = 1  # TODO should this ever be 0?
    steps_per_year = forage.find_steps_per_year()
    if f_args['diet_verbose']:
        master_diet_dict = {}
        diet_segregation_dict = {'step': [], 'segregation': []}
    herbivore_list = []
    if f_args[u'herbivore_csv'] is not None:
        herbivore_input = (pandas.read_csv(
            f_args[u'herbivore_csv'])).to_dict(orient='records')
        for h_class in herbivore_input:
            herd = forage.HerbivoreClass(h_class)
            herd.update()
            BC = 1  # TODO get optional BC from user
            # if BC:
            # herd.check_BC(BC)

            herbivore_list.append(herd)
    results_dict = {'step': [], 'year': [], 'month': []}
    for h_class in herbivore_list:
        results_dict[h_class.label + '_kg'] = []
        results_dict[h_class.label + '_gain_kg'] = []
        results_dict[h_class.label + '_intake_forage_per_indiv_kg'] = []
        if h_class.sex == 'lac_female':
            results_dict['milk_prod_kg'] = []
    results_dict['total_offtake'] = []
    supp_available = 0
    if 'supp_csv' in f_args.keys():
        supp_list = (pandas.read_csv(
            f_args[u'supp_csv'])).to_dict(orient='records')
        assert len(supp_list) == 1, "Only one supplement type is allowed"
        supp_info = supp_list[0]
        supp = forage.Supplement(FreerParam.FreerParamCattle('indicus'),
                                 supp_info['digestibility'],
                                 supp_info['kg_per_day'], supp_info['M_per_d'],
                                 supp_info['ether_extract'],
                                 supp_info['crude_protein'],
                                 supp_info['rumen_degradability'])
        if supp.DMO > 0.:
            supp_available = 1

    stocking_density_dict = forage.populate_sd_dict(herbivore_list)
    total_SD = forage.calc_total_stocking_density(herbivore_list)
    site = forage.SiteInfo(f_args[u'steepness'], f_args[u'latitude'])
    threshold_exceeded = 0
    try:
        for step in xrange(f_args[u'num_months']):
            step_month = f_args[u'start_month'] + step
            if step_month > 12:
                mod = step_month % 12
                if mod == 0:
                    month = 12
                else:
                    month = mod
            else:
                month = step_month
            year = (step / 12) + f_args[u'start_year']
            if month == 1 and f_args['restart_yearly'] and \
                                            f_args[u'herbivore_csv'] is not None:
                threshold_exceeded = 0
                herbivore_list = []
                for h_class in herbivore_input:
                    herd = forage.HerbivoreClass(h_class)
                    herd.update()
                    herbivore_list.append(herd)
            # get biomass and crude protein for each grass type
            available_forage = forage.calc_feed_types(grass_list)
            results_dict['step'].append(step)
            results_dict['year'].append(year)
            results_dict['month'].append(month)
            if not f_args[u'user_define_digestibility']:
                for feed_type in available_forage:
                    feed_type.calc_digestibility_from_protein()
            total_biomass = forage.calc_total_biomass(available_forage)
            if step == 0:
                # threshold biomass, amount of biomass required to be left
                # standing (kg per ha)
                threshold_biomass = total_biomass * float(
                    f_args[u'mgmt_threshold'])
            diet_dict = {}
            for herb_class in herbivore_list:
                herb_class.calc_distance_walked(total_SD, site.S,
                                                available_forage)
                max_intake = herb_class.calc_max_intake()

                ZF = herb_class.calc_ZF()
                HR = forage.calc_relative_height(available_forage)
                diet = forage.diet_selection_t2(ZF, HR, f_args[u'prop_legume'],
                                                supp_available, supp,
                                                max_intake, herb_class.FParam,
                                                available_forage,
                                                herb_class.f_w, herb_class.q_w)
                diet_interm = forage.calc_diet_intermediates(
                    diet, supp, herb_class, site, f_args[u'prop_legume'],
                    f_args[u'DOY'])
                if herb_class.type != 'hindgut_fermenter':
                    reduced_max_intake = forage.check_max_intake(
                        diet, diet_interm, herb_class, max_intake)
                    if reduced_max_intake < max_intake:
                        diet = forage.diet_selection_t2(
                            ZF, HR, f_args[u'prop_legume'], supp_available,
                            supp, reduced_max_intake, herb_class.FParam,
                            available_forage)
                diet_dict[herb_class.label] = diet
            forage.reduce_demand(diet_dict, stocking_density_dict,
                                 available_forage)
            if f_args['diet_verbose']:
                # save diet_dict across steps to be written out later
                master_diet_dict[step] = diet_dict
                # diet_segregation = forage.calc_diet_segregation(diet_dict)
                # diet_segregation_dict['step'].append(step)
                # diet_segregation_dict['segregation'].append(diet_segregation)
            total_intake_step = forage.calc_total_intake(
                diet_dict, stocking_density_dict)
            if (total_biomass - total_intake_step) < threshold_biomass:
                print "Forage consumed violates management threshold"
                threshold_exceeded = 1
                total_intake_step = 0
            for herb_class in herbivore_list:
                if threshold_exceeded:
                    diet_dict[herb_class.label] = forage.Diet()
                diet = diet_dict[herb_class.label]
                # if herb_class.type != 'hindgut_fermenter':
                diet_interm = forage.calc_diet_intermediates(
                    diet, supp, herb_class, site, f_args[u'prop_legume'],
                    f_args[u'DOY'])
                if herb_class.sex == 'lac_female':
                    milk_production = forage.check_milk_production(
                        herb_class.FParam, diet_interm)
                    milk_kg_day = herb_class.calc_milk_yield(milk_production)
                if threshold_exceeded:
                    delta_W = -(forage.convert_step_to_daily(herb_class.W))
                else:
                    delta_W = forage.calc_delta_weight(diet_interm, herb_class)
                delta_W_step = forage.convert_daily_to_step(delta_W)
                herb_class.update(delta_weight=delta_W_step,
                                  delta_time=forage.find_days_per_step())

                results_dict[herb_class.label + '_kg'].append(herb_class.W)
                results_dict[herb_class.label +
                             '_gain_kg'].append(delta_W_step)
                results_dict[herb_class.label +
                             '_intake_forage_per_indiv_kg'].append(
                                 forage.convert_daily_to_step(diet.If))
                if herb_class.sex == 'lac_female':
                    results_dict['milk_prod_kg'].append(
                        forage.convert_daily_to_step(milk_kg_day))
            results_dict['total_offtake'].append(total_intake_step)
    finally:
        if f_args['diet_verbose']:
            # df = pandas.DataFrame(diet_segregation_dict)
            # save_as = os.path.join(f_args['outdir'], 'diet_segregation.csv')
            # df.to_csv(save_as, index=False)
            for h_label in master_diet_dict[0].keys():
                new_dict = {}
                new_dict['step'] = master_diet_dict.keys()
                new_dict['DMDf'] = [
                    master_diet_dict[step][h_label].DMDf
                    for step in master_diet_dict.keys()
                ]
                new_dict['CPIf'] = [
                    master_diet_dict[step][h_label].CPIf
                    for step in master_diet_dict.keys()
                ]
                grass_labels = master_diet_dict[0][h_label].intake.keys()
                for g_label in grass_labels:
                    new_dict['intake_' + g_label] = \
                          [master_diet_dict[step][h_label].intake[g_label] for
                           step in master_diet_dict.keys()]
                df = pandas.DataFrame(new_dict)
                save_as = os.path.join(f_args['outdir'], h_label + '_diet.csv')
                df.to_csv(save_as, index=False)
        filled_dict = forage.fill_dict(results_dict, 'NA')
        df = pandas.DataFrame(filled_dict)
        df.to_csv(os.path.join(f_args['outdir'], 'summary_results.csv'))
Example #7
0
def run_simulations():
    century_dir = 'C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/CENTURY4.6/Century46_PC_Jan-2014'
    fix_file = 'drytrpfi.100'
    graz_file = os.path.join(century_dir, "graz.100")
    site_list = ['Research', 'Loidien']  #, 'Rongai', 'Kamok']
    input_dir = "C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/CENTURY4.6/Kenya/input"
    outer_dir = "C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/CENTURY4.6/Output/Stocking_density_test"
    prop_legume = 0
    template_level = 'GL'
    herb_class_weights = "C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/CENTURY4.6/Kenya/Boran_weights.csv"
    sd_dir = 'C:/Users/Ginger/Dropbox/NatCap_backup/Forage_model/CENTURY4.6/Kenya/OPC_stocking_density'
    breed = 'Boran'
    steepness = 1.
    latitude = 0
    supp_available = 0
    FParam = FreerParam.FreerParam(forage.get_general_breed(breed))
    supp = forage.Supplement(FParam, 0, 0, 0, 0, 0, 0)
    forage.set_time_step('month')
    add_event = 1

    grass_file = "C:/Users/ginge/Dropbox/NatCap_backup/Forage_model/Forage_model/model_inputs/grass.csv"
    grass = (pandas.read_csv(grass_file)).to_dict(orient='records')[0]
    grass['DMD_green'] = 0.64
    grass['DMD_dead'] = 0.64
    grass['cprotein_green'] = 0.1
    grass['cprotein_dead'] = 0.1

    for site in site_list:
        spin_up_outputs = [site + '_hist_log.txt', site + '_hist.lis']
        century_outputs = [site + '_log.txt', site + '.lis', site + '.bin']
        filename = 'average_animals_%s_2km_per_ha.csv' % site
        stocking_density_file = os.path.join(sd_dir, filename)
        sd_df = pandas.read_table(stocking_density_file, sep=',')

        outdir = os.path.join(outer_dir, site)
        if not os.path.exists(outdir):
            os.makedirs(outdir)
        # write CENTURY bat for spin-up simulation
        hist_bat = os.path.join(input_dir, (site + '_hist.bat'))
        hist_schedule = site + '_hist.sch'
        hist_output = site + '_hist'
        cent.write_century_bat(input_dir, hist_bat, hist_schedule, hist_output,
                               fix_file, 'outvars.txt')
        # write CENTURY bat for extend simulation
        extend_bat = os.path.join(input_dir, site + '.bat')
        schedule = site + '.sch'
        output = site
        extend = site + '_hist'
        cent.write_century_bat(century_dir, extend_bat, schedule, output,
                               fix_file, 'outvars.txt', extend)
        # move CENTURY run files to CENTURY dir
        site_file = os.path.join(input_dir, site + '.100')
        weather_file = os.path.join(input_dir, site + '.wth')
        e_schedule = os.path.join(input_dir, site + '.sch')
        h_schedule = os.path.join(input_dir, site + '_hist.sch')
        file_list = [
            hist_bat, extend_bat, e_schedule, h_schedule, site_file,
            weather_file
        ]
        for file in file_list:
            shutil.copyfile(file,
                            os.path.join(century_dir, os.path.basename(file)))

        # make a copy of the original graz params and schedule file
        shutil.copyfile(graz_file, os.path.join(century_dir, 'graz_orig.100'))
        label = os.path.basename(e_schedule)[:-4]
        copy_name = label + '_orig.sch'
        shutil.copyfile(e_schedule, os.path.join(input_dir, copy_name))

        # run CENTURY for spin-up up to start_year and start_month
        hist_bat = os.path.join(century_dir, site + '_hist.bat')
        century_bat = os.path.join(century_dir, site + '.bat')
        p = Popen(["cmd.exe", "/c " + hist_bat], cwd=century_dir)
        stdout, stderr = p.communicate()
        p = Popen(["cmd.exe", "/c " + century_bat], cwd=century_dir)
        stdout, stderr = p.communicate()

        # save copies of CENTURY outputs, but remove from CENTURY dir
        intermediate_dir = os.path.join(outdir, 'CENTURY_outputs_spin_up')
        if not os.path.exists(intermediate_dir):
            os.makedirs(intermediate_dir)
        to_move = century_outputs + spin_up_outputs
        for file in to_move:
            shutil.copyfile(os.path.join(century_dir, file),
                            os.path.join(intermediate_dir, file))
            os.remove(os.path.join(century_dir, file))

        grass_list = [grass]
        results_dict = {'year': [], 'month': []}
        herbivore_input = (pandas.read_csv(herb_class_weights).to_dict(
            orient='records'))
        herbivore_list = []
        for h_class in herbivore_input:
            results_dict[h_class['label'] + '_gain_kg'] = []
            results_dict[h_class['label'] + '_offtake'] = []
        results_dict['milk_prod_kg'] = []
        for grass in grass_list:
            results_dict[grass['label'] + '_green_kgha'] = []
            results_dict[grass['label'] + '_dead_kgha'] = []
        results_dict['total_offtake'] = []
        results_dict['stocking_density'] = []
        try:
            for row in xrange(len(sd_df)):
                herbivore_list = []
                for h_class in herbivore_input:
                    herd = forage.HerbivoreClass(FParam,
                                                 breed,
                                                 h_class['weight'],
                                                 h_class['sex'],
                                                 h_class['age'],
                                                 h_class['stocking_density'],
                                                 h_class['label'],
                                                 Wbirth=24)
                    herd.update(FParam, 0, 0)
                    herbivore_list.append(herd)
                for h_class in herbivore_list:
                    h_class.stocking_density = sd_df.iloc[row][h_class.label]
                total_SD = forage.calc_total_stocking_density(herbivore_list)
                results_dict['stocking_density'].append(total_SD)
                siteinfo = forage.SiteInfo(total_SD, steepness, latitude)
                month = sd_df.iloc[row].month
                year = sd_df.iloc[row].year
                suf = '%d-%d' % (month, year)
                DOY = month * 30
                # get biomass and crude protein for each grass type from CENTURY
                output_file = os.path.join(intermediate_dir, site + '.lis')
                outputs = cent.read_CENTURY_outputs(output_file, year,
                                                    year + 2)
                target_month = cent.find_prev_month(year, month)
                grass['prev_g_gm2'] = grass['green_gm2']
                grass['prev_d_gm2'] = grass['dead_gm2']
                grass['green_gm2'] = outputs.loc[target_month, 'aglivc']
                grass['dead_gm2'] = outputs.loc[target_month, 'stdedc']
                grass['cprotein_green'] = (
                    outputs.loc[target_month, 'aglive1'] /
                    outputs.loc[target_month, 'aglivc'])
                grass['cprotein_dead'] = (
                    outputs.loc[target_month, 'stdede1'] /
                    outputs.loc[target_month, 'stdedc'])
                if row == 0:
                    available_forage = forage.calc_feed_types(grass_list)
                else:
                    available_forage = forage.update_feed_types(
                        grass_list, available_forage)
                results_dict['year'].append(year)
                results_dict['month'].append(month)
                for feed_type in available_forage:
                    results_dict[feed_type.label + '_' +
                                 feed_type.green_or_dead + '_kgha'].append(
                                     feed_type.biomass)

                siteinfo.calc_distance_walked(FParam, available_forage)
                for feed_type in available_forage:
                    feed_type.calc_digestibility_from_protein()
                total_biomass = forage.calc_total_biomass(available_forage)
                # Initialize containers to track forage consumed across herbivore
                # classes
                total_intake_step = 0.
                total_consumed = {}
                for feed_type in available_forage:
                    label_string = ';'.join(
                        [feed_type.label, feed_type.green_or_dead])
                    total_consumed[label_string] = 0.

                for herb_class in herbivore_list:
                    max_intake = herb_class.calc_max_intake(FParam)
                    if herb_class.Z < FParam.CR7:
                        ZF = 1. + (FParam.CR7 - herb_class.Z)
                    else:
                        ZF = 1.
                    if herb_class.stocking_density > 0:
                        adj_forage = forage.calc_adj_availability(
                            available_forage, herb_class.stocking_density)
                    else:
                        adj_forage = list(available_forage)
                    diet = forage.diet_selection_t2(ZF, prop_legume,
                                                    supp_available, supp,
                                                    max_intake, FParam,
                                                    adj_forage)
                    diet_interm = forage.calc_diet_intermediates(
                        FParam, diet, supp, herb_class, siteinfo, prop_legume,
                        DOY)
                    reduced_max_intake = forage.check_max_intake(
                        FParam, diet, diet_interm, herb_class, max_intake)
                    if reduced_max_intake < max_intake:
                        diet = forage.diet_selection_t2(
                            ZF, prop_legume, supp_available, supp,
                            reduced_max_intake, FParam, adj_forage)
                        diet_interm = forage.calc_diet_intermediates(
                            FParam, diet, supp, herb_class, siteinfo,
                            prop_legume, DOY)
                    total_intake_step += (
                        forage.convert_daily_to_step(diet.If) *
                        herb_class.stocking_density)
                    if herb_class.sex == 'lac_female':
                        milk_production = forage.check_milk_production(
                            FParam, diet_interm)
                        milk_kg_day = forage.calc_milk_yield(
                            FParam, milk_production)
                    delta_W = forage.calc_delta_weight(FParam, diet,
                                                       diet_interm, supp,
                                                       herb_class)
                    delta_W_step = forage.convert_daily_to_step(delta_W)
                    herb_class.update(FParam, delta_W_step,
                                      forage.find_days_per_step())
                    if herb_class.stocking_density > 0:
                        results_dict[herb_class.label +
                                     '_gain_kg'].append(delta_W_step)
                        results_dict[herb_class.label + '_offtake'].append(
                            diet.If)
                    else:
                        results_dict[herb_class.label +
                                     '_gain_kg'].append('NA')
                        results_dict[herb_class.label +
                                     '_offtake'].append('NA')
                    if herb_class.sex == 'lac_female':
                        results_dict['milk_prod_kg'].append(milk_kg_day * 30.)

                    # after have performed max intake check, we have the final diet
                    # selected
                    # calculate percent live and dead removed for each grass type
                    consumed_by_class = forage.calc_percent_consumed(
                        available_forage, diet, herb_class.stocking_density)
                    forage.sum_percent_consumed(total_consumed,
                                                consumed_by_class)

                results_dict['total_offtake'].append(total_intake_step)
                # send to CENTURY for this month's scheduled grazing event
                date = year + float('%.2f' % (month / 12.))
                schedule = os.path.join(century_dir, site + '.sch')
                target_dict = cent.find_target_month(add_event, schedule, date,
                                                     1)
                if target_dict == 0:
                    er = "Error: no opportunities exist to add grazing event"
                    raise Exception(er)
                new_code = cent.add_new_graz_level(grass, total_consumed,
                                                   graz_file, template_level,
                                                   outdir, suf)
                cent.modify_schedule(schedule, add_event, target_dict,
                                     new_code, outdir, suf)

                # call CENTURY from the batch file
                century_bat = os.path.join(century_dir, site + '.bat')
                p = Popen(["cmd.exe", "/c " + century_bat], cwd=century_dir)
                stdout, stderr = p.communicate()
                # save copies of CENTURY outputs, but remove from CENTURY dir
                intermediate_dir = os.path.join(
                    outdir, 'CENTURY_outputs_m%d_y%d' % (month, year))
                if not os.path.exists(intermediate_dir):
                    os.makedirs(intermediate_dir)
                for file in century_outputs:
                    shutil.copyfile(os.path.join(century_dir, file),
                                    os.path.join(intermediate_dir, file))
                    os.remove(os.path.join(century_dir, file))

        # remove files from CENTURY directory
        finally:
            # replace graz params used by CENTURY with original file
            os.remove(graz_file)
            shutil.copyfile(os.path.join(century_dir, 'graz_orig.100'),
                            graz_file)
            os.remove(os.path.join(century_dir, 'graz_orig.100'))
            files_to_remove = [
                os.path.join(century_dir, os.path.basename(f))
                for f in file_list
            ]
            for file in files_to_remove:
                os.remove(file)
            os.remove(os.path.join(century_dir, site + '_hist.bin'))
            filled_dict = forage.fill_dict(results_dict, 'NA')
            df = pandas.DataFrame(filled_dict)
            df.to_csv(os.path.join(outdir, 'summary_results.csv'))
def one_step(site,
             DOY,
             herb_class,
             available_forage,
             prop_legume,
             supp_available,
             supp,
             intake=None,
             force_supp=None):
    """One step of the forage model, if available forage does not change."""

    row = []
    row.append(supp.DMO)
    herb_class.calc_distance_walked(herb_class.stocking_density, site.S,
                                    available_forage)
    max_intake = herb_class.calc_max_intake()

    ZF = herb_class.calc_ZF()
    HR = forage.calc_relative_height(available_forage)
    if intake is not None:  # if forage intake should be forced
        diet = forage.Diet()
        diet.If = intake
        diet.DMDf = available_forage[0].digestibility  # this is super hack-y
        diet.CPIf = intake * available_forage[
            0].crude_protein  # and only works with one type of available forage
        diet.Is = supp.DMO  # also force intake of all supplement offered
        diet_interm = forage.calc_diet_intermediates(diet, supp, herb_class,
                                                     site, prop_legume, DOY)
        row.append('NA')
    else:
        diet = forage.diet_selection_t2(ZF, HR, prop_legume, supp_available,
                                        supp, max_intake, herb_class.FParam,
                                        available_forage, force_supp)
        diet_interm = forage.calc_diet_intermediates(diet, supp, herb_class,
                                                     site, prop_legume, DOY)
        if herb_class.type != 'hindgut_fermenter':
            reduced_max_intake = forage.check_max_intake(
                diet, diet_interm, herb_class, max_intake)
            row.append(reduced_max_intake)
            if reduced_max_intake < max_intake:
                diet = forage.diet_selection_t2(ZF, HR, prop_legume,
                                                supp_available, supp,
                                                reduced_max_intake,
                                                herb_class.FParam,
                                                available_forage, force_supp)
                diet_interm = forage.calc_diet_intermediates(
                    diet, supp, herb_class, site, prop_legume, DOY)
    delta_W = forage.calc_delta_weight(diet_interm, herb_class)
    delta_W_step = forage.convert_daily_to_step(delta_W)
    herd_t1 = forage.HerdT1(herb_class.W, 297)
    maint_t1 = herd_t1.e_maintenance()
    delta_W_t1 = herd_t1.e_allocate(diet_interm.MEItotal, maint_t1, 'moderate')
    herb_class.update(delta_weight=delta_W_step,
                      delta_time=forage.find_days_per_step())

    row.append(max_intake)
    row.append(diet.If)
    row.append(diet.Is)
    row.append(diet.CPIf)
    row.append(diet_interm.MEItotal)
    row.append(delta_W)
    row.append(delta_W_t1)
    return row