Exemple #1
0
 def test_timestamp_to_iso_8601_timestamp_list(self):
     with NetCDFData("tests/testdata/nemo_test.nc") as nc_data:
         result = nc_data.timestamp_to_iso_8601([2031436800, 2034072000])
         expected = [
             cftime.real_datetime(2014, 5, 17, tzinfo=pytz.UTC),
             cftime.real_datetime(2014, 6, 16, 12, tzinfo=pytz.UTC),
         ]
         self.assertEqual(result, expected)
Exemple #2
0
    def run_caete(self, start_date, end_date, spinup=0, fix_co2=None):
        """ start_date [str]   "yyyymmdd" Start model execution

            end_date   [str]   "yyyymmdd" End model execution

            spinup     [int]   Number of repetitions in spinup. 0 for no spinup

            fix_co2    [Float] Fixed value for ATM [CO2]
                       [int]   Fixed value for ATM [CO2]
                       [str]   "yyyy" Corresponding year of an ATM [CO2]

            This function run the fortran subroutines and manage data flux. It
            is the proper CAETÊ-DVM execution in the start_date - end_date period
        """

        assert self.filled, "The gridcell has no input data"
        assert not fix_co2 or type(
            fix_co2
        ) == str or fix_co2 > 0, "A fixed value for ATM[CO2] must be a positive number greater than zero or a proper string "

        def find_co2(year):
            for i in self.co2_data:
                if int(i.split('\t')[0]) == year:
                    return float(i.split('\t')[1].strip())

        def find_index(start, end):
            result = []
            num = np.arange(self.ssize)
            ind = np.arange(self.sind, self.eind + 1)
            for r, i in zip(num, ind):
                if i == start:
                    result.append(r)
            for r, i in zip(num, ind):
                if i == end:
                    result.append(r)
            return result

        # Define start and end dates (read actual arguments)
        start = cftime.real_datetime(int(start_date[:4]), int(start_date[4:6]),
                                     int(start_date[6:]))
        end = cftime.real_datetime(int(end_date[:4]), int(end_date[4:6]),
                                   int(end_date[6:]))
        # Check dates sanity
        assert start < end, "start > end"
        assert start >= self.start_date
        assert end <= self.end_date

        # Define time index
        start_index = int(cftime.date2num(start, self.time_unit,
                                          self.calendar))
        end_index = int(cftime.date2num(end, self.time_unit, self.calendar))

        lb, hb = find_index(start_index, end_index)
        steps = np.arange(lb, hb + 1)
        day_indexes = np.arange(start_index, end_index + 1)
        spin = 1 if spinup == 0 else spinup

        # Catch climatic input and make conversions
        temp = self.tas[lb:hb + 1] - 273.15  # ! K to °C
        prec = self.pr[lb:hb + 1] * 86400  # kg m-2 s-1 to  mm/day
        # transforamando de Pascal pra mbar (hPa)
        p_atm = self.ps[lb:hb + 1] * 0.01
        # W m-2 to mol m-2 s-1 ! 0.5 converts RSDS to PAR
        ipar = self.rsds[lb:hb + 1] * 0.5 / 2.18e5
        ru = self.rhs[lb:hb + 1] / 100.0

        year0 = start.year
        co2 = find_co2(year0)
        count_days = start.dayofyr - 2
        loop = 0
        next_year = 0.0

        fix_co2_p = False
        if fix_co2 is None:
            fix_co2_p = False
        elif type(fix_co2) == int or type(fix_co2) == float:
            co2 = fix_co2
            fix_co2_p = True
        elif type(fix_co2) == str:
            assert type(
                int(fix_co2)
            ) == int, "The string(\"yyyy\") for the fix_co2 argument must be an year between 1901-2016"
            co2 = find_co2(int(fix_co2))
            fix_co2_p = True

        for s in range(spin):
            self._allocate_output(steps.size)
            for step in range(steps.size):
                if fix_co2_p:
                    pass
                else:
                    loop += 1
                    count_days += 1
                    # CAST CO2 ATM CONCENTRATION
                    days = 366 if m.leap(year0) == 1 else 365
                    if count_days == days:
                        count_days = 0
                        year0 = cftime.num2date(day_indexes[step],
                                                self.time_unit,
                                                self.calendar).year
                        co2 = find_co2(year0)
                        next_year = (find_co2(year0 + 1) - co2) / days

                    elif loop == 1 and count_days < days:
                        year0 = start.year
                        next_year = (find_co2(year0 + 1) - co2) / \
                            (days - count_days)

                    co2 += next_year
                # if step == 0:
                #     #print('1st day')
                # Update soil temperature
                self.soil_temp = st.soil_temp(self.soil_temp, temp[step])

                # INFLATe VARS
                sto = np.zeros(shape=(3, npls), order='F')
                cleaf = np.zeros(npls, order='F')
                cwood = np.zeros(npls, order='F')
                croot = np.zeros(npls, order='F')
                dcl = np.zeros(npls, order='F')
                dca = np.zeros(npls, order='F')
                dcf = np.zeros(npls, order='F')
                uptk_costs = np.zeros(npls, order='F')

                sto[0, self.vp_lsid] = self.vp_sto[0, :]
                sto[1, self.vp_lsid] = self.vp_sto[1, :]
                sto[2, self.vp_lsid] = self.vp_sto[2, :]
                # Just Check the integrity of the data
                assert self.vp_lsid.size == self.vp_cleaf.size, 'different shapes'
                c = 0
                for n in self.vp_lsid:
                    cleaf[n] = self.vp_cleaf[c]
                    cwood[n] = self.vp_cwood[c]
                    croot[n] = self.vp_croot[c]
                    dcl[n] = self.vp_dcl[c]
                    dca[n] = self.vp_dca[c]
                    dcf[n] = self.vp_dcf[c]
                    uptk_costs[n] = self.sp_uptk_costs[c]
                    c += 1
                ton = self.sp_organic_n + self.sp_sorganic_n
                top = self.sp_organic_p + self.sp_sorganic_p
                out = model.daily_budget(
                    self.pls_table, self.wp_water_upper_mm,
                    self.wp_water_lower_mm, self.soil_temp, temp[step],
                    p_atm[step], ipar[step], ru[step], self.sp_available_n,
                    self.sp_available_p, ton, top, self.sp_organic_p, co2, sto,
                    cleaf, cwood, croot, dcl, dca, dcf, uptk_costs,
                    self.wmax_mm, step)

                del sto, cleaf, cwood, croot, dcl, dca, dcf, uptk_costs
                # Create a dict with the function output
                daily_output = catch_out_budget(out)

                self.vp_lsid = np.where(daily_output['ocpavg'] > 0.0)[0]
                self.vp_ocp = daily_output['ocpavg'][self.vp_lsid]
                self.ls[step] = self.vp_lsid.size

                # UPDATE STATE VARIABLES
                # WATER CWM
                self.runom[step] = self._update_pool(prec[step],
                                                     daily_output['evavg'])

                # UPDATE vegetation pools ! ABLE TO USE SPARSE MATRICES
                self.vp_cleaf = daily_output['cleafavg_pft'][self.vp_lsid]
                self.vp_cwood = daily_output['cawoodavg_pft'][self.vp_lsid]
                self.vp_croot = daily_output['cfrootavg_pft'][self.vp_lsid]
                self.vp_dcl = daily_output['delta_cveg'][0][self.vp_lsid]
                self.vp_dca = daily_output['delta_cveg'][1][self.vp_lsid]
                self.vp_dcf = daily_output['delta_cveg'][2][self.vp_lsid]
                self.vp_sto = daily_output['stodbg'][:, self.vp_lsid]
                self.sp_uptk_costs = daily_output['npp2pay'][self.vp_lsid]

                # Plant uptake and Carbon costs of nutrient uptake
                self.nupt[:, step] = daily_output['nupt']
                self.pupt[:, step] = daily_output['pupt']
                for i in range(3):
                    self.storage_pool[i, step] = np.sum(self.vp_ocp *
                                                        self.vp_sto[i])

                # OUTPUTS for SOIL CWM
                self.litter_l[step] = daily_output['litter_l']
                self.cwd[step] = daily_output['cwd']
                self.litter_fr[step] = daily_output['litter_fr']
                self.lnc[:, step] = daily_output['lnc']
                wtot = self.wp_water_upper_mm + self.wp_water_lower_mm
                s_out = soil_dec.carbon3(self.soil_temp, wtot / self.wmax_mm,
                                         self.litter_l[step], self.cwd[step],
                                         self.litter_fr[step], self.lnc[:,
                                                                        step],
                                         self.sp_csoil, self.sp_snc)

                soil_out = catch_out_carbon3(s_out)

                # Organic C N & P
                self.sp_csoil = soil_out['cs']
                self.sp_snc = soil_out['snc']

                # INCLUDE MINERALIZED NUTRIENTS
                self.sp_available_p += soil_out['pmin']
                self.sp_available_n += soil_out['nmin']

                # NUTRIENT DINAMICS
                # Inorganic N
                self.sp_in_n += self.sp_available_n + self.sp_so_n
                self.sp_so_n = soil_dec.sorbed_n_equil(self.sp_in_n)
                self.sp_available_n = soil_dec.solution_n_equil(self.sp_in_n)
                self.sp_in_n -= self.sp_so_n + self.sp_available_n

                # Inorganic P
                self.sp_in_p += self.sp_available_p + self.sp_so_p
                self.sp_so_p = soil_dec.sorbed_p_equil(self.sp_in_p)
                self.sp_available_p = soil_dec.solution_p_equil(self.sp_in_p)
                self.sp_in_p -= self.sp_so_p + self.sp_available_p

                # Sorbed P
                if self.pupt[1, step] > 0.05:
                    rwarn(
                        f"Puptk_SO > soP_max - 729 | in spin{s}, step{step} - {self.pupt[1, step]}"
                    )
                    self.pupt[1, step] = 0.0

                if self.pupt[1, step] > self.sp_so_p:
                    rwarn(
                        f"Puptk_SO > soP_pool - 731 | in spin{s}, step{step} - {self.pupt[1, step]}"
                    )

                self.sp_so_p -= self.pupt[1, step]

                t1 = np.all(self.sp_snc > 0.0)
                if not t1:
                    self.snc[np.where(self.snc < 0)] = 0.0
                # ORGANIC nutrients uptake
                # N
                if self.nupt[1, step] < 0.0:
                    rwarn(
                        f"NuptkO < 0 - 745 | in spin{s}, step{step} - {self.nupt[1, step]}"
                    )
                    self.nupt[1, step] = 0.0
                if self.nupt[1, step] > 0.8:
                    rwarn(
                        f"NuptkO  > max - 749 | in spin{s}, step{step} - {self.nupt[1, step]}"
                    )
                    self.nupt[1, step] = 0.0

                total_on = self.sp_snc[:4].sum()
                frsn = [i / total_on for i in self.sp_snc[:4]]
                for i, fr in enumerate(frsn):
                    self.sp_snc[i] -= self.nupt[1, step] * fr
                self.sp_organic_n = self.sp_snc[:2].sum()
                self.sp_sorganic_n = self.sp_snc[2:4].sum()

                # P
                if self.pupt[2, step] < 0.0:
                    rwarn(
                        f"PuptkO < 0 - 759 | in spin{s}, step{step} - {self.pupt[2, step]}"
                    )
                    self.pupt[2, step] = 0.0
                if self.pupt[2, step] > 0.1:
                    rwarn(
                        f"PuptkO  < max - 763 | in spin{s}, step{step} - {self.pupt[2, step]}"
                    )
                    self.pupt[2, step] = 0.0
                total_op = self.sp_snc[4:].sum()
                frsp = [i / total_op for i in self.sp_snc[4:]]
                for i, fr in enumerate(frsp):
                    self.sp_snc[i + 4] -= self.pupt[2, step] * fr
                self.sp_organic_p = self.sp_snc[4:6].sum()
                self.sp_sorganic_p = self.sp_snc[6:].sum()

                # Raise some warnings
                if self.sp_organic_n < 0.0:
                    rwarn(f"ON negative in spin{s}, step{step}")
                if self.sp_sorganic_n < 0.0:
                    rwarn(f"SON negative in spin{s}, step{step}")
                if self.sp_organic_p < 0.0:
                    rwarn(f"OP negative in spin{s}, step{step}")
                if self.sp_sorganic_p < 0.0:
                    rwarn(f"SOP negative in spin{s}, step{step}")

                # CALCULATE THE EQUILIBTIUM IN SOIL POOLS
                # Soluble and inorganic pools
                if self.pupt[0, step] > 2.5:
                    rwarn(
                        f"Puptk > max - 786 | in spin{s}, step{step} - {self.pupt[0, step]}"
                    )
                    self.pupt[0, step] = 0.0
                self.sp_available_p -= self.pupt[0, step]

                if self.nupt[0, step] > 2.5:
                    rwarn(
                        f"Nuptk > max - 792 | in spin{s}, step{step} - {self.nupt[0, step]}"
                    )
                    self.nupt[0, step] = 0.0
                self.sp_available_n -= self.nupt[0, step]

                # END SOIL NUTRIENT DYNAMICS

                # # # Process (cwm) & store (np.array) outputs
                self.carbon_costs[self.vp_lsid, step] = self.sp_uptk_costs
                self.emaxm.append(daily_output['epavg'])
                self.tsoil.append(self.soil_temp)
                self.photo[step] = daily_output['phavg']
                self.aresp[step] = daily_output['aravg']
                self.npp[step] = daily_output['nppavg']
                self.lai[step] = daily_output['laiavg']
                self.rcm[step] = daily_output['rcavg']
                self.f5[step] = daily_output['f5avg']
                self.evapm[step] = daily_output['evavg']
                self.wsoil[step] = self.wp_water_upper_mm
                self.swsoil[step] = self.wp_water_lower_mm
                self.rm[step] = daily_output['rmavg']
                self.rg[step] = daily_output['rgavg']
                self.wue[step] = daily_output['wueavg']
                self.cue[step] = daily_output['cueavg']
                self.cdef[step] = daily_output['c_defavg']
                self.vcmax[step] = daily_output['vcmax']
                self.specific_la[step] = daily_output['specific_la']
                self.cleaf[step] = daily_output['cp'][0]
                self.cawood[step] = daily_output['cp'][1]
                self.cfroot[step] = daily_output['cp'][2]
                self.hresp[step] = soil_out['hr']
                self.csoil[:, step] = soil_out['cs']
                self.inorg_n[step] = self.sp_in_n
                self.inorg_p[step] = self.sp_in_p
                self.sorbed_n[step] = self.sp_so_n
                self.sorbed_p[step] = self.sp_so_p
                self.snc[:, step] = soil_out['snc']
                self.nmin[step] = self.sp_available_n
                self.pmin[step] = self.sp_available_p
                self.area[self.vp_lsid, step] = self.vp_ocp
                self.lim_status[:, self.vp_lsid, step] = daily_output[
                    'limitation_status'][:, self.vp_lsid]
                self.uptake_strategy[:, self.vp_lsid, step] = daily_output[
                    'uptk_strat'][:, self.vp_lsid]

            if s > 0:
                while True:
                    if sv.is_alive():
                        sleep(0.5)
                    else:
                        break

            self.flush_data = self._flush_output('spin',
                                                 (start_index, end_index))
            sv = Thread(target=self._save_output, args=(self.flush_data, ))
            sv.start()

        while True:
            if sv.is_alive():
                sleep(0.5)
            else:
                break
        return None
