Push zunrplorkwkt #70
@@ -70,6 +70,17 @@ pub struct SelectArgs {
|
|||||||
/// Overwrite existing output directory
|
/// Overwrite existing output directory
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub force: bool,
|
pub force: bool,
|
||||||
|
|
||||||
|
/// Copy each layer's unchanged kmer-identity files (mphf/unitigs/
|
||||||
|
/// evidence/fingerprint) instead of hard-linking them. By default
|
||||||
|
/// `select` hard-links these (never rewritten by a column
|
||||||
|
/// projection/aggregation, so a hard link is exact and avoids
|
||||||
|
/// transiently doubling disk usage for a large index), falling back to
|
||||||
|
/// a real copy only if linking itself fails (e.g. source and output on
|
||||||
|
/// different filesystems). Set this when the output must survive
|
||||||
|
/// independently of the source on disk.
|
||||||
|
#[arg(long)]
|
||||||
|
pub force_copy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Split a repeatable `<name>:<value>` argument. Exits on malformed input.
|
/// Split a repeatable `<name>:<value>` argument. Exits on malformed input.
|
||||||
@@ -128,6 +139,7 @@ pub fn run(args: SelectArgs) {
|
|||||||
let mut alg = Select::new(&src, &args.output, &specs, output_presence)
|
let mut alg = Select::new(&src, &args.output, &specs, output_presence)
|
||||||
.threshold(args.presence_threshold)
|
.threshold(args.presence_threshold)
|
||||||
.force(args.force)
|
.force(args.force)
|
||||||
|
.force_copy(args.force_copy)
|
||||||
.sparse(!args.dense)
|
.sparse(!args.dense)
|
||||||
.on_progress(|_: Progress| pb.inc(1));
|
.on_progress(|_: Progress| pb.inc(1));
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub struct Select<'a> {
|
|||||||
threshold: u32,
|
threshold: u32,
|
||||||
output_presence: bool,
|
output_presence: bool,
|
||||||
force: bool,
|
force: bool,
|
||||||
|
force_copy: bool,
|
||||||
sparse: bool,
|
sparse: bool,
|
||||||
reporter: Reporter,
|
reporter: Reporter,
|
||||||
on_progress: Option<Box<dyn FnMut(Progress) + Send + 'a>>,
|
on_progress: Option<Box<dyn FnMut(Progress) + Send + 'a>>,
|
||||||
@@ -43,6 +44,7 @@ impl<'a> Select<'a> {
|
|||||||
threshold: 0,
|
threshold: 0,
|
||||||
output_presence,
|
output_presence,
|
||||||
force: false,
|
force: false,
|
||||||
|
force_copy: false,
|
||||||
sparse: true,
|
sparse: true,
|
||||||
reporter: Reporter::new(),
|
reporter: Reporter::new(),
|
||||||
on_progress: None,
|
on_progress: None,
|
||||||
@@ -63,6 +65,18 @@ impl<'a> Select<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Always copy each layer's unchanged kmer-identity files
|
||||||
|
/// (mphf/unitigs/evidence/fingerprint) instead of hard-linking them
|
||||||
|
/// (default: `false` — hard link, falling back to a copy only if
|
||||||
|
/// linking itself fails). Set this when `output` must survive
|
||||||
|
/// independently of `src` on disk — a hard link still shares the same
|
||||||
|
/// underlying inode, so rewriting one path outside `select` would
|
||||||
|
/// affect the other.
|
||||||
|
pub fn force_copy(mut self, v: bool) -> Self {
|
||||||
|
self.force_copy = v;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Pack the output's presence matrices in the compact sparse format
|
/// Pack the output's presence matrices in the compact sparse format
|
||||||
/// rather than dense (default: `true`).
|
/// rather than dense (default: `true`).
|
||||||
pub fn sparse(mut self, v: bool) -> Self {
|
pub fn sparse(mut self, v: bool) -> Self {
|
||||||
@@ -124,7 +138,7 @@ impl Algorithm for Select<'_> {
|
|||||||
runner
|
runner
|
||||||
.run(
|
.run(
|
||||||
&order,
|
&order,
|
||||||
|i| select_partition(&dst, src, i, self.specs, self.threshold, self.output_presence),
|
|i| select_partition(&dst, src, i, self.specs, self.threshold, self.output_presence, self.force_copy),
|
||||||
|_, _, _| {
|
|_, _, _| {
|
||||||
done += 1;
|
done += 1;
|
||||||
if let Some(cb) = on_progress.as_mut() {
|
if let Some(cb) = on_progress.as_mut() {
|
||||||
|
|||||||
@@ -107,15 +107,26 @@ fn add_result(mb: &mut MatrixBuilder, r: AggResult) -> io::Result<()> {
|
|||||||
|
|
||||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Copy all plain files (not subdirectories) from `src_dir` to `dst_dir` —
|
/// Carries a layer's kmer identity (mphf/unitigs/evidence/fingerprint) from
|
||||||
/// carries a layer's kmer identity (mphf/unitigs/evidence/fingerprint)
|
/// `src_dir` to `dst_dir` **unchanged** — these files are never rewritten by
|
||||||
/// across unchanged; only the data matrix (a subdirectory) is rebuilt.
|
/// `select` (only the data matrix, a subdirectory, is rebuilt), so a hard
|
||||||
fn copy_layer_files(src_dir: &Path, dst_dir: &Path) -> io::Result<()> {
|
/// link is exact and avoids transiently doubling disk usage for the layer's
|
||||||
|
/// largest files on a big index. Falls back to a real copy per file when
|
||||||
|
/// linking fails (e.g. `src`/`dst` on different filesystems, `EXDEV`) or
|
||||||
|
/// when `force_copy` is set (`--force-copy`, for a destination meant to
|
||||||
|
/// survive independently of the source — a hard link still shares the same
|
||||||
|
/// underlying inode, so truncating/rewriting one path in place, outside
|
||||||
|
/// `select` itself, would affect the other).
|
||||||
|
fn copy_layer_files(src_dir: &Path, dst_dir: &Path, force_copy: bool) -> io::Result<()> {
|
||||||
for entry in fs::read_dir(src_dir)? {
|
for entry in fs::read_dir(src_dir)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.is_file() {
|
if !path.is_file() {
|
||||||
fs::copy(&path, dst_dir.join(entry.file_name()))?;
|
continue;
|
||||||
|
}
|
||||||
|
let dst_path = dst_dir.join(entry.file_name());
|
||||||
|
if force_copy || fs::hard_link(&path, &dst_path).is_err() {
|
||||||
|
fs::copy(&path, &dst_path)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -133,6 +144,7 @@ pub(crate) fn select_partition(
|
|||||||
specs: &[OutputCol],
|
specs: &[OutputCol],
|
||||||
threshold: u32,
|
threshold: u32,
|
||||||
output_presence: bool,
|
output_presence: bool,
|
||||||
|
force_copy: bool,
|
||||||
) -> OKIResult<()> {
|
) -> OKIResult<()> {
|
||||||
let src_partition = src.partition(i)?;
|
let src_partition = src.partition(i)?;
|
||||||
let dst_partition = dst.partition(i)?;
|
let dst_partition = dst.partition(i)?;
|
||||||
@@ -147,7 +159,7 @@ pub(crate) fn select_partition(
|
|||||||
.map_err(OKIError::Io)?
|
.map_err(OKIError::Io)?
|
||||||
.dir()
|
.dir()
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
copy_layer_files(src_layer.dir(), &dst_layer_dir).map_err(OKIError::Io)?;
|
copy_layer_files(src_layer.dir(), &dst_layer_dir, force_copy).map_err(OKIError::Io)?;
|
||||||
|
|
||||||
let group_mat: Box<dyn MatrixGroupOps> = match src_layer.content() {
|
let group_mat: Box<dyn MatrixGroupOps> = match src_layer.content() {
|
||||||
LayerContent::Count => {
|
LayerContent::Count => {
|
||||||
|
|||||||
Reference in New Issue
Block a user