Beispiel #1
0
    def test_timestepping(self):

        steps = ['ambitious', 'default', 'conservative',
                 'ultra-conservative'][::-1]
        lens = []
        surface_h = []
        volume = []
        yrs = np.arange(1, 400, 2)
        for step in steps:
            fls = dummy_constant_bed()
            mb = LinearMassBalance(2600.)

            model = FluxBasedModel(fls,
                                   mb_model=mb,
                                   glen_a=self.glen_a,
                                   time_stepping=step)

            length = yrs * 0.
            vol = yrs * 0.
            for i, y in enumerate(yrs):
                model.run_until(y)
                length[i] = fls[-1].length_m
                vol[i] = fls[-1].volume_km3
            lens.append(length)
            volume.append(vol)
            surface_h.append(fls[-1].surface_h.copy())

        np.testing.assert_allclose(volume[0][-1], volume[1][-1], atol=1e-2)
        np.testing.assert_allclose(volume[0][-1], volume[2][-1], atol=1e-2)
        np.testing.assert_allclose(volume[0][-1], volume[3][-1], atol=1e-2)
Beispiel #2
0
def get_first_guess_surface_h(data_logger):
    """TODO: Function to conduct spinup for first guess surface_h"""
    fl = data_logger.flowline_init

    if 'surface_h' in list(data_logger.spinup_options.keys()):
        mb_options = data_logger.spinup_options['surface_h']['mb_model']
        if mb_options['type'] == 'constant':
            yr_start_run = mb_options['years'][0]
            yr_end_run = mb_options['years'][1]
            halfsize = (yr_end_run - yr_start_run) / 2
            mb_spinup = MultipleFlowlineMassBalance(
                data_logger.gdir,
                fls=[fl],
                mb_model_class=ConstantMassBalance,
                filename='climate_historical',
                input_filesuffix='',
                y0=yr_start_run + halfsize,
                halfsize=halfsize)
            mb_spinup.temp_bias = mb_options['t_bias']

            model = FluxBasedModel(copy.deepcopy([fl]),
                                   mb_spinup,
                                   y0=yr_start_run)
            model.run_until(yr_end_run)

            return model.fls[0].surface_h
        else:
            raise NotImplementedError(
                f'mb type {mb_options["type"]} not implemented!')

    else:
        raise NotImplementedError(
            'The provided spinup option is not implemented!')
Beispiel #3
0
    def test_timestepping(self):

        steps = ['ambitious',
                 'default',
                 'conservative',
                 'ultra-conservative'][::-1]
        lens = []
        surface_h = []
        volume = []
        yrs = np.arange(1, 400, 2)
        for step in steps:
            fls = dummy_constant_bed()
            mb = LinearMassBalance(2600.)

            model = FluxBasedModel(fls, mb_model=mb,
                                   glen_a=self.glen_a,
                                   time_stepping=step)

            length = yrs * 0.
            vol = yrs * 0.
            for i, y in enumerate(yrs):
                model.run_until(y)
                length[i] = fls[-1].length_m
                vol[i] = fls[-1].volume_km3
            lens.append(length)
            volume.append(vol)
            surface_h.append(fls[-1].surface_h.copy())

        np.testing.assert_allclose(volume[0][-1], volume[1][-1], atol=1e-2)
        np.testing.assert_allclose(volume[0][-1], volume[2][-1], atol=1e-2)
        np.testing.assert_allclose(volume[0][-1], volume[3][-1], atol=1e-2)
Beispiel #4
0
    def test_limiter(self, default_calving):

        _, ds1, df_diag1 = default_calving

        model = FluxBasedModel(bu_tidewater_bed(),
                               mb_model=ScalarMassBalance(),
                               is_tidewater=True,
                               calving_use_limiter=False,
                               flux_gate=0.06,
                               do_kcalving=True,
                               calving_k=0.2)
        _, ds2 = model.run_until_and_store(3000)
        df_diag2 = model.get_diagnostics()
        assert_allclose(model.volume_m3 + model.calving_m3_since_y0,
                        model.flux_gate_m3_since_y0)
        assert_allclose(ds2.calving_m3[-1], model.calving_m3_since_y0)

        # Not exact same of course
        assert_allclose(ds1.volume_m3[-1], ds2.volume_m3[-1], rtol=0.06)
        assert_allclose(ds1.calving_m3[-1], ds2.calving_m3[-1], rtol=0.15)

        if do_plot:
            f, ax = plt.subplots(1, 1, figsize=(12, 5))
            df_diag1[['surface_h']].plot(ax=ax, color=['C3'])
            df_diag2[['surface_h', 'bed_h']].plot(ax=ax, color=['C1', 'k'])
            plt.hlines(0, 0, 60000, color='C0', linestyles=':')
            plt.ylim(-350, 800)
            plt.ylabel('Altitude [m]')
            plt.show()
Beispiel #5
0
    def test_boundaries(self):

        fls = dummy_constant_bed()
        mb = LinearMassBalance(2000.)
        model = FluxBasedModel(fls, mb_model=mb, y0=0.,
                               glen_a=self.glen_a,
                               fs=self.fs)
        with pytest.raises(RuntimeError) as excinfo:
            model.run_until(300)
        assert 'exceeds domain boundaries' in str(excinfo.value)
Beispiel #6
0
    def test_boundaries(self):

        fls = dummy_constant_bed()
        mb = LinearMassBalance(2000.)
        model = FluxBasedModel(fls, mb_model=mb, y0=0.,
                               glen_a=self.glen_a,
                               fs=self.fs)
        with pytest.raises(RuntimeError) as excinfo:
            model.run_until(300)
        assert 'exceeds domain boundaries' in str(excinfo.value)
Beispiel #7
0
    def test_run_until_and_store(self):

        fls = dummy_constant_bed()
        mb = LinearMassBalance(2600.)

        model = FluxBasedModel(fls, mb_model=mb)
        ds_f, ds_d = model.run_until_and_store(150)
        assert ds_d['length_m'][-1] > 1e3

        df = model.get_diagnostics()
        assert (df['ice_velocity'].max() * cfg.SEC_IN_YEAR) > 10
Beispiel #8
0
    def test_flux_gate_with_trib(self):

        mb = ScalarMassBalance()
        model = FluxBasedModel(dummy_width_bed_tributary(), mb_model=mb,
                               flux_gate_thickness=150, flux_gate_build_up=50)
        model.run_until(1000)
        assert_allclose(model.volume_m3, model.flux_gate_m3_since_y0)

        if do_plot:  # pragma: no cover
            plt.plot(model.fls[-1].bed_h, 'k')
            plt.plot(model.fls[-1].surface_h, 'b')
            plt.show()
