Skip to content

Commit b971a72

Browse files
committed
restructuring
1 parent 72a1087 commit b971a72

11 files changed

Lines changed: 820 additions & 635 deletions

build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ cd ../..
2828
# Copy the built library into the python package directory
2929
# This allows maturin to include it in the wheel
3030
cp $DYLIB_PATH native_fisher_py/python/native_fisher_py/
31+
cp vendor/RawFileReader/Libs/NetCore/Net8/Assemblies/*.dll native_fisher_py/python/native_fisher_py/
3132

3233
# 2. Build Python Package
3334
cd native_fisher_py

native/ThermoNativeReader/NativeApi.cs

Lines changed: 346 additions & 171 deletions
Large diffs are not rendered by default.

native_fisher_py/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ tests = [
2525
[tool.maturin]
2626
python-source = "python"
2727
module-name = "native_fisher_py.native_fisher_py_backend"
28-
include = ["python/native_fisher_py/ThermoNativeReader.*"]
28+
include = ["python/native_fisher_py/ThermoNativeReader.*", "python/native_fisher_py/*.dll"]
33.5 KB
Binary file not shown.
61 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

native_fisher_py/python/native_fisher_py/raw_file.py

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
import threading
3+
_chdir_lock = threading.Lock()
24
import numpy as np
35
from typing import List, Tuple
46
from .native_fisher_py_backend import *
@@ -38,15 +40,21 @@ class RawFile(object):
3840
A high-level wrapper to provide a drop-in replacement for fisher_py.RawFile
3941
"""
4042
def __init__(self, path: str):
41-
"""
42-
Open a Thermo RAW file.
43-
"""
4443
self._path = path
4544
real_path = os.path.realpath(path)
4645
if not os.path.isfile(real_path):
4746
raise FileNotFoundError(f'No raw file with path "{path}" found.')
48-
res = open_raw_file(real_path)
49-
if res != 0:
47+
48+
dll_dir = os.path.dirname(__file__)
49+
with _chdir_lock:
50+
original_cwd = os.getcwd()
51+
try:
52+
os.chdir(dll_dir)
53+
self._handle = open_raw_file(real_path)
54+
finally:
55+
os.chdir(original_cwd)
56+
57+
if getattr(self, "_handle", -1) < 0:
5058
raise RawFileException(f"Could not open RAW file: {path}")
5159
self._is_open = True
5260

@@ -59,7 +67,7 @@ def _raw_file_access(self):
5967
return self
6068

6169
def select_instrument(self, device_type: int, device_number: int):
62-
select_instrument(device_type, device_number)
70+
select_instrument(self._handle, device_type, device_number)
6371

6472
def average_scans(self, start, end): return None
6573
def average_scans_in_scan_range(self, start, end, options): return None
@@ -68,14 +76,14 @@ def default_mass_options(self): return MassOptions()
6876
def dispose(self): self.close()
6977
def get_all_instrument_names_from_instrument_method(self): return []
7078
def get_instrument_method(self, index):
71-
return get_instrument_method(index)
79+
return get_instrument_method(self._handle, index)
7280
def get_instrument_methods_count(self) -> int:
73-
return get_instrument_method_count()
81+
return get_instrument_method_count(self._handle)
7482
def get_instrument_type(self): return 0
7583
def get_segment_event_table(self): return []
7684
def has_instrument_method(self): return self.get_instrument_methods_count() > 0
7785
def is_centroid_scan_from_scan_number(self, scan_number):
78-
return is_centroid(scan_number)
86+
return is_centroid(self._handle, scan_number)
7987
def refresh_view_of_file(self): pass
8088
@property
8189
def selected_instrument(self): return 0
@@ -98,31 +106,31 @@ def path(self) -> str:
98106

99107
@property
100108
def number_of_scans(self) -> int:
101-
return get_num_scans()
109+
return get_num_scans(self._handle)
102110

103111
@property
104112
def first_scan(self) -> int:
105-
return get_first_scan()
113+
return get_first_scan(self._handle)
106114

107115
@property
108116
def last_scan(self) -> int:
109-
return get_last_scan()
117+
return get_last_scan(self._handle)
110118

111119
@property
112120
def file_name(self) -> str:
113-
return get_file_name()
121+
return get_file_name(self._handle)
114122

115123
@property
116124
def creation_date(self) -> str:
117-
return get_creation_date()
125+
return get_creation_date(self._handle)
118126

119127
@property
120128
def computer_name(self) -> str:
121-
return get_computer_name()
129+
return get_computer_name(self._handle)
122130

123131
@property
124132
def creator_id(self) -> str:
125-
return get_creator_id()
133+
return get_creator_id(self._handle)
126134

127135
def get_instrument_data(self) -> InstrumentData:
128136
return InstrumentData()
@@ -165,21 +173,21 @@ def include_reference_and_exception_data(self, value: bool):
165173

166174
@property
167175
def is_open(self) -> bool:
168-
return is_open()
176+
return is_open(self._handle)
169177

170178
@property
171179
def is_error(self) -> bool:
172-
return is_error()
180+
return is_error(self._handle)
173181

174182
@property
175183
def in_acquisition(self) -> bool:
176-
return in_acquisition()
184+
return in_acquisition(self._handle)
177185

178186
def retention_time_from_scan_number(self, scan_number: int) -> float:
179-
return get_scan_rt(scan_number)
187+
return get_scan_rt(self._handle, scan_number)
180188

181189
def scan_number_from_retention_time(self, rt: float) -> int:
182-
return get_scan_number_from_rt(rt)
190+
return get_scan_number_from_rt(self._handle, rt)
183191

184192
def get_scan_event_for_scan_number(self, scan_number: int):
185193
from .data.classes import ScanEvent
@@ -188,21 +196,21 @@ def get_scan_event_for_scan_number(self, scan_number: int):
188196
def get_status_log_for_retention_time(self, rt: float):
189197
from .data.classes import LogEntry
190198
scan = self.scan_number_from_retention_time(rt)
191-
return LogEntry(get_status_log_values(scan))
199+
return LogEntry(get_status_log_values(self._handle, scan))
192200

193201
def get_status_log_for_scan_number(self, scan_number: int):
194202
from .data.classes import LogEntry
195-
return LogEntry(get_status_log_values(scan_number))
203+
return LogEntry(get_status_log_values(self._handle, scan_number))
196204

197205
def get_scan_event_string_for_scan_number(self, scan_number: int):
198-
return get_scan_event_string(scan_number)
206+
return get_scan_event_string(self._handle, scan_number)
199207

200208
def get_centroid_stream(self, scan_number: int, include_ref_peaks: bool = False):
201209
from .native_fisher_py_backend import get_centroid_stream
202210
from .data.classes import CentroidStream
203211
import numpy as np
204212

205-
masses, intensities, baselines, noises, charges, bp_noise, bp_res = get_centroid_stream(scan_number, 1000000)
213+
print(f"Calling get_centroid_stream with handle={self._handle}, scan={scan_number}"); masses, intensities, baselines, noises, charges, bp_noise, bp_res = get_centroid_stream(self._handle, scan_number, 1000000)
206214

207215
return CentroidStream(
208216
masses=np.array(masses),
@@ -218,13 +226,13 @@ def get_centroid_stream(self, scan_number: int, include_ref_peaks: bool = False)
218226
def get_segmented_scan_from_scan_number(self, scan_number: int, stats = None):
219227
from .native_fisher_py_backend import get_spectrum
220228
from .data.classes import SegmentedScan
221-
masses, intensities = get_spectrum(scan_number, 1000000)
229+
masses, intensities = get_spectrum(self._handle, scan_number, 1000000)
222230
return SegmentedScan(masses=masses, intensities=intensities)
223231

224232
def get_scan_stats_for_scan_number(self, scan_number: int):
225233
from .data.classes import ScanStatistics
226234
from .native_fisher_py_backend import get_scan_stats
227-
data = get_scan_stats(scan_number)
235+
data = get_scan_stats(self._handle, scan_number)
228236
return ScanStatistics(
229237
start_time=data[0],
230238
low_mass=data[1],
@@ -251,48 +259,48 @@ def get_chromatogram_data(self, settings, start_scan, end_scan, tolerance = None
251259
starts = [float(r.low) for r in s.mass_ranges]
252260
ends = [float(r.high) for r in s.mass_ranges]
253261

254-
times, intensities = get_chromatogram(trace_type, filter_str, starts, ends, start_scan, end_scan, 1000000)
262+
times, intensities = get_chromatogram(self._handle, trace_type, filter_str, starts, ends, start_scan, end_scan, 1000000)
255263
all_times.append(times)
256264
all_intensities.append(intensities)
257265
all_scans.append([]) # Empty scans for now
258266

259267
return ChromatogramData(all_times, all_intensities, all_scans)
260268

261269
def get_instrument_count_of_type(self, device_type):
262-
return get_instrument_count_of_type(device_type)
270+
return get_instrument_count_of_type(self._handle, device_type)
263271

264272
def get_trailer_extra_information(self, scan_number):
265273
from .data.classes import LogEntry
266274
labels = [h.label for h in self.get_trailer_extra_header_information()]
267-
return LogEntry(get_trailer_extra_values(scan_number), labels)
275+
return LogEntry(get_trailer_extra_values(self._handle, scan_number), labels)
268276

269277
def get_trailer_extra_header_information(self):
270278
from .data.classes import HeaderItem
271-
return [HeaderItem(h) for h in get_trailer_extra_header()]
279+
return [HeaderItem(h) for h in get_trailer_extra_header(self._handle)]
272280

273281
def get_trailer_extra_values(self, scan_number, formatted=False):
274-
return get_trailer_extra_values(scan_number)
282+
return get_trailer_extra_values(self._handle, scan_number)
275283

276284
def get_status_log_header_information(self):
277285
from .data.classes import HeaderItem
278-
return [HeaderItem(h) for h in get_status_log_header()]
286+
return [HeaderItem(h) for h in get_status_log_header(self._handle)]
279287

280288
def get_status_log_values(self, scan_number, formatted=False):
281289
from .data.classes import LogEntry
282290
labels = [h.label for h in self.get_status_log_header_information()]
283-
return LogEntry(get_status_log_values(scan_number), labels)
291+
return LogEntry(get_status_log_values(self._handle, scan_number), labels)
284292

285293
def get_status_log_entries_count(self):
286-
return get_status_log_count()
294+
return get_status_log_count(self._handle)
287295

288296
def get_status_log_for_retention_time(self, rt):
289297
from .data.classes import LogEntry
290298
labels = [h.label for h in self.get_status_log_header_information()]
291-
return LogEntry(get_status_log_values_for_rt(rt), labels)
299+
return LogEntry(get_status_log_values_for_rt(self._handle, rt), labels)
292300
def get_tune_data_count(self):
293-
return get_tune_data_count()
301+
return get_tune_data_count(self._handle)
294302
def get_tune_data(self, index): return None
295-
def get_filters(self): return get_filters()
303+
def get_filters(self): return get_filters(self._handle)
296304
def get_auto_filters(self): return []
297305
def get_filter_for_scan_number(self, scan_number):
298306
from .data.classes import ScanFilter
@@ -302,7 +310,7 @@ def get_scan_dependents(self, scan_number, precision): return ScanDependents()
302310

303311
@property
304312
def has_ms_data(self) -> bool:
305-
return has_ms_data()
313+
return has_ms_data(self._handle)
306314

307315
def get_scan_type(self, scan_number: int):
308316
return ""
@@ -324,32 +332,32 @@ def instrument_methods_count(self) -> int:
324332

325333
@property
326334
def instrument_count(self) -> int:
327-
return get_instrument_count()
335+
return get_instrument_count(self._handle)
328336

329337
@property
330338
def total_time_min(self) -> float:
331-
return get_end_time()
339+
return get_end_time(self._handle)
332340

333341
def get_chromatogram(self, mass: float = 0.0, tolerance: float = 0.0, trace_type: int = 1, ms_filter: str = 'ms') -> Tuple[np.ndarray, np.ndarray]:
334342
starts = [mass - tolerance] if mass > 0 else []
335343
ends = [mass + tolerance] if mass > 0 else []
336-
times, intensities = get_chromatogram(int(trace_type), ms_filter, starts, ends, -1, -1, 1000000)
344+
times, intensities = get_chromatogram(self._handle, int(trace_type), ms_filter, starts, ends, -1, -1, 1000000)
337345
return np.array(times), np.array(intensities)
338346

339347
def get_averaged_ms2_scans(self, scan_numbers: List[int]) -> Tuple[np.ndarray, np.ndarray, int]:
340348
if not scan_numbers:
341349
return np.array([]), np.array([]), 0
342-
masses, intensities = get_averaged_spectrum(scan_numbers, 1000000)
350+
masses, intensities = get_averaged_spectrum(self._handle, scan_numbers, 1000000)
343351
return np.array(masses), np.array(intensities), scan_numbers[0]
344352

345353
def get_ms1_scan_number_from_retention_time(self, rt: float) -> Tuple[int, float]:
346-
scan_number = get_ms1_scan_number_from_rt(rt)
354+
scan_number = get_ms1_scan_number_from_rt(self._handle, rt)
347355
if scan_number < 1: return 0, 0.0
348356
return scan_number, self.retention_time_from_scan_number(scan_number)
349357

350358
def get_ms2_scan_number_from_retention_time(self, rt: float, precursor_mz: float = None) -> Tuple[int, float]:
351359
pmz = precursor_mz if precursor_mz is not None else 0.0
352-
scan_number = get_ms2_scan_number_from_rt(rt, pmz, 1.0)
360+
scan_number = get_ms2_scan_number_from_rt(self._handle, rt, pmz, 1.0)
353361
if scan_number < 1: return 0, 0.0
354362
return scan_number, self.retention_time_from_scan_number(scan_number)
355363

@@ -384,23 +392,23 @@ def ms2_filter_masses(self) -> List[float]:
384392
if not hasattr(self, "_ms2_filter_masses_cache"):
385393
mass_set = set()
386394
for i in range(self.first_scan, self.last_scan + 1):
387-
if get_ms_order(i) == 2:
388-
mass_set.add(get_precursor_mass(i))
395+
if get_ms_order(self._handle, i) == 2:
396+
mass_set.add(get_precursor_mass(self._handle, i))
389397
self._ms2_filter_masses_cache = sorted(list(mass_set))
390398
return self._ms2_filter_masses_cache
391399

392400
def get_precursor_mz(self, scan_number: int) -> float:
393-
return get_precursor_mass(scan_number)
401+
return get_precursor_mass(self._handle, scan_number)
394402

395403
def get_scan_from_scan_number(self, scan_number: int):
396404
# Use get_centroid_stream to match behavior for parity
397-
masses, intensities, *_ = get_centroid_stream(scan_number, 1000000)
405+
masses, intensities, *_ = get_centroid_stream(self._handle, scan_number, 1000000)
398406
charges = np.zeros_like(masses)
399407
event_str = self.get_scan_event_string_for_scan_number(scan_number)
400408
return np.array(masses), np.array(intensities), charges, event_str
401409

402410
def get_scan_number_from_retention_time(self, rt: float) -> int:
403-
return get_scan_number_from_rt(rt)
411+
return get_scan_number_from_rt(self._handle, rt)
404412

405413
def __enter__(self):
406414
return self
@@ -409,4 +417,6 @@ def __exit__(self, exc_type, exc_val, exc_tb):
409417
self.close()
410418

411419
def close(self):
412-
close_raw_file()
420+
if hasattr(self, "_handle") and self._handle > 0:
421+
close_raw_file(self._handle)
422+
self._handle = -1

0 commit comments

Comments
 (0)