Exemple #3
0
    def bdg_spinup(self, start_date='19010101', end_date='19030101'):
        """SPINUP SOIL POOLS - generate soil OM and Organic nutrients inputs for soil spinup
        - Side effect - Start soil water pools pools """

        assert self.filled, "The gridcell has no input data"
        self.budget_spinup = True

        def find_co2(year):
            for i in self.co2_data:
                if int(i.split('\t')[0]) == year:
                    return float(i.split('\t')[1].strip())

        def find_index(start, end):
            result = []
            num = np.arange(self.ssize)
            ind = np.arange(self.sind, self.eind + 1)
            for r, i in zip(num, ind):
                if i == start:
                    result.append(r)
            for r, i in zip(num, ind):
                if i == end:
                    result.append(r)
            return result

        # Define start and end dates
        start = cftime.real_datetime(int(start_date[:4]), int(start_date[4:6]),
                                     int(start_date[6:]))
        end = cftime.real_datetime(int(end_date[:4]), int(end_date[4:6]),
                                   int(end_date[6:]))
        # Check dates sanity
        assert start < end, "start > end"
        assert start >= self.start_date
        assert end <= self.end_date

        # Define time index
        start_index = int(cftime.date2num(start, self.time_unit,
                                          self.calendar))
        end_index = int(cftime.date2num(end, self.time_unit, self.calendar))

        lb, hb = find_index(start_index, end_index)
        steps = np.arange(lb, hb + 1)
        day_indexes = np.arange(start_index, end_index + 1)

        # Catch climatic input and make conversions
        temp = self.tas[lb:hb + 1] - 273.15  # ! K to °C
        prec = self.pr[lb:hb + 1] * 86400  # kg m-2 s-1 to  mm/day
        # transforamando de Pascal pra mbar (hPa)
        p_atm = self.ps[lb:hb + 1] * 0.01
        # W m-2 to mol m-2 s-1 ! 0.5 converts RSDS to PAR
        ipar = self.rsds[lb:hb + 1] * 0.5 / 2.18e5
        ru = self.rhs[lb:hb + 1] / 100.0

        year0 = start.year
        co2 = find_co2(year0)
        count_days = start.dayofyr - 2
        loop = 0
        next_year = 0
        wo = []
        llo = []
        cwdo = []
        rlo = []
        lnco = []

        sto = self.vp_sto
        cleaf = self.vp_cleaf
        cwood = self.vp_cwood
        croot = self.vp_croot
        dcl = self.vp_dcl
        dca = self.vp_dca
        dcf = self.vp_dcf
        uptk_costs = np.zeros(npls, order='F')

        for step in range(steps.size):
            loop += 1
            count_days += 1
            # CAST CO2 ATM CONCENTRATION
            days = 366 if m.leap(year0) == 1 else 365
            if count_days == days:
                count_days = 0
                year0 = cftime.num2date(day_indexes[step], self.time_unit,
                                        self.calendar).year
                co2 = find_co2(year0)
                next_year = (find_co2(year0 + 1) - co2) / days

            elif loop == 1 and count_days < days:
                year0 = start.year
                next_year = (find_co2(year0 + 1) - co2) / \
                    (days - count_days)

            co2 += next_year
            self.soil_temp = st.soil_temp(self.soil_temp, temp[step])

            out = model.daily_budget(
                self.pls_table, self.wp_water_upper_mm, self.wp_water_lower_mm,
                self.soil_temp, temp[step], p_atm[step], ipar[step], ru[step],
                self.sp_available_n, self.sp_available_p,
                self.sp_snc[:4].sum(), self.sp_so_p, self.sp_snc[4:].sum(),
                co2, sto, cleaf, cwood, croot, dcl, dca, dcf, uptk_costs,
                self.wmax_mm, step)

            # Create a dict with the function output
            daily_output = catch_out_budget(out)
            runoff = self._update_pool(prec[step], daily_output['evavg'])

            # UPDATE vegetation pools

            wo.append(
                np.float64(self.wp_water_upper_mm + self.wp_water_lower_mm))
            llo.append(daily_output['litter_l'])
            cwdo.append(daily_output['cwd'])
            rlo.append(daily_output['litter_fr'])
            lnco.append(daily_output['lnc'])

        f = np.array

        def x(a):
            return a * 0.75

        return x(f(wo).mean()), x(f(llo).mean()), x(f(cwdo).mean()), x(
            f(rlo).mean()), x(f(lnco).mean(axis=0, ))
