Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Revision d1bb46dfe8bc7efe6257b5ce222c04bfe8aedaab authored by Ryan Wick on 10 May 2018, 23:16:07 UTC, committed by Ryan Wick on 10 May 2018, 23:16:07 UTC
Fix typo
1 parent 260bb4d
  • Files
  • Changes
  • c0d2609
  • /
  • src
  • /
  • main.cpp
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • revision
  • directory
  • content
revision badge
swh:1:rev:d1bb46dfe8bc7efe6257b5ce222c04bfe8aedaab
directory badge
swh:1:dir:3a1cefd215cbb8c5c686cc840a95178f90953d5e
content badge
swh:1:cnt:68b6d21cd13a620e8990df8d1ca89e58d675ce5c

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • revision
  • directory
  • content
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
main.cpp
// Copyright 2017 Ryan Wick

// This file is part of Filtlong

// Filtlong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
// version.

// Filtlong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
// details.

// You should have received a copy of the GNU General Public License along with Filtlong.  If not, see
// <http://www.gnu.org/licenses/>.


#include <iostream>
#include <zlib.h>
#include <stdio.h>
#include <vector>
#include <limits>
#include <unordered_map>
#include <utility>
#include <math.h>

#include "kseq.h"
#include "read.h"
#include "arguments.h"
#include "kmers.h"
#include "misc.h"

#define PROGRAM_VERSION "0.2.0"

KSEQ_INIT(gzFile, gzread)


