Beispiel #1
0
def _run_setup_model(
        model_file, scenario, model_format, override_dict):
    """
    Build model in CLI commands. Returns ``model``, a ready-to-run
    calliope.Model instance.

    """
    # Try to determine model file type if not given explicitly
    if model_format is None:
        if model_file.split('.')[-1] in ['yaml', 'yml']:
            model_format = 'yaml'
        elif model_file.split('.')[-1] in ['nc', 'nc4', 'netcdf']:
            model_format = 'netcdf'
        else:
            raise ValueError(
                'Cannot determine model file format based on file '
                'extension for "{}". Set format explicitly with '
                '--model_format.'.format(model_file)
            )

    if model_format == 'yaml':
        model = Model(
            model_file, scenario=scenario, override_dict=override_dict
        )
    elif model_format == 'netcdf':
        if scenario is not None or override_dict is not None:
            raise ValueError(
                'When loading a pre-built model from NetCDF, the '
                '--scenario and --override_dict options are not available.'
            )
        model = read_netcdf(model_file)
    else:
        raise ValueError('Invalid model format: {}'.format(model_format))

    return model
Beispiel #2
0
def _run_setup_model(model_file, scenario, model_format, override_dict):
    """
    Build model in CLI commands. Returns ``model``, a ready-to-run
    calliope.Model instance.

    """
    # Try to determine model file type if not given explicitly
    if model_format is None:
        if model_file.split(".")[-1] in ["yaml", "yml"]:
            model_format = "yaml"
        elif model_file.split(".")[-1] in ["nc", "nc4", "netcdf"]:
            model_format = "netcdf"
        else:
            raise ValueError(
                "Cannot determine model file format based on file "
                'extension for "{}". Set format explicitly with '
                "--model_format.".format(model_file))

    if model_format == "yaml":
        model = Model(model_file,
                      scenario=scenario,
                      override_dict=override_dict)
    elif model_format == "netcdf":
        if scenario is not None or override_dict is not None:
            raise ValueError(
                "When loading a pre-built model from NetCDF, the "
                "--scenario and --override_dict options are not available.")
        model = read_netcdf(model_file)
    else:
        raise ValueError("Invalid model format: {}".format(model_format))

    return model
Beispiel #3
0
def run(config_file, override_file, save_netcdf, save_csv, save_plots,
        save_logs, debug, pdb, profile, profile_filename):
    """Execute the given model."""
    if debug:
        print(_get_version())
    logging.captureWarnings(True)
    start_time = datetime.datetime.now()
    with format_exceptions(debug, pdb, profile, profile_filename, start_time):
        if save_csv is None and save_netcdf is None:
            print(
                '!!!\nWARNING: Neither save_csv nor save_netcdf have been '
                'specified. Model will run without saving results!\n!!!\n'
            )
        tstart = start_time.strftime(_time_format)
        print('Calliope run starting at {}\n'.format(tstart))
        override_dict = {
            'run.save_logs': save_logs
        }
        model = Model(
            config_file, override_file=override_file, override_dict=override_dict
        )
        model_name = model._model_run.get_key('model.name', default='None')
        print('Model name:   {}'.format(model_name))
        msize = '{locs} locations, {techs} technologies, {times} timesteps'.format(
            locs=len(model._model_run.sets['locs']),
            techs=(
                len(model._model_run.sets['techs_non_transmission']) +
                len(model._model_run.sets['techs_transmission_names'])
            ),
            times=len(model._model_run.sets['timesteps']))
        print('Model size:   {}\n'.format(msize))
        print('Starting model run...')
        model.run()
        if save_csv:
            print('Saving CSV results to directory: {}'.format(save_csv))
            model.to_csv(save_csv)
        if save_netcdf:
            print('Saving NetCDF results to file: {}'.format(save_netcdf))
            model.to_netcdf(save_netcdf)
        if save_plots:
            print('Saving HTML file with plots to: {}'.format(save_plots))
            model.plot.summary(out_file=save_plots)
        print_end_time(start_time)