Beispiel #9
0
def default_calving():
    cfg.initialize()
    model = FluxBasedModel(bu_tidewater_bed(),
                           mb_model=ScalarMassBalance(),
                           is_tidewater=True, calving_use_limiter=True,
                           flux_gate=0.06, do_kcalving=True,
                           calving_k=0.2)
    _, ds = model.run_until_and_store(3000)
    df_diag = model.get_diagnostics()
    assert_allclose(model.volume_m3 + model.calving_m3_since_y0,
                    model.flux_gate_m3_since_y0)
    assert_allclose(ds.calving_m3[-1], model.calving_m3_since_y0)
    return model, ds, df_diag
def run_model(param, gdir, y_t, random_climate2, t0, te):
    '''
    :param param: temp bias, is changed by optimization
    :param gdir:  oggm.GlacierDirectory
    :param y_t: oggm.Flowline for the observed state (2000)
    :param random_climate2: oggm.massbalance.RandomMassBalance
    :return: 2 oggm.flowline.FluxBasedModels
             (glacier candidate and predicted glacier model)
    '''

    # run estimated glacier with random climate 2 until equilibrium
    # (glacier candidate)

    # estimated flowline = observed flowline
    estimated_fls = deepcopy(y_t)
    climate = deepcopy(random_climate2)
    # change temp_bias
    climate.temp_bias = param
    random_model = FluxBasedModel(estimated_fls, mb_model=climate, y0=t0)
    random_model.run_until_equilibrium()
    # run glacier candidate with past climate until 2000
    candidate_fls = deepcopy(y_t)
    for i in range(len(y_t)):
        candidate_fls[i].surface_h = random_model.fls[i].surface_h

    past_climate = PastMassBalance(gdir)
    past_model = FluxBasedModel(candidate_fls,
                                mb_model=past_climate,
                                glen_a=cfg.A,
                                y0=t0)
    past_model.run_until(te)

    return [random_model, past_model]
def run_model(param, gdir, start_fls, t, i):
    fls = gdir.read_pickle('model_flowlines')

    fls1 = copy.deepcopy(fls)
    fls1[-1].surface_h = copy.deepcopy(y_start.fls[-1].surface_h)
    climate = copy.deepcopy(past_climate)
    #climate= RandomMassBalance(gdir)
    climate.temp_bias = param
    model = FluxBasedModel(fls1, mb_model=climate, glen_a=i * cfg.A, y0=1850)
    model.run_until(1850 + t)
    fls2 = copy.deepcopy(fls)
    fls2[-1].surface_h = model.fls[-1].surface_h
    real_model = FluxBasedModel(fls2,
                                mb_model=past_climate,
                                glen_a=cfg.A,
                                y0=1850)

    real_model.run_until(2000)

    dif = real_model.fls[-1].surface_h - y_1900.fls[-1].surface_h
    s = np.sum(np.abs(dif))

    #ax1.plot(x, model.fls[-1].surface_h, label='optimum')
    #ax2.plot(x, real_model.fls[-1].surface_h)
    #ax3.plot(x, dif)

    return [model, real_model]
Beispiel #12
0
def minimize_dl(tbias, mb, fls, dl, len2003, gdir, optimization, runsuffix=''):
    # Mass balance
    mb.temp_bias = tbias

    model = FluxBasedModel(fls, mb_model=mb)

    try:
        model.run_until_equilibrium(ystep=10, max_ite=200)
    except FloatingPointError:
        if optimization is True:
            log.info('(%s) tbias of %.2f gave length: %.2f' %
                     (gdir.rgi_id, tbias, model.length_m))
            return len2003**2
        else:
            raise RuntimeError('This should never happen...')
    except RuntimeError as err:
        if (optimization is True) and\
           ('Glacier exceeds domain boundaries' in err.args[0]):
            log.info('(%s) tbias of %.2f exceeds domain boundaries' %
                     (gdir.rgi_id, tbias))
            return len2003**2
        elif (optimization is True) and \
                ('Did not find equilibrium' in err.args[0]):
            log.info('(%s) tbias of %.2f did exceed max iterations' %
                     (gdir.rgi_id, tbias))
            return len2003**2
        elif 'CFL error' in err.args[0]:
            log.info('(%s) tbias of %.2f leads to CFL error' %
                     (gdir.rgi_id, tbias))
            print(err)
            return len2003**2
        else:
            print(err)
            raise RuntimeError('This should never happen 2...')

    if optimization is True:
        if model.length_m < fls[-1].dx_meter:
            log.info('(%s) tbias of %.2f gave length: %.2f' %
                     (gdir.rgi_id, tbias, model.length_m))
            return len2003**2

        dl_spinup = model.length_m - len2003
        delta = (dl - dl_spinup)**2
        print('%s: tbias: %.2f  delta: %.4f' % (gdir.rgi_id, tbias, delta))
        return delta
    else:

        filesuffix = '_spinup' + runsuffix

        run_path = gdir.get_filepath('model_run',
                                     filesuffix=filesuffix,
                                     delete=True)
        diag_path = gdir.get_filepath('model_diagnostics',
                                      filesuffix=filesuffix,
                                      delete=True)
        model2 = FluxBasedModel(fls, mb_model=mb)
        model2.run_until_and_store(model.yr,
                                   run_path=run_path,
                                   diag_path=diag_path)
Beispiel #13
0
def init_model(init_flowline, mb_model, years, glen_a=None, fs=None):
    """This function initializes a new model, therefore it uses FluxBasedModel.
    The outline of the glacier is calculated for a chosen amount of years.

    Parameters
    ----------
    init_flowline : oggm.core.flowline.RectangularBedFlowline
        the glacier flowline
    mb_model : oggm.core.massbalance.LinearMassBalance
        the glacier mass balance model
    years : int
        year until which glacier evolution is calculated
    glen_a : float, optional
        Glen's parameter, (default: 2.4e-24 s^-1 Pa^-3)
    fs : float, optional
        sliding parameter, (default: 0)

   Returns
   -------
   model : oggm.core.flowline.FluxBasedModel
       the initialized model

   TODO: return also length and volume steps (they are calculated for every
         time step)
   """
    if not glen_a:
        glen_a = cfg.PARAMS['glen_a']

    if not fs:
        fs = 0

    model = FluxBasedModel(init_flowline,
                           mb_model=mb_model,
                           y0=0.,
                           glen_a=glen_a,
                           fs=fs)
    # Year 0 to 600 in 5 years step
    yrs = np.arange(0, years, 5)
    # Array to fill with data
    nsteps = len(yrs)
    length = np.zeros(nsteps)
    vol = np.zeros(nsteps)
    # Loop
    for i, yr in enumerate(yrs):
        model.run_until(yr)
        length[i] = model.length_m
        vol[i] = model.volume_km3

    return model  # , length, vol