Exemple #4
0
def hovmoller_plot(data,
                   time,
                   grid,
                   ax=None,
                   make_cbar=True,
                   ctype='basic',
                   vmin=None,
                   vmax=None,
                   zmin=None,
                   zmax=None,
                   monthly=True,
                   contours=None,
                   title=None,
                   titlesize=18,
                   return_fig=False,
                   fig_name=None,
                   extend=None,
                   figsize=(14, 5),
                   dpi=None):

    # Choose what the endpoints of the colourbar should do
    if extend is None:
        extend = get_extend(vmin=vmin, vmax=vmax)

    # If we're zooming, we need to choose the correct colour bounds
    if any([zmin, zmax]):
        vmin_tmp, vmax_tmp = var_min_max_zt(data, grid, zmin=zmin, zmax=zmax)
        if vmin is None:
            vmin = vmin_tmp
        if vmax is None:
            vmax = vmax_tmp
    # Get colourmap
    cmap, vmin, vmax = set_colours(data, ctype=ctype, vmin=vmin, vmax=vmax)

    if monthly:
        # As in netcdf_time, the time axis will have been corrected so it is
        # marked with the beginning of each month. So to get the boundaries of
        # each time index, we just need to add one month to the end.
        if time[-1].month == 12:
            end_time = datetime.datetime(time[-1].year + 1, 1, 1)
        else:
            end_time = datetime.datetime(time[-1].year, time[-1].month + 1, 1)
        time_edges = np.concatenate((time, [end_time]))
    else:
        # Following MITgcm convention, the time axis will be stamped with the
        # first day of the next averaging period. So to get the boundaries of
        # each time index, we just need to extrapolate to the beginning,
        # assuming regularly spaced time intervals.
        dt = time[1] - time[0]
        start_time = time[0] - dt
        time_edges = np.concatenate(([start_time], time))
    # Update for versions of pcolormesh that don't support date axis:
    time_flt = [(t - time_edges[0]).total_seconds() for t in time_edges]
    # Ticks at each year
    xtick_years = np.sort(list(set([t.year for t in time_edges])))
    xtick_loc = [
        (cftime.real_datetime(year, 1, 1) - time_edges[0]).total_seconds()
        for year in xtick_years
    ]
    xtick_labels = [str(year) for year in xtick_years]
    time_edges = np.array(time_flt)

    # Make the figure and axes, if needed
    existing_ax = ax is not None
    if not existing_ax:
        fig, ax = plt.subplots(figsize=figsize)

    # Plot the data
    img = ax.pcolormesh(time_edges,
                        grid.z_edges,
                        np.transpose(data),
                        cmap=cmap,
                        vmin=vmin,
                        vmax=vmax)
    ax.set_xticks(xtick_loc)
    ax.set_xticklabels(xtick_labels)
    if contours is not None:
        # Overlay contours
        # Need time at the centres of each index
        # Have to do this with a loop unfortunately
        time_centres = []
        for t in range(time_edges.size - 1):
            dt = (time_edges[t + 1] - time_edges[t]) / 2
            time_centres.append(time_edges[t] + dt)
        plt.contour(time_centres,
                    grid.z,
                    np.transpose(data),
                    levels=contours,
                    colors='black',
                    linestyles='solid')

    # Set depth limits
    if zmin is None:
        # Index of last masked cell
        k_bottom = np.argwhere(np.invert(data[0, :].mask))[-1][0]
        zmin = grid.z_edges[k_bottom + 1]
    if zmax is None:
        # Index of first unmasked cell
        k_top = np.argwhere(np.invert(data[0, :].mask))[0][0]
        zmax = grid.z_edges[k_top]
    ax.set_ylim([zmin, zmax])
    # Make nice axes labels
    depth_axis(ax)
    if make_cbar:
        # Add a colourbar
        plt.colorbar(img, extend=extend)
    if title is not None:
        # Add a title
        plt.title(title, fontsize=titlesize)

    if return_fig:
        return fig, ax
    elif existing_ax:
        return img
    else:
        finished_plot(fig, fig_name=fig_name, dpi=dpi)
 def test_get_time_offset(self):
     """Test time unit."""
     result = _get_time_offset("days since 1950-01-01")
     expected = cftime.real_datetime(1950, 1, 1, 0, 0)
     np.testing.assert_equal(result, expected)