Beispiel #4
0
def run_model(run_id, model_path, user_id, *args, **kwargs):
    """
    Run calliope model using asynchronous way with Celery.
    Please refer to
    https://calliope.readthedocs.io/en/stable/_modules/calliope/core/model.html#Model

    Parameters
    ----------
    run_id: int, the id of api.models.Run instance.
    model_path: str, the path to the model yaml file.
    user_id: int, the user id of current task.

    Returns
    -------
    dict, the outputs of model run, including netcdf, csv and plots.

    """
    # Logging Config
    log_file = os.path.join(os.path.dirname(os.path.dirname(model_path)),
                            "logs.html")
    logger = get_model_logger(log_file)

    # Update run status
    run = Run.objects.get(id=run_id)
    run.status = task_status.RUNNING
    run.save()

    # Model run
    model = CalliopeModel(config=model_path, *args, **kwargs)
    model.run()
    logger.info("Backend: Model runs successfully!")

    # Model outputs in csv
    base_path = os.path.dirname(os.path.dirname(model_path))
    logger.info("Backend: Saving model results...")
    try:
        if not os.path.exists(base_path + "/outputs"):
            os.makedirs(base_path + "/outputs", exist_ok=True)
        save_outputs = os.path.join(base_path, "outputs/model_outputs")
        if os.path.exists(save_outputs):
            shutil.rmtree(save_outputs)
        model.to_csv(save_outputs)
        logger.info("Backend: Model outputs was saved.")
    except Exception as e:
        logger.error("Backend: Failed to save model outputs.")
        logger.error(str(e))
        save_outputs = ""

    # Model plots in html
    try:
        if not os.path.exists(base_path + "/plots"):
            os.makedirs(base_path + "/plots", exist_ok=True)
        save_plots = os.path.join(base_path, "plots/model_plots.html")
        if os.path.exists(save_plots):
            os.remove(save_plots)
        model.plot.summary(to_file=save_plots)
        logger.info("Backend: Model plots was saved.")
    except Exception as e:
        logger.error("Backend: Failed to save model plots.")
        logger.error(str(e))
        save_plots = ""

    # Model logs in plain text
    save_logs = logger.handlers[0].baseFilename

    return {
        "run_id": run_id,
        "save_outputs": save_outputs,
        "save_plots": save_plots,
        "save_logs": save_logs
    }
Beispiel #5
0
def run(model_file, override_file, save_netcdf, save_csv, save_plots,
        save_logs, model_format, debug, quiet, pdb, profile, profile_filename):
    """
    Execute the given model. Tries to guess from the file extension whether
    ``model_file`` is a YAML file or a pre-built model saved to NetCDF.
    This can also explicitly be set with the --model_format=yaml or
    --model_format=netcdf option.

    """
    if debug:
        print(_get_version())

    set_quietness_level(quiet)

    logging.captureWarnings(True)
    pywarning_logger = logging.getLogger('py.warnings')
    pywarning_logger.addHandler(console)

    start_time = datetime.datetime.now()
    with format_exceptions(debug, pdb, profile, profile_filename, start_time):
        if save_csv is None and save_netcdf is None:
            click.secho(
                '\n!!!\nWARNING: No options to save results have been '
                'specified.\nModel will run without saving results!\n!!!\n',
                fg='red',
                bold=True)
        tstart = start_time.strftime(_time_format)
        print('Calliope {} starting at {}\n'.format(__version__, tstart))

        # Try to determine model file type if not given explicitly
        if model_format is None:
            if model_file.split('.')[-1] in ['yaml', 'yml']:
                model_format = 'yaml'
            elif model_file.split('.')[-1] in ['nc', 'nc4', 'netcdf']:
                model_format = 'netcdf'
            else:
                raise ValueError(
                    'Cannot determine model file format based on file '
                    'extension for "{}". Set format explicitly with '
                    '--model_format.'.format(model_file))

        if model_format == 'yaml':
            override_dict = {'run.save_logs': save_logs}
            model = Model(model_file,
                          override_file=override_file,
                          override_dict=override_dict)
        elif model_format == 'netcdf':
            if override_file is not None:
                raise ValueError(
                    'Overrides cannot be applied when loading a pre-built '
                    'model from NetCDF. Please run without --override options.'
                )
            model = read_netcdf(model_file)
            if save_logs is not None:
                model._model_data.attrs['run.save_logs'] = save_logs
        else:
            raise ValueError('Invalid model format: {}'.format(model_format))

        print(model.info() + '\n')
        print('Starting model run...')
        model.run()

        termination = model._model_data.attrs.get('termination_condition',
                                                  'unknown')
        if save_csv:
            print('Saving CSV results to directory: {}'.format(save_csv))
            model.to_csv(save_csv)
        if save_netcdf:
            print('Saving NetCDF results to file: {}'.format(save_netcdf))
            model.to_netcdf(save_netcdf)
        if save_plots:
            if termination == 'optimal':
                print('Saving HTML file with plots to: {}'.format(save_plots))
                model.plot.summary(to_file=save_plots)
            else:
                click.secho(
                    'Model termination condition non-optimal. Not saving plots',
                    fg='red',
                    bold=True)
        print_end_time(start_time)