Beispiel #14
0
    def test_find_flux_from_thickness(self):

        mb = LinearMassBalance(2600.)
        model = FluxBasedModel(dummy_constant_bed(), mb_model=mb)
        model.run_until(700)

        # Pick a flux and slope somewhere in the glacier
        for i in [1, 10, 20, 50]:
            flux = model.flux_stag[0][i]
            slope = model.slope_stag[0][i]
            thick = model.thick_stag[0][i]
            width = model.fls[0].widths_m[i]

            out = find_sia_flux_from_thickness(slope, width, thick)
            assert_allclose(out, flux, atol=1e-7)
Beispiel #15
0
    def test_flux_gate_with_calving(self):

        mb = ScalarMassBalance()
        model = FluxBasedModel(dummy_constant_bed(), mb_model=mb,
                               flux_gate_thickness=150, flux_gate_build_up=50,
                               water_level=2000,
                               is_tidewater=True)
        model.run_until(2000)
        assert_allclose(model.volume_m3 + model.calving_m3_since_y0,
                        model.flux_gate_m3_since_y0)

        if do_plot:  # pragma: no cover
            plt.plot(model.fls[-1].bed_h, 'k')
            plt.plot(model.fls[-1].surface_h, 'b')
            plt.show()
Beispiel #16
0
    def progress_to_year(self, year):
        """Progress the glacier to year n.

        Parameters
        ----------
        year: int
            Year until which to progress the glacier.
        """
        # Some checks on the year.
        if year < 0:
            raise ValueError("Year has to be above zero")

        elif year <= self.age:
            warnings.warn(
                "Year has to be above the current age of"
                + " the glacier. It is not possible to"
                + " de-age the glacier."
                + " Geometry will remain."
            )

        # If all passes
        else:
            # Check if we have a current state already.
            state = self._state()
            # Initialise the model
            model = FluxBasedModel(
                state,
                mb_model=self.mass_balance,
                y0=self.age,
                glen_a=self.creep,
                fs=self.basal_sliding,
            )
            # Run the model. Store the history.
            try:
                out = model.run_until_and_store(year, fl_diag_path=None)
            # If it fails, see above.
            except RuntimeError:
                print("Glacier grew out of its domain, stepping back five years")
                # Ohhh recursion
                self.progress_to_year(year - 5)
                return

            # Update attributes.
            self.history = out[0]
            self.state_history = out[1][0]
            self._current_state = model.fls[0]
            self._model_state = model
            self.age = model.yr
Beispiel #17
0
def iterative_initial_glacier_search(gdir, y0=None, init_bias=0., rtol=0.005,
                                     write_steps=True):
    """Iterative search for the glacier in year y0.

    this is outdated and doesn't really work.
    """

    fs = cfg.PARAMS['fs']
    glen_a = cfg.PARAMS['glen_a']

    if y0 is None:
        y0 = cfg.PARAMS['y0']
    y1 = gdir.rgi_date.year
    mb = mbmods.PastMassBalance(gdir)
    fls = gdir.read_pickle('model_flowlines')

    model = FluxBasedModel(fls, mb_model=mb, y0=0., fs=fs, glen_a=glen_a)
    assert np.isclose(model.area_km2, gdir.rgi_area_km2, rtol=0.05)

    mb = mbmods.BackwardsMassBalanceModel(gdir)
    ref_area = gdir.rgi_area_m2
    ite, bias, past_model = _find_inital_glacier(model, mb, y0, y1,
                                                 rtol=rtol,
                                                 init_bias=init_bias,
                                                 ref_area=ref_area)

    path = gdir.get_filepath('model_run', delete=True)
    if write_steps:
        past_model.run_until_and_store(y1, path=path)
    else:
        past_model.to_netcdf(path)
def find_residual(gdir, a=-2000,b=2000):

    try:

        max_it = 15
        i = 0
        bounds = [a,b]

        df = pd.DataFrame()

        fls = gdir.read_pickle('model_flowlines')
        mod = FluxBasedModel(flowlines=fls)

        while i < max_it:
            bias = round((bounds[0] + bounds[1]) / 2,1)
            ex_mod2 = _run_experiment(gdir, bias)
            fit = fitness_function(ex_mod2,mod)
            df = df.append(pd.Series({'bias':bias,'fitness':fit}),ignore_index=True)

            if bounds[1]-bounds[0]<=1:
                break

            elif ex_mod2.area_km2 > mod.area_km2:
                bounds[0] = bias
            else:
                bounds[1] = bias
            i +=1

        # best bias found
        bias = df.iloc[df.fitness.idxmin()].bias
        rp = gdir.get_filepath('model_run', filesuffix='_advanced_experiment_' + str(bias))
        model = FileModel(rp)
        model.run_until(2000)

        rp = gdir.get_filepath('model_run', filesuffix='_advanced_experiment_' + str(0.0))
        ex_mod = FileModel(rp)
        ex_mod.run_until(2000)

        plt.figure(figsize=(15,10))
        plt.plot(model.fls[-1].surface_h,'r',label='best')
        plt.plot(mod.fls[-1].surface_h, 'orange', label='original')
        plt.plot(ex_mod.fls[-1].surface_h, 'r:', label='old experiment')
        plt.plot(model.fls[-1].bed_h,'k', label='bed')
        plt.legend()
        utils.mkdir(os.path.join(cfg.PATHS['plot_dir'],'bias_test'))
        plt.savefig(os.path.join(cfg.PATHS['plot_dir'],'bias_test',gdir.rgi_id+'.png'),dpi=200)
        plt.show()
        diff = mod.area_km2 - model.area_km2_ts()[2000]
        model.reset_y0(1865)

        series = pd.Series({'rgi_id':gdir.rgi_id,'bias':bias,'iterations':i, 'fitness':fit, 'area_diff':diff, 'model':model})
    except:
        series =  pd.Series({'rgi_id':gdir.rgi_id})

    return series
