Skip to content

Commit

Permalink
Misc
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Nov 13, 2024
1 parent 6d89c72 commit 21b0aee
Show file tree
Hide file tree
Showing 15 changed files with 181 additions and 109 deletions.
67 changes: 45 additions & 22 deletions packages/core/BaseFeatureWidget/SequenceFeatureDetails/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,54 @@ function localStorageGetNumber(key: string, defaultVal: number) {
return +(localStorageGetItem(key) ?? defaultVal)
}

function localStorageGetBoolean(key: string, defaultVal: boolean) {
return Boolean(
JSON.parse(localStorageGetItem(key) || JSON.stringify(defaultVal)),
)
}

function localStorageSetNumber(key: string, value: number) {
return localStorageSetItem(key, JSON.stringify(value))

Check failure on line 22 in packages/core/BaseFeatureWidget/SequenceFeatureDetails/model.ts

View workflow job for this annotation

GitHub Actions / Lint, typecheck, test

Returning a void expression from a function is forbidden. Please remove the `return` statement
}

function localStorageSetBoolean(key: string, value: boolean) {
return localStorageSetItem(key, JSON.stringify(value))

Check failure on line 26 in packages/core/BaseFeatureWidget/SequenceFeatureDetails/model.ts

View workflow job for this annotation

GitHub Actions / Lint, typecheck, test

Returning a void expression from a function is forbidden. Please remove the `return` statement
}

const p = 'sequenceFeatureDetails'