Beispiel #6
0
 def builtin_model(self):
     model = Model()
     model.run()
     return model
Beispiel #7
0
def run(model_file, override_file, save_netcdf, save_csv, save_plots,
        save_logs, model_format,
        debug, pdb, profile, profile_filename):
    """
    Execute the given model. Tries to guess from the file extension whether
    ``model_file`` is a YAML file or a pre-built model saved to NetCDF.
    This can also explicitly be set with the --model_format=yaml or
    --model_format=netcdf option.

    """
    if debug:
        print(_get_version())
    logging.captureWarnings(True)
    start_time = datetime.datetime.now()
    with format_exceptions(debug, pdb, profile, profile_filename, start_time):
        if save_csv is None and save_netcdf is None:
            print(
                '!!!\nWARNING: Neither save_csv nor save_netcdf have been '
                'specified. Model will run without saving results!\n!!!\n'
            )
        tstart = start_time.strftime(_time_format)
        print('Calliope run starting at {}\n'.format(tstart))

        # Try to determine model file type if not given explicitly
        if model_format is None:
            if model_file.split('.')[-1] in ['yaml', 'yml']:
                model_format = 'yaml'
            elif model_file.split('.')[-1] in ['nc', 'nc4', 'netcdf']:
                model_format = 'netcdf'
            else:
                raise ValueError(
                    'Cannot determine model file format based on file '
                    'extension for "{}". Set format explicitly with '
                    '--model_format.'.format(model_file)
                )

        if model_format == 'yaml':
            override_dict = {
                'run.save_logs': save_logs
            }
            model = Model(
                model_file, override_file=override_file, override_dict=override_dict
            )
        elif model_format == 'netcdf':
            if override_file is not None:
                raise ValueError(
                    'Overrides cannot be applied when loading a pre-built '
                    'model from NetCDF. Please run without --override options.'
                )
            model = read_netcdf(model_file)
            if save_logs is not None:
                model._model_data.attrs['run.save_logs'] = save_logs
        else:
            raise ValueError('Invalid model format: {}'.format(model_format))

        # FIXME: get this from model._model_data rather than _model_run
        model_name = model._model_data.attrs.get('model.name', 'None')
        print('Model name:   {}'.format(model_name))
        msize = '{locs} locations, {techs} technologies, {times} timesteps'.format(
            locs=len(model._model_data.coords['locs'].values),
            techs=(
                len(model._model_data.coords['techs_non_transmission'].values) +
                len(model._model_data.coords['techs_transmission_names'].values)
            ),
            times=len(model._model_data.coords['timesteps'].values))
        print('Model size:   {}\n'.format(msize))
        print('Starting model run...')
        model.run()
        if save_csv:
            print('Saving CSV results to directory: {}'.format(save_csv))
            model.to_csv(save_csv)
        if save_netcdf:
            print('Saving NetCDF results to file: {}'.format(save_netcdf))
            model.to_netcdf(save_netcdf)
        if save_plots:
            print('Saving HTML file with plots to: {}'.format(save_plots))
            model.plot.summary(out_file=save_plots)
        print_end_time(start_time)
