# Path: calliope/core/util/tools.py
# def load_function(source):
#     """
#     Returns a function from a module, given a source string of the form:
# 
#         'module.submodule.subsubmodule.function_name'
# 
#     """
#     module_string, function_string = source.rsplit(".", 1)
#     modules = sys.modules.keys()
#     # Check if module already loaded, if so, don't re-import it
#     if module_string in modules:
#         module = sys.modules[module_string]
#     # Else load the module
#     else:
#         module = importlib.import_module(module_string)
#     return getattr(module, function_string)


import pyomo.core as po
from calliope.core.util.tools import load_function
                        backend_model.unmet_demand[carrier, node, timestep]
                        - backend_model.unused_supply[carrier, node, timestep]
                    )
                    * backend_model.timestep_weights[timestep]
                    for [carrier, node, timestep] in backend_model.carriers
                    * backend_model.nodes
                    * backend_model.timesteps
                    if [carrier, node, timestep] in backend_model.unmet_demand._index
                )
                * backend_model.bigM
            )
            if backend_model.objective_sense == "maximize":
                unmet_demand *= -1
        else:
            unmet_demand = 0

        return (
            po.quicksum(
                po.quicksum(
                    backend_model.cost[class_name, node, tech]
                    for [node, tech] in backend_model.nodes * backend_model.techs
                    if [class_name, node, tech] in backend_model.cost._index
                )
                * weight
                for class_name, weight in backend_model.objective_cost_class.items()
            )
            + unmet_demand
        )

    backend_model.obj = po.Objective(

# Path: calliope/core/util/tools.py
# def load_function(source):
#     """
#     Returns a function from a module, given a source string of the form:
# 
#         'module.submodule.subsubmodule.function_name'
# 
#     """
#     module_string, function_string = source.rsplit(".", 1)
#     modules = sys.modules.keys()
#     # Check if module already loaded, if so, don't re-import it
#     if module_string in modules:
#         module = sys.modules[module_string]
#     # Else load the module
#     else:
#         module = importlib.import_module(module_string)
#     return getattr(module, function_string)


# Path: calliope/backend/pyomo/objective.py
import pyomo.core as po
from calliope.core.util.tools import load_function

                        backend_model.unmet_demand[carrier, node, timestep]
                        - backend_model.unused_supply[carrier, node, timestep]
                    )
                    * backend_model.timestep_weights[timestep]
                    for [carrier, node, timestep] in backend_model.carriers
                    * backend_model.nodes
                    * backend_model.timesteps
                    if [carrier, node, timestep] in backend_model.unmet_demand._index
                )
                * backend_model.bigM
            )
            if backend_model.objective_sense == "maximize":
                unmet_demand *= -1
        else:
            unmet_demand = 0

        return (
            po.quicksum(
                po.quicksum(
                    backend_model.cost[class_name, node, tech]
                    for [node, tech] in backend_model.nodes * backend_model.techs
                    if [class_name, node, tech] in backend_model.cost._index
                )
                * weight
                for class_name, weight in backend_model.objective_cost_class.items()
            )
            + unmet_demand
        )

    backend_model.obj = po.Objective(

        sense=load_function("pyomo.core." + backend_model.objective_sense),