Beispiel #19
0
def find_residual(gdir, temp_bias_list, ys, a=-2000, b=2000):

    for temp_bias in temp_bias_list:
        try:

            fls = gdir.read_pickle('model_flowlines')
            mod = FluxBasedModel(flowlines=fls)

            ye = gdir.rgi_date
            max_it = 15
            i = 0
            bounds = [a, b]

            df = pd.DataFrame()

            while i < max_it:
                bias = round((bounds[0] + bounds[1]) / 2, 2)

                ex_mod2 = _run_experiment(gdir, temp_bias, bias, ys, ye)
                if isinstance(ex_mod2, FileModel):
                    diff = gdir.rgi_area_km2 - ex_mod2.area_km2_ts()[ye]
                    error = ''
                # bias needs to be set higher
                else:
                    diff = -np.inf
                    error = ex_mod2.split(':')[-1]

                df = df.append(pd.Series({
                    'bias': bias,
                    'area_diff': diff,
                    'error': error
                }),
                               ignore_index=True)

                if (abs(diff) < 1e-4) or bounds[1] - bounds[0] <= 0.25:
                    break

                elif diff < 0:
                    bounds[0] = bias
                else:
                    bounds[1] = bias
                i += 1

            # best bias found
            bias = df.iloc[df.area_diff.abs().idxmin()].bias
            df.to_csv(os.path.join(gdir.dir, 'experiment_iteration.csv'))

            for file in os.listdir(gdir.dir):
                if file.startswith('model') and file.endswith(
                        '.nc') and not file.endswith('_' + str(bias) + '.nc'):
                    os.remove(os.path.join(gdir.dir, file))

        except:
            pass
Beispiel #20
0
def read_result_parallel(gdir):

    try:
        ye = gdir.rgi_date
        fls = gdir.read_pickle('model_flowlines')
        mod = FluxBasedModel(flowlines=fls)

        lec = get_ref_length_data(gdir).dL

        if lec.index[0] < 1917:
            lec.loc[1917] = np.nan
            lec = lec.sort_index().interpolate()
            lec = lec - lec.loc[1917]
            lec = lec[lec.index >=1917]

        '''
        lec = lec[lec.index <= ye]

        lec = (lec - lec.iloc[-1]) + mod.length_m

        ini_df = pd.read_pickle(os.path.join(gdir.dir,'result1917.pkl'), compression='gzip')
        ini_df.loc[:,'fitness'] = pd.to_numeric(ini_df.fitness / 125)
        ini_df = ini_df.dropna(subset=['fitness'])

        med_mod, perc_min, perc_max = find_median(ini_df)

        temp_bias = cfg.PATHS['working_dir'].split('_')[-1]
        #return pd.Series({'rgi':gdir.rgi_id, 'temp_bias':temp_bias, 'median':med_mod})


        lec.loc[1917] = np.nan
        lec = lec.sort_index().interpolate()[lec.index >= 1917]
        lec.loc['rgi_id']= gdir.rgi_id
        print(med_mod.length_m_ts()[lec.index])
        rmse = np.sqrt(((lec - med_mod.length_m_ts(rollmin=5)[lec.index]) ** 2).mean())
        error = (lec - med_mod.length_m_ts(rollmin=5)[lec.index]).mean()
        max = (lec - med_mod.length_m_ts(rollmin=5)[lec.index]).max()
        min = (lec - med_mod.length_m_ts(rollmin=5)[lec.index]).min()
        if abs(max)>abs(min):
            max_diff = max
        else:
            max_diff = min


        # saves median state, minimum state and experiment model
        return pd.Series({'rgi':gdir.rgi_id,'region':gdir.rgi_region,
                          'length':mod.length_m, 'temp_bias':temp_bias,
                          'rmse':rmse, 'max_diff':max_diff, 'error':error})
        '''
        lec.loc['rgi'] = gdir.rgi_id
        return lec
    except:
        return pd.Series({'rgi':gdir.rgi_id})
def run_model(param, gdir):
    nx = y_1900.fls[-1].nx

    fls = gdir.read_pickle('model_flowlines')
    surface_h = rescale(param, nx)

    thick = surface_h - y_1900.fls[-1].bed_h
    # We define the length a bit differently: but more robust
    try:
        pok = np.where(thick < 10)[0]
        surface_h[int(pok[0]):] = y_1900.fls[-1].bed_h[int(pok[0]):]
        fls[-1].surface_h = surface_h
        real_model = FluxBasedModel(fls,
                                    mb_model=past_climate,
                                    glen_a=cfg.A,
                                    y0=1850)
        model = copy.deepcopy(real_model)
        real_model.run_until(1900)
        return [model, real_model]

    except:
        pass
Beispiel #22
0
def find_residual(gdir, temp_bias_list, ys,a=-2000,b=2000):

    best_df = pd.DataFrame()

    fls = gdir.read_pickle('model_flowlines')
    mod = FluxBasedModel(flowlines=fls)

    for temp_bias in temp_bias_list:

        try:
            ye = gdir.rgi_date
            max_it = 15
            i = 0
            bounds = [a,b]

            df = pd.DataFrame()

            while i < max_it:
                bias = round((bounds[0] + bounds[1]) / 2,1)

                ex_mod2 = _run_experiment(gdir, temp_bias, bias, ys, ye)

                diff = mod.area_km2 - ex_mod2.area_km2_ts()[ye]

                df = df.append(pd.Series({'bias':bias,'area_diff':diff}),ignore_index=True)

                if  (abs(diff)<1e-4) or bounds[1]-bounds[0]<=1:
                    break

                elif ex_mod2.area_km2_ts()[ye] > mod.area_km2:
                    bounds[0] = bias
                else:
                    bounds[1] = bias
                i +=1

            # best bias found
            bias = df.iloc[df.area_diff.abs().idxmin()].bias

            rp = gdir.get_filepath('model_run', filesuffix='_advanced_experiment_'+str(temp_bias)+'_'+str(bias))
            model = FileModel(rp)

            diff = gdir.rgi_area_km2 - model.area_km2_ts()[gdir.rgi_date]

            series = pd.Series({'rgi_id':gdir.rgi_id,'bias':bias,'iterations':i,  'area_diff':diff, 'model':model, 'temp_bias':temp_bias})

        except:
            series =  pd.Series({'rgi_id':gdir.rgi_id, 'temp_bias':temp_bias})
        best_df = best_df.append(series, ignore_index=True)


    return best_df