Beispiel #8
0
def _has_consistent_outputs_6_region(
        model: calliope.Model) -> bool:  # pragma: no cover
    """Check if model outputs (costs, generation levels, emissions) are internally consistent.
    Log errors whenever they are not.

    Parameters:
    -----------
    model: instance of SixRegionModel
    """

    passing = True  # Changes to False if any outputs are found to be inconsistent
    cost_total_v1 = 0

    costs = _get_technology_info(model=model)

    sum_out = model.get_summary_outputs()
    ts_out = model.get_timeseries_outputs()
    res = model.results

    # Normalise install costs to same temporal scale as generation costs
    corrfac = model.num_timesteps / 8760

    # Get list of tech-location pairs
    regions = list(model._model_run['locations'].keys())
    tech_locations = [i.split('_') for i in costs.index]
    generation_tech_locations = [
        i for i in tech_locations if i[0] != 'transmission'
    ]
    transmission_tech_locations = [
        i for i in tech_locations if i[0] == 'transmission'
    ]

    # Test if generation technology installation costs are consistent
    if model.run_mode == 'plan':
        for tech, region in generation_tech_locations:
            if tech == 'unmet':
                continue  # Unmet demand doesn't have meaningful install cost
            cost_v1 = corrfac * float(costs.loc[f'{tech}_{region}', 'install']
                                      * sum_out.loc[f'cap_{tech}_{region}'])
            cost_v2 = float(
                res.cost_investment.loc['monetary',
                                        f'{region}::{tech}_{region}'])
            if not np.isclose(cost_v1, cost_v2, rtol=0., atol=0.1):
                logger.error(
                    f'Cannot recreate {tech} install costs in {region} -- '
                    f'manual: {cost_v1}, model: {cost_v2}.')
                passing = False
            cost_total_v1 += cost_v1

    # Test if transmission technology installation costs are consistent
    if model.run_mode == 'plan':
        for tech, region, region_to in transmission_tech_locations:
            cost_v1 = corrfac * float(
                costs.loc[f'{tech}_{region}_{region_to}', 'install'] *
                sum_out.loc[f'cap_transmission_{region}_{region_to}'])
            cost_v2 = 2 * float(res.cost_investment.loc[
                'monetary',
                f'{region}::{tech}_{region}_{region_to}:{region_to}'])
            if not np.isclose(cost_v1, cost_v2, rtol=0., atol=0.1):
                logger.error(
                    f'Cannot recreate {tech} install costs from {region} to {region_to} -- '
                    f'manual: {cost_v1}, model: {cost_v2}.')
                passing = False
            cost_total_v1 += cost_v1

    # Test if generation costs are consistent
    for tech, region in generation_tech_locations:
        cost_v1 = float(costs.loc[f'{tech}_{region}', 'generation'] *
                        sum_out.loc[f'gen_{tech}_{region}'])
        cost_v2 = float(res.cost_var.loc['monetary',
                                         f'{region}::{tech}_{region}'].sum())
        if not np.isclose(cost_v1, cost_v2, rtol=0., atol=0.1):
            logger.error(
                f'Cannot recreate {tech} generation costs in {region} -- '
                f'manual: {cost_v1}, model: {cost_v2}.')
            passing = False
        cost_total_v1 += cost_v1

    # Test if total costs are consistent
    if model.run_mode == 'plan':
        cost_total_v2 = float(res.cost.loc['monetary'].sum())
        if not np.isclose(cost_total_v1, cost_total_v2, rtol=0., atol=0.1):
            logger.error(
                f'Cannot recreate system cost -- manual: {cost_total_v1}, model: {cost_total_v2}.'
            )
            passing = False

    # Test if emissions are consistent
    for tech, region in generation_tech_locations:
        emission_v1 = float(costs.loc[f'{tech}_{region}', 'emissions'] *
                            sum_out.loc[f'gen_{tech}_{region}'])
        emission_v2 = float(
            res.cost_var.loc['emissions', f'{region}::{tech}_{region}'].sum())
        if not np.isclose(cost_v1, cost_v2, rtol=0., atol=0.1):
            logger.error(f'Cannot recreate {tech} emissions in {region} -- '
                         f'manual: {emission_v1}, model: {emission_v2}.')
            passing = False
        cost_total_v1 += cost_v1

    # Test if supply matches demand
    generation_total = float(
        sum_out.filter(regex='gen_.*_region.*', axis=0).sum())
    demand_total = float(sum_out.loc['demand_total'])
    if not np.isclose(generation_total, demand_total, rtol=0., atol=0.1):
        logger.error(
            f'Supply-demand mismatch -- generation: {generation_total}, demand: {demand_total}.'
        )
        passing = False

    # Test that generation levels are all nonnegative and add up to the demand
    if not (ts_out.filter(like='gen', axis=1) >= 0).all().all():
        logger.error(
            f'Some generation/demand levels are negative:\n\n{ts_out}\n.')
        passing = False
    if not np.allclose(ts_out.filter(like='gen', axis=1).sum(axis=1),
                       ts_out.filter(like='demand', axis=1).sum(axis=1),
                       rtol=0.,
                       atol=0.1):
        logger.error(f'Generation does not add up to demand:\n\n{ts_out}\n.')
        passing = False

    # Test regional power balance: generation equals demand + transmission out of region
    for region in regions:
        generation_total_region = ts_out.filter(regex=f'gen_.*_{region}',
                                                axis=1).sum(axis=1)
        demand_total_region = ts_out.filter(regex=f'demand_{region}',
                                            axis=1).sum(axis=1)
        transmission_total_from_region = (
            ts_out.filter(regex=f'transmission_{region}_region.*',
                          axis=1).sum(axis=1) -
            ts_out.filter(regex=f'transmission_region.*_{region}',
                          axis=1).sum(axis=1))
        if not np.allclose(
                generation_total_region,
                demand_total_region + transmission_total_from_region,
                rtol=0.,
                atol=0.1):
            balance_info = pd.DataFrame()
            balance_info['generation'] = generation_total_region
            balance_info['demand'] = demand_total_region
            balance_info['transmission_out'] = transmission_total_from_region
            logger.error(
                f'Power balance not satisfied in {region}:\n\n{balance_info}\n.'
            )
            passing = False

    return passing