export function SequenceFeatureDetailsF() {
return types
.model('SequenceFeatureDetails')
.volatile(() => ({
/**
* #volatile
*/
showCoordinatesSetting:
localStorageGetItem('sequenceFeatureDetails-showCoordinatesSetting') ||
'none',
intronBp: localStorageGetNumber('sequenceFeatureDetails-intronBp', 10),
upDownBp: localStorageGetNumber('sequenceFeatureDetails-upDownBp', 100),
upperCaseCDS: Boolean(
JSON.parse(
localStorageGetItem('sequenceFeatureDetails-upperCaseCDS') || 'true',
),
),
localStorageGetItem(`${p}-showCoordinatesSetting`) || 'none',
/**
* #volatile
*/
intronBp: localStorageGetNumber(`${p}-intronBp`, 10),
/**
* #volatile
*/
upDownBp: localStorageGetNumber(`${p}-upDownBp`, 100),
/**
* #volatile
*/
upperCaseCDS: localStorageGetBoolean(`${p}-upperCaseCDS`, true),
/**
* #volatile
*/
charactersPerRow: 100,
/**
* #volatile
*/
feature: undefined as SimpleFeatureSerialized | undefined,
/**
* #volatile
*/
mode: '',
}))
.actions(self => ({
Expand Down Expand Up @@ -110,20 +142,11 @@ export function SequenceFeatureDetailsF() {
addDisposer(
self,
autorun(() => {
localStorageSetNumber(`${p}-upDownBp`, self.upDownBp)
localStorageSetNumber(`${p}-intronBp`, self.intronBp)
localStorageSetBoolean(`${p}-upperCaseCDS`, self.upperCaseCDS)
localStorageSetItem(
'sequenceFeatureDetails-upDownBp',
JSON.stringify(self.upDownBp),
)
localStorageSetItem(
'sequenceFeatureDetails-intronBp',
JSON.stringify(self.intronBp),
)
localStorageSetItem(
'sequenceFeatureDetails-upperCaseCDS',
JSON.stringify(self.upperCaseCDS),
)
localStorageSetItem(
'sequenceFeatureDetails-showCoordinatesSetting',
`${p}-showCoordinatesSetting`,
self.showCoordinatesSetting,
)
}),
Expand Down
22 changes: 13 additions & 9 deletions packages/core/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,24 +609,29 @@ export function cartesianToPolar(x: number, y: number) {
const theta = Math.atan(y / x)
return [rho, theta] as [number, number]
}
interface MinimalRegion {
start: number
end: number
reversed?: boolean
}

export function featureSpanPx(
feature: Feature,
region: { start: number; end: number; reversed?: boolean },
region: MinimalRegion,
bpPerPx: number,
): [number, number] {
) {
return bpSpanPx(feature.get('start'), feature.get('end'), region, bpPerPx)
}

export function bpSpanPx(
leftBp: number,
rightBp: number,
region: { start: number; end: number; reversed?: boolean },
region: MinimalRegion,
bpPerPx: number,
): [number, number] {
) {
const start = bpToPx(leftBp, region, bpPerPx)
const end = bpToPx(rightBp, region, bpPerPx)
return region.reversed ? [end, start] : [start, end]
return region.reversed ? ([end, start] as const) : ([start, end] as const)
}

// do an array map of an iterable
Expand All @@ -646,8 +651,7 @@ export function iterMap<T, U>(

/**
* Returns the index of the last element in the array where predicate is true,
* and -1 otherwise.
* Based on https://stackoverflow.com/a/53187807
* and -1 otherwise. Based on https://stackoverflow.com/a/53187807
*
* @param array - The source array to search in
*
Expand All @@ -660,7 +664,7 @@ export function iterMap<T, U>(
export function findLastIndex<T>(
array: T[],
predicate: (value: T, index: number, obj: T[]) => boolean,
): number {
) {
let l = array.length
while (l--) {
if (predicate(array[l]!, l, array)) {
Expand All @@ -673,7 +677,7 @@ export function findLastIndex<T>(
export function findLast<T>(
array: T[],
predicate: (value: T, index: number, obj: T[]) => boolean,
): T | undefined {
) {
let l = array.length
while (l--) {
if (predicate(array[l]!, l, array)) {
Expand Down
35 changes: 22 additions & 13 deletions plugins/alignments/src/BamAdapter/BamAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { BamFile } from '@gmod/bam'
import { toArray } from 'rxjs/operators'
import { firstValueFrom } from 'rxjs'
// jbrowse
import {
BaseFeatureDataAdapter,
BaseOptions,
Expand All @@ -7,9 +10,10 @@ import { Region } from '@jbrowse/core/util/types'
import { bytesForRegions, updateStatus, Feature } from '@jbrowse/core/util'
import { openLocation } from '@jbrowse/core/util/io'
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
import { toArray } from 'rxjs/operators'
import { firstValueFrom } from 'rxjs'
import QuickLRU from '@jbrowse/core/util/QuickLRU'
import { AnyConfigurationModel } from '@jbrowse/core/configuration'
import { getSubAdapterType } from '@jbrowse/core/data_adapters/dataAdapterCache'
import PluginManager from '@jbrowse/core/PluginManager'

// locals
import BamSlightlyLazyFeature from './BamSlightlyLazyFeature'
Expand All @@ -25,15 +29,23 @@ export default class BamAdapter extends BaseFeatureDataAdapter {

private setupP?: Promise<Header>

private featureMap = new QuickLRU<string, Feature>({
maxSize: 5000,
})
private featureCache: QuickLRU<string, Feature>

private configureP?: Promise<{
bam: BamFile
sequenceAdapter?: BaseFeatureDataAdapter
}>

constructor(
config: AnyConfigurationModel,
getSubAdapter?: getSubAdapterType,
pluginManager?: PluginManager,
) {
super(config, getSubAdapter, pluginManager)
this.featureCache = new QuickLRU({
maxSize: this.getConf('featureCacheSize'),
})
}
// derived classes may not use the same configuration so a custom configure
// method allows derived classes to override this behavior
protected async configurePre() {
Expand Down Expand Up @@ -190,8 +202,6 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
readName,
} = filterBy || {}

let retrievedFromCache = 0
let notRetrievedFromCache = 0
for (const record of records) {
let ref: string | undefined
if (!record.tags.MD) {
Expand Down Expand Up @@ -223,19 +233,18 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
continue
}

const ret = this.featureMap.get(`${record.id}`)

// retrieve a feature from our feature cache if it is available, the
// features in the cache have pre-computed mismatches objects that
// can be re-used across blocks
const ret = this.featureCache.get(`${record.id}`)
if (!ret) {
const elt = new BamSlightlyLazyFeature(record, this, ref)
this.featureMap.set(`${record.id}`, elt)
this.featureCache.set(`${record.id}`, elt)
observer.next(elt)
notRetrievedFromCache++
} else {
retrievedFromCache++
observer.next(ret)
}
}
// console.log({ retrievedFromCache, notRetrievedFromCache })
observer.complete()
})
}, signal)
Expand Down
31 changes: 10 additions & 21 deletions plugins/alignments/src/BamAdapter/BamSlightlyLazyFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BamRecord } from '@gmod/bam'
// locals
import { getMismatches } from '../MismatchParser'
import BamAdapter from './BamAdapter'
import { cacheGetter } from '../shared/util'

export default class BamSlightlyLazyFeature implements Feature {
// uses parameter properties to automatically create fields on the class
Expand All @@ -30,11 +31,15 @@ export default class BamSlightlyLazyFeature implements Feature {
)
}

get qual() {
return this.record.qual?.join(' ')
}

get(field: string): any {
return field === 'mismatches'
? this.mismatches
: field === 'qual'
? this.record.qual?.join(' ')
? this.qual
: this.fields[field]
}

Expand Down Expand Up @@ -74,27 +79,11 @@ export default class BamSlightlyLazyFeature implements Feature {
}

toJSON(): SimpleFeatureSerialized {
return this.fields
}
}

function cacheGetter<T>(ctor: { prototype: T }, prop: keyof T): void {
const desc = Object.getOwnPropertyDescriptor(ctor.prototype, prop)
if (!desc) {
throw new Error('t1')
}

const getter = desc.get
if (!getter) {
throw new Error('t2')
return {
...this.fields,
qual: this.qual,
}
}
Object.defineProperty(ctor.prototype, prop, {
get() {
const ret = getter.call(this)
Object.defineProperty(this, prop, { value: ret })
return ret
},
})
}

cacheGetter(BamSlightlyLazyFeature, 'fields')
Expand Down
11 changes: 11 additions & 0 deletions plugins/alignments/src/BamAdapter/configSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ const configSchema = ConfigurationSchema(
'sequence data adapter, used to calculate SNPs when BAM reads lacking MD tags',
defaultValue: null,
},
/**
* #slot
* number of BAM features to store in cache, features are re-used across
* zoom in and out. if you experience memory issues, you can consider
* lowering, or if you experience slowness, you can consider raising
*/
featureCacheSize: {
type: 'number',
description: 'the literal number of BAM features to store in the cache',
defaultValue: 5000,
},
},
{ explicitlyTyped: true },
)
Expand Down
34 changes: 31 additions & 3 deletions plugins/alignments/src/CramAdapter/CramAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { CraiIndex, IndexedCramFile, CramRecord } from '@gmod/cram'
import { toArray } from 'rxjs/operators'
import { firstValueFrom } from 'rxjs'
// jbrowse
import {
BaseFeatureDataAdapter,
BaseOptions,
Expand All @@ -8,8 +11,10 @@ import type { Region, Feature } from '@jbrowse/core/util'
import { checkAbortSignal, updateStatus, toLocale } from '@jbrowse/core/util'
import { openLocation } from '@jbrowse/core/util/io'
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
import { toArray } from 'rxjs/operators'
import { firstValueFrom } from 'rxjs'
import QuickLRU from '@jbrowse/core/util/QuickLRU'
import { AnyConfigurationModel } from '@jbrowse/core/configuration'
import { getSubAdapterType } from '@jbrowse/core/data_adapters/dataAdapterCache'
import PluginManager from '@jbrowse/core/PluginManager'

// locals
import CramSlightlyLazyFeature from './CramSlightlyLazyFeature'
Expand All @@ -35,12 +40,25 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
sequenceAdapter: BaseSequenceAdapter
}>

private featureCache: QuickLRU<string, Feature>

// maps a refname to an id
private seqIdToRefName: string[] | undefined

// maps a seqId to original refname, passed specially to render args, to a seqid
private seqIdToOriginalRefName: string[] = []

constructor(
config: AnyConfigurationModel,
getSubAdapter?: getSubAdapterType,
pluginManager?: PluginManager,
) {
super(config, getSubAdapter, pluginManager)
this.featureCache = new QuickLRU({
maxSize: this.getConf('featureCacheSize'),
})
}

public async configurePre() {
const cramLocation = this.getConf('cramLocation')
const craiLocation = this.getConf('craiLocation')
Expand Down Expand Up @@ -266,7 +284,17 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
if (readName && record.readName !== readName) {
continue
}
observer.next(this.cramRecordToFeature(record))
// retrieve a feature from our feature cache if it is available, the
// features in the cache have pre-computed mismatches objects that
// can be re-used across blocks
const ret = this.featureCache.get(`${record.uniqueId}`)
if (!ret) {
const elt = this.cramRecordToFeature(record)
this.featureCache.set(`${record.uniqueId}`, elt)
observer.next(elt)
} else {
observer.next(ret)
}
}

observer.complete()
Expand Down
Loading

0 comments on commit 21b0aee

Please sign in to comment.