Skip to content

Commit 6ebad20

Browse files
author
10x Genomics
committed
feat: Cell Ranger 9.0.1
1 parent fe06169 commit 6ebad20

15 files changed

Lines changed: 232 additions & 735 deletions

File tree

etc/cellranger_telemetry.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
---
33
config_version: 1
44
product: cellranger
5-
version: "9.0.0"
5+
version: "9.0.1"
66

77
groups:
88
os_stats:

lib/go/cmd/sitecheck/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121

2222
func main() {
2323
showVersion := flag.Bool("version", false, "Show version and exit.")
24+
flag.BoolVar(&sitecheck.Verbose, "verbose", false,
25+
"Show verbose error messages.")
2426
flag.Usage = func() {
2527
product := os.Getenv("TENX_PRODUCT")
2628
fmt.Fprintf(flag.CommandLine.Output(),

lib/go/sitecheck/checkers.go

Lines changed: 34 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,26 @@ func cstring(b []byte) string {
3939
}
4040
}
4141

42+
// If true, enable verbose error reporting.
43+
var Verbose bool
44+
45+
func verboseErrorf(format string, args ...any) {
46+
if Verbose {
47+
fmt.Fprintf(os.Stderr, format, args...)
48+
}
49+
}
50+
func verboseErrorln(args ...any) {
51+
if Verbose {
52+
fmt.Fprintln(os.Stderr, args...)
53+
}
54+
}
55+
4256
// Collect the uname data.
4357
func checkUname(_ context.Context, index int, results chan<- siteCheckSection) {
4458
var buf unix.Utsname
4559
if err := unix.Uname(&buf); err != nil {
46-
fmt.Fprintf(os.Stderr, "Could not get OS info: %v\n",
60+
verboseErrorf("Could not get OS info: %v\n",
4761
err)
48-
return
4962
}
5063
results <- siteCheckSection{
5164
Section: "System Info",
@@ -133,7 +146,7 @@ func catFiles(section string, index int, results chan<- siteCheckSection, names
133146
// Collect the kernel version information.
134147
func checkKernel(_ context.Context, index int, results chan<- siteCheckSection) {
135148
if err := catFiles("Kernel Build", index, results, "/proc/version"); err != nil {
136-
fmt.Fprintf(os.Stderr, "Could not get kernel info: %v\n",
149+
verboseErrorf("Could not get kernel info: %v\n",
137150
err)
138151
}
139152
}
@@ -148,7 +161,7 @@ func checkGlibc(_ context.Context, index int, results chan<- siteCheckSection) {
148161
}
149162
}
150163

151-
// Reusable byte arrays
164+
// Reusable byte arrays.
152165
var (
153166
colon = []byte{':'}
154167
slash = []byte{'/'}
@@ -173,7 +186,7 @@ func lineValue(line, search []byte) ([]byte, bool) {
173186
func checkCpu(_ context.Context, index int, results chan<- siteCheckSection) {
174187
f, err := os.Open("/proc/cpuinfo")
175188
if err != nil {
176-
fmt.Fprintf(os.Stderr, "Could not get cpu info: %v\n",
189+
verboseErrorf("Could not get cpu info: %v\n",
177190
err)
178191
return
179192
}
@@ -236,7 +249,7 @@ func checkCpu(_ context.Context, index int, results chan<- siteCheckSection) {
236249
func checkMemory(_ context.Context, index int, results chan<- siteCheckSection) {
237250
f, err := os.Open("/proc/meminfo")
238251
if err != nil {
239-
fmt.Fprintf(os.Stderr, "Could not get mem info: %v\n",
252+
verboseErrorf("Could not get mem info: %v\n",
240253
err)
241254
return
242255
}
@@ -254,23 +267,20 @@ func checkMemory(_ context.Context, index int, results chan<- siteCheckSection)
254267
return
255268
}
256269
}
257-
fmt.Fprintln(os.Stderr, "Could not find MemTotal in /proc/meminfo")
270+
verboseErrorln("Could not find MemTotal in /proc/meminfo")
258271
}
259272

260273
// Check disk free space and mount options.
261274
func checkDisk(_ context.Context, index int, results chan<- siteCheckSection) {
262275
f, err := os.Open("/proc/self/mountinfo")
263276
if err != nil {
264-
fmt.Fprintf(os.Stderr, "Could not get mount info: %v\n",
277+
verboseErrorf("Could not get mount info: %v\n",
265278
err)
266279
return
267280
}
268281
defer f.Close()
269282
scanner := bufio.NewScanner(f)
270-
var spaceBuf, optsBuf strings.Builder
271-
spaceBuf.WriteString("Size Used Avail")
272-
spaceStartLen := spaceBuf.Len()
273-
autofs := []byte("autofs")
283+
var optsBuf strings.Builder
274284
for scanner.Scan() {
275285
// From `man 5 proc`:
276286
// /proc/[pid]/mountinfo (since Linux 2.6.26)
@@ -307,17 +317,6 @@ func checkDisk(_ context.Context, index int, results chan<- siteCheckSection) {
307317
optsBuf.WriteString(" (")
308318
optsBuf.Write(fields[5])
309319
optsBuf.WriteString(")\n")
310-
if !bytes.Equal(kind, autofs) {
311-
getSpace(&spaceBuf, string(fields[4]))
312-
}
313-
}
314-
if space := spaceBuf.String(); len(space) > spaceStartLen {
315-
results <- siteCheckSection{
316-
Section: "Disk Space",
317-
Cmd: "df -Ph | awk '{print $2, $3, $4}'",
318-
Output: space,
319-
Index: index,
320-
}
321320
}
322321
if opts := strings.TrimSpace(optsBuf.String()); len(opts) > 0 {
323322
results <- siteCheckSection{
@@ -329,57 +328,6 @@ func checkDisk(_ context.Context, index int, results chan<- siteCheckSection) {
329328
}
330329
}
331330

332-
// Convert block size and number of blocks into human-readable value.
333-
func humanSpace(bsize int64, amount uint64) (string, rune) {
334-
total := uint64(bsize) * amount
335-
if total < 1024 {
336-
return strconv.FormatUint(total, 10), 0
337-
}
338-
for i, suffix := range sizeSuffixes[:len(sizeSuffixes)-1] {
339-
unit := ((total >> (9 + 10*i)) + 1) / 2
340-
if unit < 1024 {
341-
return strconv.FormatUint(unit, 10), suffix
342-
}
343-
}
344-
unit := ((total >> (9 + 40)) + 1) / 2
345-
return strconv.FormatUint(unit, 10), 'P'
346-
}
347-
348-
// Write the total/used/avail space for a mount to the given string builder.
349-
func getSpace(builder *strings.Builder, mount string) {
350-
if strings.HasPrefix(mount, "/sys/kernel/debug") {
351-
return
352-
}
353-
var buf unix.Statfs_t
354-
err := unix.Statfs(mount, &buf)
355-
if err != nil {
356-
fmt.Fprintf(os.Stderr, "Could not get disk stats for %s: %v\n",
357-
mount, err)
358-
return
359-
}
360-
if buf.Blocks == 0 {
361-
return
362-
}
363-
builder.WriteByte('\n')
364-
a, s := humanSpace(buf.Bsize, buf.Blocks)
365-
builder.WriteString(a)
366-
if s != 0 {
367-
builder.WriteRune(s)
368-
}
369-
builder.WriteByte(' ')
370-
a, s = humanSpace(buf.Bsize, buf.Blocks-buf.Bfree)
371-
builder.WriteString(a)
372-
if s != 0 {
373-
builder.WriteRune(s)
374-
}
375-
builder.WriteByte(' ')
376-
a, s = humanSpace(buf.Bsize, buf.Bavail)
377-
builder.WriteString(a)
378-
if s != 0 {
379-
builder.WriteRune(s)
380-
}
381-
}
382-
383331
func writeRlim(v, scaleN, scaleD uint64, buf *strings.Builder) {
384332
if v == unix.RLIM_INFINITY {
385333
buf.WriteString("unlimited")
@@ -466,15 +414,15 @@ func checkUlimits(_ context.Context, index int, results chan<- siteCheckSection)
466414
func checkFileLimit(_ context.Context, index int, results chan<- siteCheckSection) {
467415
if err := catFiles("Global File Limit", index, results,
468416
"/proc/sys/fs/file-max", "/proc/sys/fs/file-nr"); err != nil {
469-
fmt.Fprintf(os.Stderr, "Could not get file max: %v\n",
417+
verboseErrorf("Could not get file max: %v\n",
470418
err)
471419
}
472420
}
473421

474422
// Report kernel memory settings.
475423
func checkVmConfig(_ context.Context, index int, results chan<- siteCheckSection) {
476424
if entries, err := os.ReadDir("/proc/sys/vm"); err != nil {
477-
fmt.Fprintf(os.Stderr, "Could not list vm proc directory: %v\n",
425+
verboseErrorf("Could not list vm proc directory: %v\n",
478426
err)
479427
} else {
480428
var buf strings.Builder
@@ -497,11 +445,11 @@ func checkVmConfig(_ context.Context, index int, results chan<- siteCheckSection
497445
}
498446
}
499447
if thp, err := filepath.Glob("/sys/kernel/mm/*transparent_hugepage/enabled"); err != nil {
500-
fmt.Fprintf(os.Stderr, "Could not list hugepage config files: %v\n",
448+
verboseErrorf("Could not list hugepage config files: %v\n",
501449
err)
502450
} else if len(thp) > 0 {
503451
if err := catFiles("THP memory config", index, results, thp...); err != nil {
504-
fmt.Fprintf(os.Stderr, "Could not read hugepage config file: %v\n",
452+
verboseErrorf("Could not read hugepage config file: %v\n",
505453
err)
506454
}
507455
}
@@ -608,26 +556,26 @@ func checkMemoryCgroup(cgroup string, v2 bool, index int, results chan<- siteChe
608556
cgroupBuf = append(cgroupBuf, "/memory."...)
609557
if err := catFiles("cgroup mem stats", index, results,
610558
string(append(cgroupBuf, "stat"...))); err != nil {
611-
fmt.Fprintf(os.Stderr, "Could not read cgroup stats: %v\n",
559+
verboseErrorf("Could not read cgroup stats: %v\n",
612560
err)
613561
}
614562
if err := catFiles("memory soft limit", index, results,
615563
makeString(append(cgroupBuf, softLimit...)),
616564
makeString(append(cgroupBuf, softSwapLimt...))); err != nil &&
617565
!errors.Is(err, os.ErrNotExist) {
618-
fmt.Fprintf(os.Stderr, "Could not read cgroup soft limit: %v\n",
566+
verboseErrorf("Could not read cgroup soft limit: %v\n",
619567
err)
620568
}
621569
if err := catFiles("memory hard limit", index, results,
622570
string(append(cgroupBuf, hardLimit...))); err != nil &&
623571
!errors.Is(err, os.ErrNotExist) {
624-
fmt.Fprintf(os.Stderr, "Could not read cgroup hard limit: %v\n",
572+
verboseErrorf("Could not read cgroup hard limit: %v\n",
625573
err)
626574
}
627575
if err := catFiles("memory swap limit", index, results,
628576
string(append(cgroupBuf, hardSwapLimit...))); err != nil &&
629577
!errors.Is(err, os.ErrNotExist) {
630-
fmt.Fprintf(os.Stderr, "Could not read cgroup swap limit: %v\n",
578+
verboseErrorf("Could not read cgroup swap limit: %v\n",
631579
err)
632580
}
633581
}
@@ -673,8 +621,9 @@ func checkContainer(_ context.Context, index int, results chan<- siteCheckSectio
673621
func checkInit(_ context.Context, index int, results chan<- siteCheckSection) {
674622
f, err := os.Open("/proc/1/sched")
675623
if err != nil {
676-
fmt.Fprintf(os.Stderr, "Could not read init process info: %v\n",
624+
verboseErrorf("Could not read init process info: %v\n",
677625
err)
626+
return
678627
}
679628
defer f.Close()
680629
scanner := bufio.NewScanner(f)
@@ -706,7 +655,7 @@ func execOut(ctx context.Context, stderr bool, e string, args ...string) []byte
706655
return nil
707656
}
708657
if ctx.Err() != nil {
709-
fmt.Fprintln(os.Stderr, e, "timed out")
658+
verboseErrorln(e, "timed out")
710659
}
711660
return bytes.TrimSpace(buf.Bytes())
712661
}
@@ -979,7 +928,7 @@ func checkRefdata(_ context.Context, index int, results chan<- siteCheckSection)
979928
if refdata != "" {
980929
if err := catFiles("10X Refdata Version", index, results,
981930
path.Join(refdata, "version")); err != nil {
982-
fmt.Fprintf(os.Stderr, "Could not read refdata version: %v\n",
931+
verboseErrorf("Could not read refdata version: %v\n",
983932
err)
984933
}
985934
}

lib/python/cellranger/analysis/singlegenome.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ def _select_bc_indices(self, cell_bc_indices):
146146
tsne.transformed_tsne_matrix[cell_bc_indices, :], name=tsne.name, key=tsne.key
147147
)
148148

149+
for name, umap in self.umap.items():
150+
self.umap[name] = UMAP(
151+
umap.transformed_umap_matrix[cell_bc_indices, :], name=umap.name, key=umap.key
152+
)
153+
149154
def subsample_bcs(self, num_bcs):
150155
"""Subsample barcodes across entire analysis (matrix, DR, etc)."""
151156
if num_bcs >= self.matrix.bcs_dim:

lib/rust/cr_types/src/chemistry/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,10 @@ impl ChemistryName {
745745
| ThreePrimeV3HTPolyA
746746
| ThreePrimeV3HTCS1
747747
| ThreePrimeV3LT
748+
| ThreePrimeV4PolyA
749+
| ThreePrimeV4CS1
750+
| ThreePrimeV4PolyAOCM
751+
| ThreePrimeV4CS1OCM
748752
),
749753
LibraryType::Cellplex => matches!(
750754
self,

lib/rust/cr_wrap/src/mkfastq.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ use std::process::{Command, ExitCode};
88
/// (full Rust-ification is an early work in progress for now)
99
pub fn run_mkfastq(args: &AllArgs, legacy_wrapper: &str) -> Result<ExitCode> {
1010
let warning_msg = r#"
11-
The `cellranger mkfastq` pipeline is deprecated and will be removed in a future release.
12-
Please use Illumina's BCL Convert to generate Cell Ranger-compatible FASTQ files.
13-
For detailed guidance, refer to the Generating FASTQs support page:
14-
https://www.10xgenomics.com/support/software/cell-ranger/latest/analysis/inputs/cr-direct-demultiplexing
11+
The `cellranger mkfastq` pipeline is deprecated and will be removed in a future release.
12+
Please use Illumina's BCL Convert to generate Cell Ranger-compatible FASTQ files.
13+
For detailed guidance, refer to the Generating FASTQs support page:
14+
https://www.10xgenomics.com/support/software/cell-ranger/latest/analysis/inputs/cr-direct-demultiplexing
1515
"#;
16+
1617
eprintln!("{warning_msg}");
1718

1819
let mut cmd = Command::new(legacy_wrapper);
@@ -22,8 +23,11 @@ pub fn run_mkfastq(args: &AllArgs, legacy_wrapper: &str) -> Result<ExitCode> {
2223
cmd.arg(r);
2324
}
2425

25-
Ok(cmd
26+
let result = cmd
2627
.status()
27-
.with_context(|| format!("Running {legacy_wrapper}"))?
28-
.into_exit_code())
28+
.with_context(|| format!("Running {legacy_wrapper}"));
29+
30+
eprintln!("{warning_msg}");
31+
32+
Ok(result?.into_exit_code())
2933
}

lib/rust/cr_wrap/src/mkref.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,12 +263,14 @@ fn move_outputs(pipestance_name: &str, args: &MrpArgs, output_ref_dir_name: &str
263263
let item = item?;
264264
fs::rename(item.path(), pipestance_path.join(item.file_name()))?;
265265
}
266-
fs::remove_dir_all(outs_path)?;
266+
// Don't fail if we didn't successfully delete.
267+
let _ = fs::remove_dir_all(outs_path.clone());
267268
} else {
268269
// We ran in the current working dir, so move the outputs into it
269270
// and delete the pipestance directory.
270271
fs::rename(output_reference_path, output_ref_dir_name)?;
271-
fs::remove_dir_all(pipestance_path)?;
272+
// Don't fail if we didn't successfully delete.
273+
let _ = fs::remove_dir_all(pipestance_path);
272274
}
273275
Ok(())
274276
}

lib/rust/cr_wrap/src/telemetry.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ impl TelemetryCollector {
8484
Ok(child) => {
8585
self.procs.push((child, Instant::now()));
8686
}
87-
Err(err) => eprintln!("Failed to start telemetry collector: {err}"),
87+
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
88+
eprintln!("The telemetry collector is not available.");
89+
}
90+
Err(err) => eprintln!("Telemetry collection will not run: {err}"),
8891
}
8992
}
9093

lib/rust/jibes_o3/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,22 @@ impl JibesEMO3 {
253253
coefficients[0] = y_bar - MIN_FOREGROUND_DELTA * x_bar;
254254
}
255255

256+
// Check if the coefficients are Nan (happens if all UMI counts for tag are equal)
257+
// such that the regression matrix isn't full rank.
258+
if coefficients.iter().any(|&x| x.is_nan()) {
259+
// By convention we will set the foreground to the minimum foreground and the
260+
// background to the average counts seen since we can't explain a constant
261+
// with two two values in a deterministic way. This should be `first_elem` most
262+
// of the time.
263+
let avg_cnts = self
264+
.counts
265+
.axis_iter(Axis(0))
266+
.map(|row| row[k])
267+
.fold(0.0, |sum, cnt| sum + cnt)
268+
/ (self.n() as f64);
269+
coefficients[0] = avg_cnts;
270+
coefficients[1] = MIN_FOREGROUND_DELTA;
271+
}
256272
self.model.background[k] = coefficients[0];
257273
self.model.foreground[k] = coefficients[1];
258274

0 commit comments

Comments
 (0)