int main(int argc, char **argv)
{
    Arguments args(argc, argv);
    if (args.parsing_result == BAD)
        return 1;
    else if (args.parsing_result == HELP)
        return 0;
    else if (args.parsing_result == VERSION) {
        std::cout << "Filtlong v" << PROGRAM_VERSION << "\n";
        return 0;
    }

    std::cerr << "\n";

    // Read through references and save 16-mers. For assembly references, this will save all 16-mers in the assembly.
    // For Illumina read references, the k-mer needs to appear a few times before it's added to the set.
    Kmers kmers;
    if (args.assembly_set || args.illumina_reads.size() > 0) {
        if (args.assembly_set)
            kmers.add_assembly_fasta(args.assembly);
        if (args.illumina_reads.size() > 0)
            kmers.add_read_fastqs(args.illumina_reads);
    }

    // Read through input long reads once, storing them as Read objects and calculating their scores.
    // While we go, make sure there are no duplicate read names. Quit with an error if so.
    long long total_bases = 0;
    long long last_progress = 0;
    std::vector<Read*> reads;
    std::unordered_map<std::string, Read*> read_dict;
    if (!args.verbose)
        std::cerr << "Scoring long reads\n";
    int l;
    gzFile fp = gzopen(args.input_reads.c_str(), "r");
    kseq_t * seq = kseq_init(fp);

    bool any_fasta = false;
    bool any_fastq = false;

    while (true) {
        l = kseq_read(seq);
        if (l == -1)  // end of file
            break;
        if (l == -2) {
            std::cerr << "Error: incorrect FASTQ format for read " << seq->name.s << "\n";
            return 1;
        }
        if (l == -3) {
            std::cerr << "Error reading " << args.input_reads << "\n";
            return 1;
        }
        else {
            total_bases += seq->seq.l;
            std::string read_name = seq->name.s;

            bool fasta_format = (seq->qual.l == 0 && seq->seq.l > 0);
            bool fastq_format = (seq->qual.l > 0 && seq->seq.l > 0 && seq->qual.l == seq->seq.l);

            any_fasta = (any_fasta || fasta_format);
            any_fastq = (any_fastq || fastq_format);
            if (any_fasta && any_fastq) {
                std::cerr << "\n\n" << "Error: could not parse input reads" << "\n";
                std::cerr << "  problem occurred at read " << read_name << "\n";
                return 1;
            }

            if (fasta_format && kmers.empty()) {
                std::cerr << "\n\n" << "Error: FASTA input not supported without an external reference" << "\n";
                return 1;
            }

            Read * read = new Read(read_name, seq->seq.s, seq->qual.s, int(seq->seq.l), &kmers, &args);
            reads.push_back(read);
            if (args.verbose)
                read->print_verbose_read_info();

            if (read_dict.find(read->m_name) != read_dict.end()) {
                std::cerr << "Error: duplicate read name: " << read->m_name << "\n";
                return 1;
            }
            read_dict[read->m_name] = read;

            if (total_bases - last_progress >= 483611) {  // a big prime number so progress updates don't round off
                last_progress = total_bases;
                if (!args.verbose)
                    print_read_score_progress(reads.size(), total_bases);
            }
        }
    }
    kseq_destroy(seq);
    gzclose(fp);
    if (!args.verbose)
        print_read_score_progress(reads.size(), total_bases);
    std::cerr << "\n";

    // Determine the output format.
    bool fasta_output = any_fasta;
    bool fastq_output = any_fastq;

    // Gather up reads to output. If a read has been trimmed/split, it's these child reads which we use, not the
    // parent read.
    std::vector<Read*> reads2;
    for (auto read : reads) {
        if (read->m_child_reads.size() == 0) {
            reads2.push_back(read);
        }
        else {
            for (auto child : read->m_child_reads)
                reads2.push_back(child);
        }
    }
    size_t longest_read_name = 0;
    for (auto read : reads2) {
        if (read->m_name.size() > longest_read_name)
            longest_read_name = read->m_name.size();
    }

    // If --trim or --split was used, display some summary info here.
    if (args.trim || args.split_set) {
        long long total_after_trim_split = 0;
        for (auto read : reads2)
            total_after_trim_split += read->m_length;
        if (args.trim && args.split_set)
            std::cerr << "  after trimming and splitting: ";
        else if (args.trim)
            std::cerr << "  after trimming: ";
        else
            std::cerr << "  after splitting: ";
        std::cerr << int_to_string(reads2.size()) << " reads (" << int_to_string(total_after_trim_split) << " bp)\n";
    }
    std::cerr << "\n";

    // Go through the mean quality scores and find the min, max, mean and standard deviation.
    double min_quality = 100.0;
    double max_quality = 0.0;
    double quality_sum = 0.0;
    for (auto read : reads2) {
        quality_sum += read->m_mean_quality;
        if (read->m_mean_quality > max_quality)
            max_quality = read->m_mean_quality;
        if (read->m_mean_quality < min_quality)
            min_quality = read->m_mean_quality;
    }
    double mean_quality = quality_sum / reads2.size();
    double stdev_sum = 0.0;
    for (auto read : reads2) {
        double mean_diff = read->m_mean_quality - mean_quality;
        stdev_sum += mean_diff * mean_diff;
    }
    double stdev_quality = sqrt(stdev_sum / reads2.size());
    double min_z_score, max_z_score;
    if (stdev_quality > 0.0) {
        min_z_score = (min_quality - mean_quality) / stdev_quality;
        max_z_score = (max_quality - mean_quality) / stdev_quality;
    }
    else {
        min_z_score = 1.0;
        max_z_score = 1.0;
    }
    double max_min_z_diff = max_z_score - min_z_score;

    // Now normalise each read's quality scores.
    if (args.verbose)
        std::cerr << "\n\n" << "Read name" << "\t" << "Length score" << "\t" << "Mean quality score" << "\t"
                  << "Window quality score" << "\t" << "Final score" << "\n";
    for (auto read : reads2) {
        double window_ratio = read->m_window_quality / read->m_mean_quality;
        if (window_ratio > 1.0)
            window_ratio = 1.0;
        double quality_z_score = (read->m_mean_quality - mean_quality) / stdev_quality;
        read->m_mean_quality = 100.0 * (quality_z_score - min_z_score) / max_min_z_diff;
        read->m_window_quality = read->m_mean_quality * window_ratio;
        read->set_final_score(args.length_weight, args.mean_q_weight, args.window_q_weight);
        if (args.verbose)
            read->print_scores(longest_read_name);
    }
    if (args.verbose)
        std::cerr << "\n";

    // If the user set thresholds using either --target_bases or --keep_percent, then we need to see which additional
    // reads should be labelled as failed.
    if (args.target_bases_set || args.keep_percent_set) {
        std::cerr << "Filtering long reads\n";

        // See how many bases have already been passed.
        long long passed_bases = 0;
        for (auto read : reads2) {
            if (read->m_passed)
                passed_bases += read->m_length;
        }

        // Determine how many bases we should keep.
        long long target_bases;
        if (args.target_bases_set)
            target_bases = args.target_bases;
        else
            target_bases = std::numeric_limits<long long>::max();
        if (args.keep_percent_set) {
            long long keep_target = (long long)((args.keep_percent / 100.0) * total_bases);
            target_bases = std::min(target_bases, keep_target);
        }
        std::cerr << "  target: " << int_to_string(target_bases) << " bp\n";
        if (target_bases >= total_bases) {
            std::cerr << "  not enough reads to reach target\n";
        }
        else if (target_bases >= passed_bases) {
            std::cerr << "  reads already fall below target after filtering\n";
        }
        else {
            // Sort reads from best to worst.
            std::sort(reads2.begin(), reads2.end(),
                      [](const Read* a, const Read* b) {return a->m_final_score > b->m_final_score;});

            // Fail all reads after the threshold has been met.
            long long bases_so_far = 0;
            for (auto read : reads2) {
                if (read->m_passed && bases_so_far < target_bases)
                    bases_so_far += read->m_length;
                else
                    read->m_passed = false;
            }
            std::cerr << "  keeping " << int_to_string(bases_so_far) << " bp\n";
        }
        std::cerr << "\n";
    }

    // Read through input reads again, this time outputting the keepers to stdout and ignoring the failures.
    std::cerr << "Outputting passed long reads\n";
    fp = gzopen(args.input_reads.c_str(), "r");
    seq = kseq_init(fp);
    while ((l = kseq_read(seq)) >= 0) {
        Read * read = read_dict[seq->name.s];

        if (read->m_child_reads.size() == 0) {
            if (read->m_passed) {
                std::cout << (fasta_output ? ">" : "@");
                std::cout << seq->name.s;
                if (seq->comment.l > 0)
                    std::cout << " " << seq->comment.s;
                std::cout << "\n";
                std::cout << seq->seq.s << "\n";
                if (fastq_output) {
                    std::cout << "+\n";
                    std::cout << seq->qual.s << "\n";
                }
            }
        }
        else {
            for (size_t i = 0; i < read->m_child_reads.size(); ++i) {
                Read * child_read = read->m_child_reads[i];
                if (child_read->m_passed) {
                    std::pair<int,int> child_read_range = read->m_child_read_ranges[i];
                    int start = child_read_range.first;
                    int end = child_read_range.second;
                    int length = end - start;
                    if (length > 0) {
                        std::cout << (fasta_output ? ">" : "@");
                        std::cout << child_read->m_name;
                        if (seq->comment.l > 0)
                            std::cout << " " << seq->comment.s;
                        std::cout << "\n";

                        std::string seq_str = seq->seq.s;
                        std::cout << seq_str.substr(start, length) << "\n";

                        if (fastq_output) {
                            std::string qual_str = seq->qual.s;
                            std::cout << "+\n";
                            std::cout << qual_str.substr(start, length) << "\n";
                        }
                    }
                }
            }
        }
    }
    kseq_destroy(seq);
    gzclose(fp);

    // Clean up.
    for (auto read : reads)
        delete read;

    std::cerr << "\n";
    return 0;
}
The diff you're trying to view is too large. Only the first 1000 changed files have been loaded.
Showing with 0 additions and 0 deletions (0 / 0 diffs computed)
swh spinner

Computing file changes ...

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API