def run_model(param, gdir, ela):
    climate = LinearMassBalance(ela_h=ela)
    climate.temp_bias = param
    fls = gdir.read_pickle('model_flowlines')
    fls1 = copy.deepcopy(fls)
    fls1[-1].surface_h = y_start.fls[-1].surface_h
    model = FluxBasedModel(fls1, mb_model=climate, glen_a=cfg.A, y0=0)
    model.run_until(50)

    fls2 = copy.deepcopy(fls)
    fls2[-1].surface_h = copy.deepcopy(model.fls[-1].surface_h)
    real_model = FluxBasedModel(fls2,
                                mb_model=past_climate,
                                glen_a=cfg.A,
                                y0=1850)
    real_model.run_until(1900)
    return [model, real_model]
Beispiel #24
0
def run_model(param,gdir,fls):

    fls1 = copy.deepcopy(fls)
    climate = random_climate2
    climate.temp_bias = param
    model = FluxBasedModel(fls1, mb_model=climate, y0=1890)
    model.run_until_equilibrium()

    # fls2= copy.deepcopy(fls)
    fls2 = model.fls

    real_model = FluxBasedModel(fls2, mb_model=past_climate,
                                glen_a=cfg.A, y0=1850)

    real_model.run_until(2000)

    return [model,real_model]
    def spinup_run(t_bias):
        # with t_bias the glacier state after spinup is changed between iterations
        mb_spinup.temp_bias = t_bias
        # run the spinup
        model_spinup = FluxBasedModel(copy.deepcopy(fls_spinup),
                                      mb_spinup,
                                      y0=0)
        model_spinup.run_until_equilibrium(max_ite=1000)

        # Now conduct the actual model run to the rgi date
        model_historical = FluxBasedModel(model_spinup.fls,
                                          mb_historical,
                                          y0=yr_spinup)
        model_historical.run_until(yr_rgi)

        cost = (model_historical.length_m - length_m_ref) / length_m_ref * 100

        return cost
Beispiel #26
0
    def test_water_level(self, default_calving):

        _, ds_1, _ = default_calving

        model = FluxBasedModel(bu_tidewater_bed(water_level=1000),
                               mb_model=ScalarMassBalance(),
                               is_tidewater=True,
                               calving_use_limiter=True,
                               flux_gate=0.06,
                               do_kcalving=True,
                               calving_water_level=1000,
                               calving_k=0.2)
        _, ds_2 = model.run_until_and_store(3000)
        assert_allclose(model.volume_m3 + model.calving_m3_since_y0,
                        model.flux_gate_m3_since_y0)

        assert_allclose(ds_1.calving_m3, ds_2.calving_m3)

        if do_plot:
            df_diag = model.get_diagnostics()
            f, ax = plt.subplots(1, 1, figsize=(12, 5))
            df_diag[['surface_h', 'bed_h']].plot(ax=ax, color=['C3', 'k'])
            plt.hlines(1000, 0, 60000, color='C0', linestyles=':')
            plt.ylim(1000 - 350, 1000 + 800)
            plt.ylabel('Altitude [m]')
            plt.show()

        # Let the model decide the water level
        fls = bu_tidewater_bed(water_level=1000)
        thick = fls[0].thick
        thick[fls[0].bed_h > 1000] = 1
        fls[0].thick = thick
        model = FluxBasedModel(fls,
                               mb_model=ScalarMassBalance(),
                               is_tidewater=True,
                               calving_use_limiter=True,
                               is_lake_terminating=True,
                               flux_gate=0.06,
                               do_kcalving=True,
                               calving_k=0.2)
        assert_allclose(model.water_level, 1000, atol=1)

        with pytest.raises(InvalidParamsError):
            fls = bu_tidewater_bed(water_level=1000)
            FluxBasedModel(fls,
                           mb_model=ScalarMassBalance(),
                           is_tidewater=True,
                           calving_use_limiter=True,
                           is_lake_terminating=True)
Beispiel #27
0
def run_model(param, gdir, fls, random_climate2):

    fls1 = copy.deepcopy(fls)
    climate = random_climate2
    climate.temp_bias = param
    model = FluxBasedModel(fls1, mb_model=climate, y0=1890)
    model.run_until_equilibrium()

    fls2 = copy.deepcopy(fls)
    #fls2 = model.fls
    for i in range(len(fls)):
        fls2[i].surface_h = model.fls[i].surface_h
    real_model = FluxBasedModel(fls2,
                                mb_model=past_climate,
                                glen_a=cfg.A,
                                y0=1850)

    real_model.run_until(2000)

    return [model, real_model]
Beispiel #28
0
    def test_simple_flux_gate(self):

        mb = ScalarMassBalance()
        model = FluxBasedModel(dummy_constant_bed(), mb_model=mb,
                               flux_gate_thickness=150, flux_gate_build_up=50)
        model.run_until(1000)
        assert_allclose(model.volume_m3, model.flux_gate_m3_since_y0)

        model = FluxBasedModel(dummy_mixed_bed(), mb_model=mb,
                               flux_gate_thickness=150, flux_gate_build_up=50)
        model.run_until(1000)
        assert_allclose(model.volume_m3, model.flux_gate_m3_since_y0)
        # Make sure that we cover the types of beds
        beds = np.unique(model.fls[0].shape_str[model.fls[0].thick > 0])
        assert len(beds) == 2

        if do_plot:  # pragma: no cover
            plt.plot(model.fls[-1].bed_h, 'k')
            plt.plot(model.fls[-1].surface_h, 'b')
            plt.show()
def plot_advanced_experiment(gdir):

    fig = plt.figure(figsize=(15, 14))
    grid = plt.GridSpec(1, 2, hspace=0.2, wspace=0.2)
    ax1 = plt.subplot(grid[0])
    ax2 = plt.subplot(grid[1], sharey=ax1)
    ax2.plot(gdir.rgi_date,gdir.rgi_area_km2,'o', label='RGI area '+ str(gdir.rgi_date))
    mod = FluxBasedModel(flowlines=gdir.read_pickle('model_flowlines'))
    ax2.plot(gdir.rgi_date, mod.area_km2, 'o',
             label='RGI area ' + str(gdir.rgi_date))

    for f in os.listdir(gdir.dir):
        if f.startswith('model_run_'):
            rp = os.path.join(gdir.dir,f)
            model = FileModel(rp)
            model.area_km2_ts().plot(ax=ax2, label=f)

    #ax2.set_xlim((1915,2005))
    #ax2.legend()
    plt.show()