Beispiel #9
0
def _has_consistent_outputs_1_region(
        model: calliope.Model) -> bool:  # pragma: no cover
    """Check if model outputs (costs, generation levels, emissions) are internally consistent.
    Log errors whenever they are not.

    Parameters:
    -----------
    model: instance of OneRegionModel
    """

    passing = True  # Changes to False if any outputs are found to be inconsistent
    cost_total_v1 = 0

    costs = _get_technology_info(model=model)
    techs = list(costs.index)

    sum_out = model.get_summary_outputs()
    ts_out = model.get_timeseries_outputs()
    res = model.results

    # Normalise install costs to same temporal scale as generation costs
    corrfac = model.num_timesteps / 8760

    # Test if generation technology installation costs are consistent
    if model.run_mode == 'plan':
        for tech in techs:
            if tech == 'unmet':
                continue  # Unmet demand doesn't have meaningful install cost
            cost_v1 = corrfac * float(
                costs.loc[tech, 'install'] * sum_out.loc[f'cap_{tech}_total'])
            cost_v2 = float(res.cost_investment.loc['monetary',
                                                    f'region1::{tech}'])
            if not np.isclose(cost_v1, cost_v2, rtol=0., atol=0.1):
                logger.error(
                    f'Cannot recreate {tech} install costs -- manual: {cost_v1}, model: {cost_v2}.'
                )
                passing = False
            cost_total_v1 += cost_v1

    # Test if generation costs are consistent
    for tech in techs:
        cost_v1 = float(costs.loc[tech, 'generation'] *
                        sum_out.loc[f'gen_{tech}_total'])
        cost_v2 = float(res.cost_var.loc['monetary', f'region1::{tech}'].sum())
        if not np.isclose(cost_v1, cost_v2, rtol=0., atol=0.1):
            logger.error(
                f'Cannot recreate {tech} generation costs -- manual: {cost_v1}, model: {cost_v2}.'
            )
            passing = False
        cost_total_v1 += cost_v1

    # Test if total costs are consistent
    if model.run_mode == 'plan':
        cost_total_v2 = float(res.cost.loc['monetary'].sum())
        if not np.isclose(cost_total_v1, cost_total_v2, rtol=0., atol=0.1):
            logger.error(
                f'Cannot recreate system cost -- manual: {cost_total_v1}, model: {cost_total_v2}.'
            )
            passing = False

    # Test if emissions are consistent
    for tech in techs:
        emission_v1 = float(costs.loc[tech, 'emissions'] *
                            sum_out.loc[f'gen_{tech}_total'])
        emission_v2 = float(res.cost_var.loc['emissions',
                                             f'region1::{tech}'].sum())
        if not np.isclose(cost_v1, cost_v2, rtol=0., atol=0.1):
            logger.error(
                f'Cannot recreate {tech} emissions -- manual: {emission_v1}, model: {emission_v2}.'
            )
            passing = False
        cost_total_v1 += cost_v1

    # Test if supply matches demand
    generation_total = float(
        sum_out.filter(regex='gen_.*_total', axis=0).sum())
    demand_total = float(sum_out.loc['demand_total'])
    if not np.isclose(generation_total, demand_total, rtol=0., atol=0.1):
        logger.error(
            f'Supply-demand mismatch -- generation: {generation_total}, demand: {demand_total}.'
        )
        passing = False

    # Test that generation levels are all nonnegative and add up to the demand
    if not (ts_out.filter(like='gen', axis=1) >= 0).all().all():
        logger.error(
            f'Some generation/demand levels are negative:\n\n{ts_out}\n.')
        passing = False
    if not np.allclose(ts_out.filter(like='gen', axis=1).sum(axis=1),
                       ts_out.filter(like='demand', axis=1).sum(axis=1),
                       rtol=0.,
                       atol=0.1):
        logger.error(f'Generation does not add up to demand:\n\n{ts_out}\n.')
        passing = False

    return passing
