<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def sortmerna_ref_cluster(seq_path=None, sortmerna_db=None, refseqs_fp=None, result_path=None, tabular=False, max_e_value=1, similarity=0.97, coverage=0.97, threads=1, best=1, HALT_EXEC=False ):
 """Launch sortmerna OTU picker Parameters seq_path : str filepath to query sequences. sortmerna_db : str indexed reference database. refseqs_fp : str filepath of reference sequences. result_path : str filepath to output OTU map. max_e_value : float, optional E-value threshold [default: 1]. similarity : float, optional similarity %id threshold [default: 0.97]. coverage : float, optional query coverage % threshold [default: 0.97]. threads : int, optional number of threads to use (OpenMP) [default: 1]. tabular : bool, optional output BLAST tabular alignments [default: False]. best : int, optional number of best alignments to output per read [default: 1]. Returns ------- clusters : dict of lists OTU ids and reads mapping to them failures : list reads which did not align """


    # Instantiate the object
    smr = Sortmerna(HALT_EXEC=HALT_EXEC)

    # Set input query sequences path
    if seq_path is not None:
        smr.Parameters['--reads'].on(seq_path)
    else:
        raise ValueError("Error: a read file is mandatory input.")

    # Set the input reference sequence + indexed database path
    if sortmerna_db is not None:
        smr.Parameters['--ref'].on("%s,%s" % (refseqs_fp, sortmerna_db))
    else:
        raise ValueError("Error: an indexed database for reference set %s must"
                         " already exist.\nUse indexdb_rna to index the"
                         " database." % refseqs_fp)

    if result_path is None:
        raise ValueError("Error: the result path must be set.")

    # Set output results path (for Blast alignments, clusters and failures)
    output_dir = dirname(result_path)
    if output_dir is not None:
        output_file = join(output_dir, "sortmerna_otus")
        smr.Parameters['--aligned'].on(output_file)

    # Set E-value threshold
    if max_e_value is not None:
        smr.Parameters['-e'].on(max_e_value)

    # Set similarity threshold
    if similarity is not None:
        smr.Parameters['--id'].on(similarity)

    # Set query coverage threshold
    if coverage is not None:
        smr.Parameters['--coverage'].on(coverage)

    # Set number of best alignments to output
    if best is not None:
        smr.Parameters['--best'].on(best)

    # Set Blast tabular output
    # The option --blast 3 represents an
    # m8 blast tabular output + two extra
    # columns containing the CIGAR string
    # and the query coverage
    if tabular:
        smr.Parameters['--blast'].on("3")

    # Set number of threads
    if threads is not None:
        smr.Parameters['-a'].on(threads)

    # Run sortmerna
    app_result = smr()

    # Put clusters into a map of lists
    f_otumap = app_result['OtuMap']
    rows = (line.strip().split('\t') for line in f_otumap)
    clusters = {r[0]: r[1:] for r in rows}

    # Put failures into a list
    f_failure = app_result['FastaForDenovo']
    failures = [re.split('>| ', label)[0]
                for label, seq in parse_fasta(f_failure)]

    # remove the aligned FASTA file and failures FASTA file
    # (currently these are re-constructed using pick_rep_set.py
    # further in the OTU-picking pipeline)
    smr_files_to_remove = [app_result['FastaForDenovo'].name,
                           app_result['FastaMatches'].name,
                           app_result['OtuMap'].name]

    return clusters, failures, smr_files_to_remove