Beispiel #30
0
def run_model(surface_h):

    nx = y1.fls[-1].nx
    random_climate = pickle.load(open('random_climate_hef.pkl', 'rb'))
    hef_fls = pickle.load(open('hef_y1.pkl', 'rb'))
    surface_h = rescale(surface_h, nx)
    thick = surface_h - hef_fls[-1].bed_h
    # We define the length a bit differently: but more robust
    try:
        pok = np.where(thick < 10)[0]
        surface_h[int(pok[0]):] = hef_fls[-1].bed_h[int(pok[0]):]

    except:
        pass
    hef_fls[-1].surface_h = surface_h
    commit_model = FluxBasedModel(hef_fls,
                                  mb_model=random_climate,
                                  glen_a=cfg.A,
                                  y0=1850)
    return commit_model
Beispiel #31
0
    def test_random(self):

        # Fake Reset (all these tests are horribly coded)
        if not os.path.exists(TEST_DIR):
            os.makedirs(TEST_DIR)
        with open(CLI_LOGF, 'wb') as f:
            pickle.dump('none', f)
        gdirs = up_to_inversion(reset=False)

        # First tests
        df = utils.compile_glacier_statistics(gdirs)
        df['volume_before_calving_km3'] = df['volume_before_calving'] * 1e-9
        assert np.sum(~df.volume_before_calving.isnull()) == 2
        dfs = df.iloc[:2]
        assert np.all(dfs['volume_before_calving_km3'] < dfs['inv_volume_km3'])
        assert_allclose(df['inv_flowline_glacier_area'] * 1e-6,
                        df['rgi_area_km2'])

        workflow.execute_entity_task(flowline.init_present_time_glacier, gdirs)
        # Check init_present_time_glacier not messing around too much
        for gd in gdirs:
            from oggm.core.massbalance import LinearMassBalance
            from oggm.core.flowline import FluxBasedModel
            mb_mod = LinearMassBalance(ela_h=2500)
            fls = gd.read_pickle('model_flowlines')
            model = FluxBasedModel(fls, mb_model=mb_mod)
            df.loc[gd.rgi_id, 'start_area_km2'] = model.area_km2
            df.loc[gd.rgi_id, 'start_volume_km3'] = model.volume_km3
            df.loc[gd.rgi_id, 'start_length'] = model.length_m
        assert_allclose(df['rgi_area_km2'], df['start_area_km2'], rtol=0.01)
        assert_allclose(df['rgi_area_km2'].sum(),
                        df['start_area_km2'].sum(),
                        rtol=0.005)
        assert_allclose(df['inv_volume_km3'], df['start_volume_km3'])
        assert_allclose(df['inv_volume_km3'].sum(),
                        df['start_volume_km3'].sum())
        assert_allclose(df['main_flowline_length'], df['start_length'])

        workflow.execute_entity_task(flowline.run_random_climate,
                                     gdirs,
                                     nyears=100,
                                     seed=0,
                                     store_monthly_step=True,
                                     output_filesuffix='_test')

        for gd in gdirs:
            path = gd.get_filepath('model_run', filesuffix='_test')
            # See that we are running ok
            with flowline.FileModel(path) as model:
                vol = model.volume_km3_ts()
                area = model.area_km2_ts()
                length = model.length_m_ts()

                self.assertTrue(np.all(np.isfinite(vol) & vol != 0.))
                self.assertTrue(np.all(np.isfinite(area) & area != 0.))
                self.assertTrue(np.all(np.isfinite(length) & length != 0.))

            ds_diag = gd.get_filepath('model_diagnostics', filesuffix='_test')
            ds_diag = xr.open_dataset(ds_diag)
            df = vol.to_frame('RUN')
            df['DIAG'] = ds_diag.volume_m3.to_series() * 1e-9
            assert_allclose(df.RUN, df.DIAG)
            df = area.to_frame('RUN')
            df['DIAG'] = ds_diag.area_m2.to_series() * 1e-6
            assert_allclose(df.RUN, df.DIAG)
            df = length.to_frame('RUN')
            df['DIAG'] = ds_diag.length_m.to_series()
            assert_allclose(df.RUN, df.DIAG)

        # Test output
        ds = utils.compile_run_output(gdirs, input_filesuffix='_test')
        assert_allclose(ds_diag.volume_m3, ds.volume.sel(rgi_id=gd.rgi_id))
        assert_allclose(ds_diag.area_m2, ds.area.sel(rgi_id=gd.rgi_id))
        assert_allclose(ds_diag.length_m, ds.length.sel(rgi_id=gd.rgi_id))
        df = ds.volume.sel(rgi_id=gd.rgi_id).to_series().to_frame('OUT')
        df['RUN'] = ds_diag.volume_m3.to_series()
        assert_allclose(df.RUN, df.OUT)

        # Compare to statistics
        df = utils.compile_glacier_statistics(gdirs)
        df['y0_vol'] = ds.volume.sel(rgi_id=df.index, time=0) * 1e-9
        df['y0_area'] = ds.area.sel(rgi_id=df.index, time=0) * 1e-6
        df['y0_len'] = ds.length.sel(rgi_id=df.index, time=0)
        assert_allclose(df['rgi_area_km2'], df['y0_area'], 0.06)
        assert_allclose(df['inv_volume_km3'], df['y0_vol'], 0.04)
        assert_allclose(df['main_flowline_length'], df['y0_len'])

        # Calving stuff
        assert ds.isel(rgi_id=0).calving[-1] > 0
        assert ds.isel(rgi_id=0).calving_rate[-1] > 0
        assert ds.isel(rgi_id=0).volume_bsl[-1] == 0
        assert ds.isel(rgi_id=0).volume_bwl[-1] > 0
        assert ds.isel(rgi_id=1).calving[-1] > 0
        assert ds.isel(rgi_id=1).calving_rate[-1] > 0
        assert not np.isfinite(ds.isel(rgi_id=1).volume_bsl[-1])