Exemple #6
0
 def test_timestamp_to_iso_8601_int_timestamp(self):
     with NetCDFData("tests/testdata/nemo_test.nc") as nc_data:
         result = nc_data.timestamp_to_iso_8601(2031436800)
         self.assertEqual(result, cftime.real_datetime(2014, 5, 17, tzinfo=pytz.UTC))
    def bdg_spinup(self, start_date='19010101', end_date='19030101'):
        """SPINUP VEGETATION"""

        assert self.filled, "The gridcell has no input data"
        self.budget_spinup = True

        def find_co2(year):
            for i in self.co2_data:
                if int(i.split('\t')[0]) == year:
                    return float(i.split('\t')[1].strip())

        def find_index(start, end):
            result = []
            num = np.arange(self.ssize)
            ind = np.arange(self.sind, self.eind + 1)
            for r, i in zip(num, ind):
                if i == start:
                    result.append(r)
            for r, i in zip(num, ind):
                if i == end:
                    result.append(r)
            return result

        # Define start and end dates
        start = cftime.real_datetime(int(start_date[:4]), int(start_date[4:6]),
                                     int(start_date[6:]))
        end = cftime.real_datetime(int(end_date[:4]), int(end_date[4:6]),
                                   int(end_date[6:]))
        # Check dates sanity
        assert start < end, "start > end"
        assert start >= self.start_date
        assert end <= self.end_date

        # Define time index
        start_index = int(cftime.date2num(start, self.time_unit,
                                          self.calendar))
        end_index = int(cftime.date2num(end, self.time_unit, self.calendar))

        lb, hb = find_index(start_index, end_index)
        steps = np.arange(lb, hb + 1)
        day_indexes = np.arange(start_index, end_index + 1)

        # Catch climatic input and make conversions
        temp = self.tas[lb:hb + 1] - 273.15  # ! K to °C
        prec = self.pr[lb:hb + 1] * 86400  # kg m-2 s-1 to  mm/day
        # transforamando de Pascal pra mbar (hPa)
        p_atm = self.ps[lb:hb + 1] * 0.01
        # W m-2 to mol m-2 s-1 ! 0.5 converts RSDS to PAR
        ipar = self.rsds[lb:hb + 1] * 0.5 / 2.18e5
        ru = self.rhs[lb:hb + 1] / 100.0

        year0 = start.year
        co2 = find_co2(year0)
        count_days = start.dayofyr - 2
        loop = 0
        next_year = 0
        wo = []
        llo = []
        cwdo = []
        rlo = []
        lnco = []

        sto = self.vp_sto
        cleaf = self.vp_cleaf
        cwood = self.vp_cwood
        croot = self.vp_croot
        csap = self.vp_csap
        cheart = self.vp_cheart
        dcl = self.vp_dcl
        dca = self.vp_dca
        dcf = self.vp_dcf
        uptk_costs = np.zeros(npls, order='F')

        for step in range(steps.size):
            loop += 1
            count_days += 1
            # CAST CO2 ATM CONCENTRATION
            days = 366 if utl.leap(year0) == 1 else 365
            if count_days == days:
                count_days = 0
                year0 = cftime.num2date(day_indexes[step], self.time_unit,
                                        self.calendar).year
                co2 = find_co2(year0)
                next_year = (find_co2(year0 + 1) - co2) / days

            elif loop == 1 and count_days < days:
                year0 = start.year
                next_year = (find_co2(year0 + 1) - co2) / \
                    (days - count_days)

            co2 += next_year
            if step == lb:  #first day (csap and cheart just for initialization)
                cleaf = cleaf
                cwood = cwood
                croot = croot
                csap = cwood * 0.05
                cheart = cwood * 0.95
                print('lb0', 'csap 1=', csap, 'cheart 1=', cheart, 'cwood 1=',
                      cwood)
            else:  # csap and cheart calculated by allometric restrictions
                cleaf = cleaf
                croot = croot
                csap = csap
                cheart = cheart
                cwood = csap + cheart
                print('lb dif. 0', 'csap 1=', csap, 'cheart 1=', cheart,
                      'cwood 1=', cwood)
            self.soil_temp = st.soil_temp(self.soil_temp, temp[step])

            self.wfim = np.zeros(npls, order='F') + self.wp_water
            self.gfim = np.zeros(npls, order='F') + self.wp_ice
            self.sfim = np.zeros(npls, order='F') + self.wp_snow
            print('SPINUPPPPPP')
            out = model.daily_budget(
                self.pls_table, self.wfim, self.gfim, self.sfim,
                self.soil_temp, temp[step], prec[step], p_atm[step],
                ipar[step], ru[step], self.sp_available_n, self.sp_available_p,
                self.sp_snc[:4].sum(), self.sp_so_p, self.sp_snc[4:].sum(),
                co2, sto, cleaf, cwood, croot, csap, cheart, dcl, dca, dcf,
                uptk_costs)

            self.wfim = None
            self.gfim = None
            self.sfim = None

            # Create a dict with the function output
            daily_output = catch_out_budget(out)

            self.wp_water = daily_output['wp'][0]
            self.wp_ice = daily_output['wp'][1]
            self.wp_snow = daily_output['wp'][2]

            # UPDATE vegetation pools

            wo.append(self.wp_water)
            llo.append(daily_output['litter_l'])
            cwdo.append(daily_output['cwd'])
            rlo.append(daily_output['litter_fr'])
            lnco.append(daily_output['lnc'])

        f = np.array

        def x(a):
            return a * 0.75

        return x(f(wo).mean()), x(f(llo).mean()), x(f(cwdo).mean()), x(
            f(rlo).mean()), x(f(lnco).mean(axis=0, ))