Create an extended model for gap-filling.

    Create a :class:`psamm.metabolicmodel.MetabolicModel` with
    all reactions added (the reaction database in the model is taken
    to be the universal database) and also with artificial exchange
    and transport reactions added. Return the extended
    :class:`psamm.metabolicmodel.MetabolicModel`
    and a weight dictionary for added reactions in that model.

    Args:
        model: :class:`psamm.datasource.native.NativeModel`.
        db_penalty: penalty score for database reactions, default is `None`.
        ex_penalty: penalty score for exchange reactions, default is `None`.
        tb_penalty: penalty score for transport reactions, default is `None`.
        penalties: a dictionary of penalty scores for database reactions.

def create_extended_model(model, db_penalty=None, ex_penalty=None,
                          tp_penalty=None, penalties=None):
    """Create an extended model for gap-filling.

    Create a :class:`psamm.metabolicmodel.MetabolicModel` with
    all reactions added (the reaction database in the model is taken
    to be the universal database) and also with artificial exchange
    and transport reactions added. Return the extended
    :class:`psamm.metabolicmodel.MetabolicModel`
    and a weight dictionary for added reactions in that model.

    Args:
        model: :class:`psamm.datasource.native.NativeModel`.
        db_penalty: penalty score for database reactions, default is `None`.
        ex_penalty: penalty score for exchange reactions, default is `None`.
        tb_penalty: penalty score for transport reactions, default is `None`.
        penalties: a dictionary of penalty scores for database reactions.
    """

    # Create metabolic model
    model_extended = model.create_metabolic_model()
    extra_compartment = model.extracellular_compartment

    compartment_ids = set(c.id for c in model.compartments)

    # Add database reactions to extended model
    if len(compartment_ids) > 0:
        logger.info(
            'Using all database reactions in compartments: {}...'.format(
                ', '.join('{}'.format(c) for c in compartment_ids)))
        db_added = add_all_database_reactions(model_extended, compartment_ids)
    else:
        logger.warning(
            'No compartments specified in the model; database reactions will'
            ' not be used! Add compartment specification to model to include'
            ' database reactions for those compartments.')
        db_added = set()

    # Add exchange reactions to extended model
    logger.info(
        'Using artificial exchange reactions for compartment: {}...'.format(
            extra_compartment))
    ex_added = add_all_exchange_reactions(
        model_extended, extra_compartment, allow_duplicates=True)

    # Add transport reactions to extended model
    boundaries = model.compartment_boundaries
    if len(boundaries) > 0:
        logger.info(
            'Using artificial transport reactions for the compartment'
            ' boundaries: {}...'.format(
                '; '.join('{}<->{}'.format(c1, c2) for c1, c2 in boundaries)))
        tp_added = add_all_transport_reactions(
            model_extended, boundaries, allow_duplicates=True)
    else:
        logger.warning(
            'No compartment boundaries specified in the model;'
            ' artificial transport reactions will not be used!')
        tp_added = set()

    # Add penalty weights on reactions
    weights = {}
    if db_penalty is not None:
        weights.update((rxnid, db_penalty) for rxnid in db_added)
    if tp_penalty is not None:
        weights.update((rxnid, tp_penalty) for rxnid in tp_added)
    if ex_penalty is not None:
        weights.update((rxnid, ex_penalty) for rxnid in ex_added)

    if penalties is not None:
        for rxnid, penalty in iteritems(penalties):
            weights[rxnid] = penalty
    return model_extended, weights