def plot_median(gdir, df, eps, ex_mod, ys, ye, lec, plot_dir):
    plot_dir = os.path.join(plot_dir, '04_median')
    utils.mkdir(plot_dir)
    x = np.arange(ex_mod.fls[-1].nx) * ex_mod.fls[-1].dx * ex_mod.fls[
        -1].map_dx
    fig = plt.figure(figsize=(25, 18))
    grid = plt.GridSpec(2, 2, hspace=0.2, wspace=0.2)
    ax1 = plt.subplot(grid[0, 0])
    ax2 = plt.subplot(grid[0, 1], sharey=ax1)
    ax3 = plt.subplot(grid[1, :])

    if gdir.name != '':
        plt.suptitle(gdir.rgi_id + ': ' + gdir.name, fontsize=30)
    elif gdir.rgi_id.endswith('779'):
        plt.suptitle(gdir.rgi_id + ': Guslarferner', fontsize=30)
    else:
        plt.suptitle(gdir.rgi_id, fontsize=30)

    df = df.sort_values('fitness', ascending=False)

    # min model

    min_mod = deepcopy(df.loc[df.fitness.idxmin(), 'model'])
    min_mod.length_m_ts(rollmin=5).plot(ax=ax3, color='C1',
                                        linewidth=3,
                                        label=r'$s_{' + str(ys) + '-' + str(
                                            ye) + '}^{min}$')
    min_mod.reset_y0(ys)
    min_mod.run_until(ys)
    # real flowlines
    fls = gdir.read_pickle('model_flowlines')
    mod = FluxBasedModel(flowlines=fls)
    ax2.plot(x, mod.fls[-1].surface_h, 'C2', label=r'OGGM$_{init}$')

    ax1.plot(x, min_mod.fls[-1].surface_h, 'C1',
             label=r'$z_{' + str(ys) + '}^{min}$',
             linewidth=3)

    min_mod.run_until(ye)

    ax2.plot(x, min_mod.fls[-1].surface_h, 'C1', label=r'$z_{2000}^{min}$',
             linewidth=3)

    # acceptable glacier states
    df = df[df.fitness <=1]
    s_t0 = pd.DataFrame()
    s_te = pd.DataFrame()
    v = pd.DataFrame()
    for i in df.index:
        model = deepcopy(df.loc[i, 'model'])
        s_t0 = s_t0.append(pd.Series(model.fls[-1].surface_h),
                                 ignore_index=True)
        model.run_until(ye)
        s_te = s_te.append(pd.Series(model.fls[-1].surface_h),
                                 ignore_index=True)
        v = v.append(model.length_m_ts(rollmin=5), ignore_index=True)

    ax1.fill_between(x, s_t0.max().values, s_t0.min().values, alpha=0.3, color='grey',
                     label=r'$\mathcal{S}_{' + str(ys) + '}^{' + str(
                         eps) + '}$')
    ax2.fill_between(x, s_te.max().values, s_te.min().values, alpha=0.3, color='grey',
                     label=r'$\mathcal{S}_{' + str(ye) + '}^{' + str(
                         eps) + '}$')
    ax3.fill_between(model.length_m_ts(rollmin=5).index,
                     v.max().values,
                     v.min().values, alpha=0.3, color='grey',
                     label=r'$\mathcal{S}_{' +str(ys)+'-'+str(ye) +'}^{' + str(eps) + '}$')

    # 5% quantile and median of 5% quantile

    df = df[df.fitness <= df.fitness.quantile(0.05)]
    s_t0 = pd.DataFrame()
    s_te = pd.DataFrame()
    v = pd.DataFrame()
    for i in df.index:
        model = deepcopy(df.loc[i, 'model'])
        s_t0 = s_t0.append(pd.Series(model.fls[-1].surface_h),
                           ignore_index=True)
        model.run_until(ye)
        s_te = s_te.append(pd.Series(model.fls[-1].surface_h),
                           ignore_index=True)
        v = v.append(model.length_m_ts(rollmin=5), ignore_index=True)

    ax1.fill_between(x, s_t0.max().values, s_t0.min().values, alpha=0.5,
                     label=r'$Q_{0.05}(\mathcal{S}_{'+str(ys)+'}^{'+str(eps)+'})$')

    ax2.fill_between(x, s_te.max().values, s_te.min().values, alpha=0.5,
                     label=r'$Q_{0.05}(\mathcal{S}_{'+str(ye)+'}^{'+str(eps)+'})$')

    ax3.fill_between(v.columns, v.max().values, v.min().values, alpha=0.5, linewidth=3,
                     label=r'$Q_{0.05}(\mathcal{S}_{'+str(ys)+'-'+str(ye)+'}^{'+str(eps)+'})$')

    # median of 5% quantile
    df.loc[:, 'length'] = df.model.apply(lambda x: x.length_m)
    df = df.sort_values('length', ascending=False)
    l = len(df)
    if l % 2:
        index = int((l - 1) / 2)
    else:
        index = int(l / 2)

    median_model = deepcopy(df.iloc[index].model)
    median_model.length_m_ts(rollmin=5).plot(ax=ax3, linewidth=3, label=r'$s_{'+str(ys)+'-'+str(ye)+'}^{med}$')
    median_model.reset_y0(ys)
    median_model.run_until(ys)

    ax1.plot(x, median_model.fls[-1].surface_h, label=r'$z_{'+str(ys)+'}^{med}$',
             linewidth=3)
    median_model.run_until(ye)
    ax2.plot(x, median_model.fls[-1].surface_h, label=r'$z_{'+str(ye)+'}^{med}$',
             linewidth=3)


    # experiment
    lec.plot(ax=ax3, color='r', linewidth=3, label='Leclercq')
    ax1.plot(x, ex_mod.fls[-1].bed_h, 'k', label=r'$b$', linewidth=3)
    ax2.plot(x, ex_mod.fls[-1].bed_h, 'k', label=r'$b$', linewidth=3)

    #ex_mod.length_m_ts(rollmin=5).plot(ax=ax3, color='k')

    # add figure names and x-/ylabels
    add_at(ax1, r"a", loc=3)
    add_at(ax2, r"b", loc=3)
    add_at(ax3, r"c", loc=3)

    ax1.set_ylabel('Altitude (m)', fontsize=30)
    ax1.set_xlabel('Distance along the main flowline (m)', fontsize=30)
    ax2.set_ylabel('Altitude (m)', fontsize=30)
    ax2.set_xlabel('Distance along the main flowline (m)', fontsize=30)
    ax3.set_ylabel(r'Length ($m$)', fontsize=30)
    ax3.set_xlabel('Time (years)', fontsize=30)

    ax1.tick_params(axis='both', which='major', labelsize=30)
    ax2.tick_params(axis='both', which='major', labelsize=30)
    ax3.tick_params(axis='both', which='major', labelsize=30)
    ax3.yaxis.offsetText.set_fontsize(30)
    ax3.set_xlim(xmin=1915, xmax=2019)

    l1 = ax1.legend(loc=1, fontsize=25)
    l1.set_zorder(1)

    l2 = ax2.legend(loc=1, fontsize=25)
    l2.set_zorder(1)

    l3 = ax3.legend(loc=1, fontsize=25)
    l3.set_zorder(1)

    fig_name = 'median_'+str(ys)+'_'+gdir.rgi_id
    plt.savefig(os.path.join(plot_dir, fig_name+'.pdf'), dpi=300)
    plt.savefig(os.path.join(plot_dir, fig_name+'.png'), dpi=300)
    plt.show()
    plt.close()