Beispiel #10
0
def run(model_file, scenario, save_netcdf, save_csv, save_plots,
        save_logs, model_format, override_dict,
        debug, quiet, pdb, profile, profile_filename):
    """
    Execute the given model. Tries to guess from the file extension whether
    ``model_file`` is a YAML file or a pre-built model saved to NetCDF.
    This can also explicitly be set with the --model_format=yaml or
    --model_format=netcdf option.

    """
    if debug:
        print(_get_version())

    set_quietness_level(quiet)

    logging.captureWarnings(True)
    pywarning_logger = logging.getLogger('py.warnings')
    pywarning_logger.addHandler(console)

    start_time = datetime.datetime.now()
    with format_exceptions(debug, pdb, profile, profile_filename, start_time):
        if save_csv is None and save_netcdf is None:
            click.secho(
                '\n!!!\nWARNING: No options to save results have been '
                'specified.\nModel will run without saving results!\n!!!\n',
                fg='red', bold=True
            )
        tstart = start_time.strftime(_time_format)
        print('Calliope {} starting at {}\n'.format(__version__, tstart))

        # Try to determine model file type if not given explicitly
        if model_format is None:
            if model_file.split('.')[-1] in ['yaml', 'yml']:
                model_format = 'yaml'
            elif model_file.split('.')[-1] in ['nc', 'nc4', 'netcdf']:
                model_format = 'netcdf'
            else:
                raise ValueError(
                    'Cannot determine model file format based on file '
                    'extension for "{}". Set format explicitly with '
                    '--model_format.'.format(model_file)
                )

        if model_format == 'yaml':
            model = Model(
                model_file, scenario=scenario, override_dict=override_dict
            )
        elif model_format == 'netcdf':
            if scenario is not None or override_dict is not None:
                raise ValueError(
                    'When loading a pre-built model from NetCDF, the '
                    '--scenario and --override_dict options are not available.'
                )
            model = read_netcdf(model_file)
        else:
            raise ValueError('Invalid model format: {}'.format(model_format))

        if save_logs:
            model._model_data.attrs['run.save_logs'] = save_logs

        print(model.info() + '\n')
        print('Starting model run...')
        model.run()

        termination = model._model_data.attrs.get(
            'termination_condition', 'unknown')
        if save_csv:
            print('Saving CSV results to directory: {}'.format(save_csv))
            model.to_csv(save_csv)
        if save_netcdf:
            print('Saving NetCDF results to file: {}'.format(save_netcdf))
            model.to_netcdf(save_netcdf)
        if save_plots:
            if termination == 'optimal':
                print('Saving HTML file with plots to: {}'.format(save_plots))
                model.plot.summary(to_file=save_plots)
            else:
                click.secho(
                    'Model termination condition non-optimal. Not saving plots',
                    fg='red', bold=True
                )
        print_end_time(start_time)