<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def network_lopf_solve(network, snapshots=None, formulation="angles", solver_options={},solver_logfile=None, keep_files=False, free_memory={'pyomo'},extra_postprocessing=None):
 """ Solve linear optimal power flow for a group of snapshots and extract results. Parameters snapshots : list or index slice A list of snapshots to optimise, must be a subset of network.snapshots, defaults to network.snapshots formulation : string Formulation of the linear power flow equations to use; must be one of ["angles","cycles","kirchhoff","ptdf"]; must match formulation used for building the model. solver_options : dictionary A dictionary with additional options that get passed to the solver. (e.g. {'threads':2} tells gurobi to use only 2 cpus) solver_logfile : None|string If not None, sets the logfile option of the solver. keep_files : bool, default False Keep the files that pyomo constructs from OPF problem construction, e.g. .lp file - useful for debugging free_memory : set, default {'pyomo'} Any subset of {'pypsa', 'pyomo'}. Allows to stash `pypsa` time-series data away while the solver runs (as a pickle to disk) and/or free `pyomo` data after the solution has been extracted. extra_postprocessing : callable function This function must take three arguments `extra_postprocessing(network,snapshots,duals)` and is called after the model has solved and the results are extracted. It allows the user to extract further information about the solution, such as additional shadow prices. Returns ------- None """


    snapshots = _as_snapshots(network, snapshots)

    logger.info("Solving model using %s", network.opt.name)

    if isinstance(network.opt, PersistentSolver):
        args = []
    else:
        args = [network.model]

    if isinstance(free_memory, string_types):
        free_memory = {free_memory}

    if 'pypsa' in free_memory:
        with empty_network(network):
            network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
    else:
        network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)

    if logger.isEnabledFor(logging.INFO):
        network.results.write()

    status = network.results["Solver"][0]["Status"].key
    termination_condition = network.results["Solver"][0]["Termination condition"].key

    if status == "ok" and termination_condition == "optimal":
        logger.info("Optimization successful")
        extract_optimisation_results(network, snapshots, formulation,
                                     free_pyomo='pyomo' in free_memory,
                                     extra_postprocessing=extra_postprocessing)
    elif status == "warning" and termination_condition == "other":
        logger.warning("WARNING! Optimization might be sub-optimal. Writing output anyway")
        extract_optimisation_results(network, snapshots, formulation,
                                     free_pyomo='pyomo' in free_memory,
                                     extra_postprocessing=extra_postprocessing)
    else:
        logger.error("Optimisation failed with status %s and terminal condition %s"
              % (status, termination_condition))

    return status, termination_condition