Beispiel #33
0
    def test_constant_bed(self):

        map_dx = 100.
        yrs = np.arange(1, 400, 5)
        lens = []
        volume = []
        areas = []
        surface_h = []

        # Flowline case
        fls = dummy_constant_bed(hmax=3000., hmin=1000., nx=200, map_dx=map_dx,
                                 widths=1.)
        mb = LinearMassBalance(2600.)

        flmodel = FluxBasedModel(fls, mb_model=mb, y0=0.)

        length = yrs * 0.
        vol = yrs * 0.
        area = yrs * 0
        for i, y in enumerate(yrs):
            flmodel.run_until(y)
            length[i] = fls[-1].length_m
            vol[i] = fls[-1].volume_km3
            area[i] = fls[-1].area_km2

        lens.append(length)
        volume.append(vol)
        areas.append(area)
        surface_h.append(fls[-1].surface_h.copy())

        # Make a 2D bed out of the 1D
        bed_2d = np.repeat(fls[-1].bed_h, 3).reshape((fls[-1].nx, 3))

        sdmodel = Upstream2D(bed_2d, dx=map_dx, mb_model=mb, y0=0.,
                             ice_thick_filter=None)

        length = yrs * 0.
        vol = yrs * 0.
        area = yrs * 0
        for i, y in enumerate(yrs):
            sdmodel.run_until(y)
            surf_1d = sdmodel.ice_thick[:, 1]
            length[i] = np.sum(surf_1d > 0) * sdmodel.dx
            vol[i] = sdmodel.volume_km3 / 3
            area[i] = sdmodel.area_km2 / 3

        lens.append(length)
        volume.append(vol)
        areas.append(area)
        surface_h.append(sdmodel.surface_h[:, 1])

        if do_plot:
            plt.figure()
            plt.plot(yrs, lens[0], 'r')
            plt.plot(yrs, lens[1], 'b')
            plt.title('Compare Length')
            plt.xlabel('years')
            plt.ylabel('[m]')
            plt.legend(['Flowline', '2D'], loc=2)

            plt.figure()
            plt.plot(yrs, volume[0], 'r')
            plt.plot(yrs, volume[1], 'b')
            plt.title('Compare Volume')
            plt.xlabel('years')
            plt.ylabel('[km^3]')
            plt.legend(['Flowline', '2D'], loc=2)

            plt.figure()
            plt.plot(fls[-1].bed_h, 'k')
            plt.plot(surface_h[0], 'r')
            plt.plot(surface_h[1], 'b')
            plt.title('Compare Shape')
            plt.xlabel('[m]')
            plt.ylabel('Elevation [m]')
            plt.legend(['Bed', 'Flowline', '2D'], loc=2)
            plt.show()

        np.testing.assert_almost_equal(lens[0][-1], lens[1][-1])
        np.testing.assert_allclose(volume[0][-1], volume[1][-1], atol=3e-3)

        self.assertTrue(utils.rmsd(lens[0], lens[1]) < 50.)
        self.assertTrue(utils.rmsd(volume[0], volume[1]) < 2e-3)
        self.assertTrue(utils.rmsd(areas[0], areas[1]) < 2e-3)
        self.assertTrue(utils.rmsd(surface_h[0], surface_h[1]) < 1.0)

        # Equilibrium
        sdmodel.run_until_equilibrium()
        flmodel.run_until_equilibrium()
        assert_allclose(sdmodel.volume_km3 / 3, flmodel.volume_km3, atol=2e-3)
        assert_allclose(sdmodel.area_km2 / 3, flmodel.area_km2, atol=2e-3)

        # Store
        run_ds = sdmodel.run_until_and_store(sdmodel.yr+50)
        ts = run_ds['ice_thickness'].mean(dim=['y', 'x'])
        assert_allclose(ts, ts.values[0], atol=1)

        # Other direction
        bed_2d = np.repeat(fls[-1].bed_h, 3).reshape((fls[-1].nx, 3)).T

        sdmodel = Upstream2D(bed_2d, dx=map_dx, mb_model=mb, y0=0.,
                             ice_thick_filter=None)

        length = yrs * 0.
        vol = yrs * 0.
        area = yrs * 0
        for i, y in enumerate(yrs):
            sdmodel.run_until(y)
            surf_1d = sdmodel.ice_thick[1, :]
            length[i] = np.sum(surf_1d > 0) * sdmodel.dx
            vol[i] = sdmodel.volume_km3 / 3
            area[i] = sdmodel.area_km2 / 3

        lens.append(length)
        volume.append(vol)
        areas.append(area)
        surface_h.append(sdmodel.surface_h[:, 1])

        np.testing.assert_almost_equal(lens[0][-1], lens[1][-1])
        np.testing.assert_allclose(volume[0][-1], volume[1][-1], atol=3e-3)

        self.assertTrue(utils.rmsd(lens[0], lens[1]) < 50.)
        self.assertTrue(utils.rmsd(volume[0], volume[1]) < 2e-3)
        self.assertTrue(utils.rmsd(areas[0], areas[1]) < 2e-3)
        self.assertTrue(utils.rmsd(surface_h[0], surface_h[1]) < 1.0)

        # Equilibrium
        sdmodel.run_until_equilibrium()
        assert_allclose(sdmodel.volume_km3 / 3, flmodel.volume_km3, atol=2e-3)
        assert_allclose(sdmodel.area_km2 / 3, flmodel.area_km2, atol=2e-3)