cellarr 0.4.1.dev1__tar.gz → 0.4.1.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/PKG-INFO +43 -2
  2. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/README.md +42 -1
  3. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/setup.cfg +2 -0
  4. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/buildutils_tiledb_frame.py +1 -1
  5. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/README.md +11 -4
  6. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/build_cellarr_steps.py +65 -33
  7. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/final_assembly.py +9 -0
  8. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/finalize_matrix.py +2 -2
  9. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/process_gene_annotation.py +15 -3
  10. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/process_matrix.py +11 -3
  11. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/process_sample_metadata.py +2 -0
  12. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/PKG-INFO +43 -2
  13. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/SOURCES.txt +1 -1
  14. cellarr-0.4.1.dev2/src/cellarr.egg-info/entry_points.txt +2 -0
  15. cellarr-0.4.1.dev1/src/cellarr/slurm/build_cellarr_steps_bkp.py +0 -185
  16. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/.coveragerc +0 -0
  17. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/.github/workflows/pypi-publish.yml +0 -0
  18. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/.github/workflows/pypi-test.yml +0 -0
  19. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/.gitignore +0 -0
  20. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/.pre-commit-config.yaml +0 -0
  21. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/.readthedocs.yml +0 -0
  22. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/AUTHORS.md +0 -0
  23. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/CHANGELOG.md +0 -0
  24. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/CONTRIBUTING.md +0 -0
  25. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/LICENSE.txt +0 -0
  26. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/assets/cellarr.jpg +0 -0
  27. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/assets/cellarr.png +0 -0
  28. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/assets/cellarr.svg +0 -0
  29. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/Makefile +0 -0
  30. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/_static/.gitignore +0 -0
  31. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/authors.md +0 -0
  32. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/changelog.md +0 -0
  33. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/conf.py +0 -0
  34. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/contributing.md +0 -0
  35. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/index.md +0 -0
  36. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/license.md +0 -0
  37. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/notes.md +0 -0
  38. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/readme.md +0 -0
  39. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/requirements.txt +0 -0
  40. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/docs/tutorial.md +0 -0
  41. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/pyproject.toml +0 -0
  42. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/setup.py +0 -0
  43. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/CellArrDataset.py +0 -0
  44. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/CellArrDatasetSlice.py +0 -0
  45. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/__init__.py +0 -0
  46. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/autoencoder.py +0 -0
  47. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/build_cellarrdataset.py +0 -0
  48. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/build_options.py +0 -0
  49. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/buildutils_tiledb_array.py +0 -0
  50. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/dataloader.py +0 -0
  51. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/queryutils_tiledb_frame.py +0 -0
  52. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/__init__.py +0 -0
  53. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/process_cell_metadata.py +0 -0
  54. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/slurm/process_matrix_all.py +0 -0
  55. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr/utils_anndata.py +0 -0
  56. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/dependency_links.txt +0 -0
  57. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/not-zip-safe +0 -0
  58. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/requires.txt +0 -0
  59. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/src/cellarr.egg-info/top_level.txt +0 -0
  60. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tests/conftest.py +0 -0
  61. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tests/data/adata.h5ad +0 -0
  62. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tests/test_anndata_utils.py +0 -0
  63. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tests/test_build.py +0 -0
  64. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tests/test_custom_config.py +0 -0
  65. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tests/test_dataloader.py +0 -0
  66. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tests/test_query.py +0 -0
  67. {cellarr-0.4.1.dev1 → cellarr-0.4.1.dev2}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cellarr
3
- Version: 0.4.1.dev1
3
+ Version: 0.4.1.dev2
4
4
  Summary: TileDB-based array storage for genomics data collections.
5
5
  Home-page: https://github.com/BiocPy/cellarr
6
6
  Author: Jayaram Kancherla
@@ -45,7 +45,7 @@ datasets but can be generalized to store any 2-dimensional experimental data.
45
45
 
46
46
  To get started, install the package from [PyPI](https://pypi.org/project/cellarr/)
47
47
 
48
- ```bash
48
+ ```sh
49
49
  pip install cellarr
50
50
 
51
51
  ## to include optional dependencies
@@ -156,6 +156,47 @@ print(dataset)
156
156
 
157
157
  Check out the [documentation](https://biocpy.github.io/cellarr/tutorial.html) for more details.
158
158
 
159
+ ### Building on HPC environments with `slurm`
160
+
161
+ To simplify building TileDB files on HPC environments that use `slurm`, there are a few steps you need to follow.
162
+
163
+ - Step 1: Construct a manifest file
164
+ A minimal manifest file (json) must contain the following fields
165
+ - `"files"`: A list of file path to the input `h5ad` objects.
166
+ - `"python_env"`: A set of commands to activate the Python environment containing this package and its dependencies.
167
+
168
+ Here’s an example of the manifest file:
169
+ ```json
170
+ manifest = {
171
+ "files": your/list/of/files,
172
+ "python_env": """
173
+ ml Miniforge3
174
+ conda activate cellarr
175
+
176
+ python --version
177
+ which python
178
+ """,
179
+ "matrix_options": [
180
+ {
181
+ "matrix_name": "non_zero_cells",
182
+ "dtype": "uint32"
183
+ },
184
+ {
185
+ "matrix_name": "pseudo_bulk_log_normed",
186
+ "dtype": "float32"
187
+ }
188
+ ],
189
+ }
190
+ ```
191
+ For more options, check out the [README](./src/cellarr/slurm/README.md).
192
+
193
+ - Step 2: Submit the job
194
+ Once your manifest file is ready, you can submit the necessary jobs using the `cellarr_build` CLI. Run the following command:
195
+
196
+ ```sh
197
+ cellarr_build --input-manifest your/path/to/test_manifest.json --output-dir your/path/to/output --memory-per-job 8 --cpus-per-task 2
198
+ ```
199
+
159
200
  ### Query a `CellArrDataset`
160
201
 
161
202
  Users have the option to reuse the `dataset` object returned when building the dataset or by creating a `CellArrDataset` object by initializing it to the path where the files were created.
@@ -12,7 +12,7 @@ datasets but can be generalized to store any 2-dimensional experimental data.
12
12
 
13
13
  To get started, install the package from [PyPI](https://pypi.org/project/cellarr/)
14
14
 
15
- ```bash
15
+ ```sh
16
16
  pip install cellarr
17
17
 
18
18
  ## to include optional dependencies
@@ -123,6 +123,47 @@ print(dataset)
123
123
 
124
124
  Check out the [documentation](https://biocpy.github.io/cellarr/tutorial.html) for more details.
125
125
 
126
+ ### Building on HPC environments with `slurm`
127
+
128
+ To simplify building TileDB files on HPC environments that use `slurm`, there are a few steps you need to follow.
129
+
130
+ - Step 1: Construct a manifest file
131
+ A minimal manifest file (json) must contain the following fields
132
+ - `"files"`: A list of file path to the input `h5ad` objects.
133
+ - `"python_env"`: A set of commands to activate the Python environment containing this package and its dependencies.
134
+
135
+ Here’s an example of the manifest file:
136
+ ```json
137
+ manifest = {
138
+ "files": your/list/of/files,
139
+ "python_env": """
140
+ ml Miniforge3
141
+ conda activate cellarr
142
+
143
+ python --version
144
+ which python
145
+ """,
146
+ "matrix_options": [
147
+ {
148
+ "matrix_name": "non_zero_cells",
149
+ "dtype": "uint32"
150
+ },
151
+ {
152
+ "matrix_name": "pseudo_bulk_log_normed",
153
+ "dtype": "float32"
154
+ }
155
+ ],
156
+ }
157
+ ```
158
+ For more options, check out the [README](./src/cellarr/slurm/README.md).
159
+
160
+ - Step 2: Submit the job
161
+ Once your manifest file is ready, you can submit the necessary jobs using the `cellarr_build` CLI. Run the following command:
162
+
163
+ ```sh
164
+ cellarr_build --input-manifest your/path/to/test_manifest.json --output-dir your/path/to/output --memory-per-job 8 --cpus-per-task 2
165
+ ```
166
+
126
167
  ### Query a `CellArrDataset`
127
168
 
128
169
  Users have the option to reuse the `dataset` object returned when building the dataset or by creating a `CellArrDataset` object by initializing it to the path where the files were created.
@@ -49,6 +49,8 @@ testing =
49
49
  pytorch-lightning
50
50
 
51
51
  [options.entry_points]
52
+ console_scripts =
53
+ cellarr_build = cellarr.slurm.build_cellarr_steps:main
52
54
 
53
55
  [tool:pytest]
54
56
  addopts =
@@ -76,7 +76,7 @@ def create_tiledb_frame_from_column_names(
76
76
  )
77
77
 
78
78
 
79
- def create_tiledb_frame_from_dataframe(tiledb_uri_path: str, frame: List[str], column_types=dict):
79
+ def create_tiledb_frame_from_dataframe(tiledb_uri_path: str, frame: List[str], column_types: dict = None):
80
80
  """Create a TileDB file with the provided attributes to persistent storage.
81
81
 
82
82
  This will materialize the array directory and all
@@ -37,10 +37,17 @@
37
37
  "cell_type": "ascii",
38
38
  "quality_score": "float32"
39
39
  },
40
- "metadata": {
41
- "processing_date": ["2024-01-01", "2024-01-02", ...]
42
- }
43
- }
40
+ },
41
+ "python_env": """
42
+ . /system/gredit/clientos/etc/profile
43
+
44
+ ml Miniforge3
45
+ conda activate biocpy_miniforge
46
+
47
+ ~/.conda/envs/biocpy_miniforge/bin/python --version
48
+ which python
49
+ python --version
50
+ """,
44
51
  }
45
52
  ```
46
53
 
@@ -4,7 +4,6 @@ import subprocess
4
4
  from pathlib import Path
5
5
  from typing import Dict, List, Optional, Tuple
6
6
 
7
- import anndata
8
7
  import numpy as np
9
8
 
10
9
  from cellarr.buildutils_tiledb_array import create_tiledb_array
@@ -55,7 +54,12 @@ class SlurmBuilder:
55
54
  self.cpus_per_task = cpus_per_task
56
55
 
57
56
  def create_slurm_script(
58
- self, job_name: str, python_script: str, args: Dict, dependencies: Optional[str] = None
57
+ self,
58
+ job_name: str,
59
+ python_script: str,
60
+ args: Dict,
61
+ dependencies: Optional[str] = None,
62
+ python_env: str = "",
59
63
  ) -> str:
60
64
  """Create a SLURM job submission script."""
61
65
  script = f"""#!/bin/bash
@@ -70,8 +74,7 @@ class SlurmBuilder:
70
74
  script += f"#SBATCH --dependency={dependencies}\n"
71
75
 
72
76
  script += f"""
73
- source ~/.bashrc
74
- conda activate cellarr_env # MODIFY AS NEEDED
77
+ {python_env}
75
78
 
76
79
  python {python_script} '{json.dumps(args)}'
77
80
  """
@@ -81,7 +84,13 @@ python {python_script} '{json.dumps(args)}'
81
84
  return script_path
82
85
 
83
86
  def create_array_script(
84
- self, job_name: str, python_script: str, args: Dict, n_tasks: int, dependencies: Optional[str] = None
87
+ self,
88
+ job_name: str,
89
+ python_script: str,
90
+ args: Dict,
91
+ n_tasks: int,
92
+ dependencies: Optional[str] = None,
93
+ python_env: str = "",
85
94
  ) -> str:
86
95
  """Create a SLURM array job submission script."""
87
96
  script = f"""#!/bin/bash
@@ -97,8 +106,7 @@ python {python_script} '{json.dumps(args)}'
97
106
  script += f"#SBATCH --dependency={dependencies}\n"
98
107
 
99
108
  script += f"""
100
- source ~/.bashrc
101
- conda activate cellarr_env # MODIFY AS NEEDED
109
+ {python_env}
102
110
 
103
111
  python {python_script} '{json.dumps(args)}'
104
112
  """
@@ -112,7 +120,7 @@ python {python_script} '{json.dumps(args)}'
112
120
  result = subprocess.run(["sbatch", script_path], capture_output=True, text=True)
113
121
  return result.stdout.strip().split()[-1]
114
122
 
115
- def submit_gene_annotation_job(self, files: List[str], gene_options: Dict) -> str:
123
+ def submit_gene_annotation_job(self, files: List[str], gene_options: Dict, python_env: str) -> str:
116
124
  """Submit gene annotation processing job."""
117
125
  args = {
118
126
  "files": files,
@@ -121,12 +129,19 @@ python {python_script} '{json.dumps(args)}'
121
129
  "temp_dir": str(self.temp_dir / "gene_annotation"),
122
130
  }
123
131
 
132
+ parent_dir = str(Path(__file__).parent)
133
+
124
134
  script_path = self.create_slurm_script(
125
- job_name="cellarr_gene_annot", python_script="process_gene_annotation.py", args=args
135
+ job_name="cellarr_gene_annot",
136
+ python_script=f"{parent_dir}/process_gene_annotation.py",
137
+ args=args,
138
+ python_env=python_env,
126
139
  )
127
140
  return self.submit_job(script_path)
128
141
 
129
- def submit_sample_metadata_job(self, files: List[str], sample_options: Dict, dependency: str) -> str:
142
+ def submit_sample_metadata_job(
143
+ self, files: List[str], sample_options: Dict, dependency: str, python_env: str
144
+ ) -> str:
130
145
  """Submit sample metadata processing job."""
131
146
  args = {
132
147
  "files": files,
@@ -135,15 +150,18 @@ python {python_script} '{json.dumps(args)}'
135
150
  "temp_dir": str(self.temp_dir / "sample_metadata"),
136
151
  }
137
152
 
153
+ parent_dir = str(Path(__file__).parent)
154
+
138
155
  script_path = self.create_slurm_script(
139
156
  job_name="cellarr_sample_meta",
140
- python_script="process_sample_metadata.py",
157
+ python_script=f"{parent_dir}/process_sample_metadata.py",
141
158
  args=args,
159
+ python_env=python_env,
142
160
  # dependencies=f"afterok:{dependency}",
143
161
  )
144
162
  return self.submit_job(script_path)
145
163
 
146
- def submit_cell_metadata_job(self, files: List[str], cell_options: Dict, dependency: str) -> str:
164
+ def submit_cell_metadata_job(self, files: List[str], cell_options: Dict, dependency: str, python_env: str) -> str:
147
165
  """Submit cell metadata processing job."""
148
166
  args = {
149
167
  "files": files,
@@ -152,23 +170,21 @@ python {python_script} '{json.dumps(args)}'
152
170
  "temp_dir": str(self.temp_dir / "cell_metadata"),
153
171
  }
154
172
 
173
+ parent_dir = str(Path(__file__).parent)
174
+
155
175
  script_path = self.create_slurm_script(
156
176
  job_name="cellarr_cell_meta",
157
- python_script="process_cell_metadata.py",
177
+ python_script=f"{parent_dir}/process_cell_metadata.py",
158
178
  args=args,
179
+ python_env=python_env,
159
180
  # dependencies=f"afterok:{dependency}",
160
181
  )
161
182
  return self.submit_job(script_path)
162
183
 
163
- def submit_matrix_processing(self, files: List[str], matrix_options: Dict, dependency: str) -> Tuple[str, str]:
184
+ def submit_matrix_processing(
185
+ self, files: List[str], matrix_options: Dict, dependency: str, python_env: str
186
+ ) -> Tuple[str, str]:
164
187
  """Submit matrix processing as SLURM array job."""
165
- # Calculate cell offsets for each file
166
- offset = 0
167
- file_infos = []
168
- for file in files:
169
- file_infos.append({"file": file, "offset": offset})
170
- adata = anndata.read_h5ad(file, backed="r")
171
- offset += adata.n_obs
172
188
 
173
189
  # Create matrix TileDB array
174
190
  matrix_uri = str(self.output_dir / "assays" / matrix_options["matrix_name"])
@@ -184,16 +200,19 @@ python {python_script} '{json.dumps(args)}'
184
200
  "temp_dir": str(self.temp_dir / f"matrix_{matrix_options['matrix_name']}"),
185
201
  "matrix_options": matrix_options,
186
202
  "gene_annotation_file": str(self.temp_dir / "gene_annotation/gene_set.json"),
187
- "file_infos": file_infos,
203
+ "files": files,
188
204
  }
189
205
 
206
+ parent_dir = str(Path(__file__).parent)
207
+
190
208
  # Submit array job
191
209
  array_script = self.create_array_script(
192
210
  job_name=f"matrix_{matrix_options['matrix_name']}",
193
- python_script="process_matrix.py",
211
+ python_script=f"{parent_dir}/process_matrix.py",
194
212
  args=array_args,
195
213
  n_tasks=len(files),
196
214
  dependencies=f"afterok:{dependency}",
215
+ python_env=python_env,
197
216
  )
198
217
  array_job_id = self.submit_job(array_script)
199
218
 
@@ -202,28 +221,32 @@ python {python_script} '{json.dumps(args)}'
202
221
  "output_dir": str(self.output_dir),
203
222
  "temp_dir": str(self.temp_dir / f"matrix_{matrix_options['matrix_name']}"),
204
223
  "matrix_options": matrix_options,
205
- "file_infos": file_infos,
224
+ "files": files,
206
225
  }
207
226
 
208
227
  final_script = self.create_slurm_script(
209
228
  job_name=f"finalize_matrix_{matrix_options['matrix_name']}",
210
- python_script="finalize_matrix.py",
229
+ python_script=f"{parent_dir}/finalize_matrix.py",
211
230
  args=final_args,
212
231
  dependencies=f"afterok:{array_job_id}",
232
+ python_env=python_env,
213
233
  )
214
234
  final_job_id = self.submit_job(final_script)
215
235
 
216
236
  return array_job_id, final_job_id
217
237
 
218
- def submit_final_assembly(self, matrix_names: List[str], dependencies: List[str]) -> str:
238
+ def submit_final_assembly(self, matrix_names: List[str], dependencies: List[str], python_env: str) -> str:
219
239
  """Submit final assembly job."""
220
240
  args = {"input_dir": str(self.output_dir), "output_dir": str(self.output_dir), "matrix_names": matrix_names}
221
241
 
242
+ parent_dir = str(Path(__file__).parent)
243
+
222
244
  script_path = self.create_slurm_script(
223
245
  job_name="cellarr_final_assembly",
224
- python_script="final_assembly.py",
246
+ python_script=f"{parent_dir}/final_assembly.py",
225
247
  args=args,
226
248
  dependencies=f"afterok:{','.join(dependencies)}",
249
+ python_env=python_env,
227
250
  )
228
251
  return self.submit_job(script_path)
229
252
 
@@ -242,8 +265,9 @@ def main():
242
265
  log_dir = base_dir / "logs"
243
266
  temp_dir = base_dir / "temp"
244
267
  final_dir = base_dir / "final"
268
+ assays_dir = base_dir / "final/assays"
245
269
 
246
- for d in [log_dir, temp_dir, final_dir]:
270
+ for d in [log_dir, temp_dir, final_dir, assays_dir]:
247
271
  d.mkdir(parents=True, exist_ok=True)
248
272
 
249
273
  # Read manifest
@@ -261,13 +285,17 @@ def main():
261
285
  )
262
286
 
263
287
  # Submit jobs
264
- gene_job_id = builder.submit_gene_annotation_job(manifest["files"], manifest.get("gene_options", {}))
288
+ gene_job_id = builder.submit_gene_annotation_job(
289
+ manifest["files"], manifest.get("gene_options", {}), manifest["python_env"]
290
+ )
265
291
 
266
292
  sample_job_id = builder.submit_sample_metadata_job(
267
- manifest["files"], manifest.get("sample_options", {}), gene_job_id
293
+ manifest["files"], manifest.get("sample_options", {}), gene_job_id, manifest["python_env"]
268
294
  )
269
295
 
270
- cell_job_id = builder.submit_cell_metadata_job(manifest["files"], manifest.get("cell_options", {}), sample_job_id)
296
+ cell_job_id = builder.submit_cell_metadata_job(
297
+ manifest["files"], manifest.get("cell_options", {}), sample_job_id, manifest["python_env"]
298
+ )
271
299
 
272
300
  # Process matrices
273
301
  matrix_options = manifest.get("matrix_options", [{"matrix_name": "counts"}])
@@ -276,11 +304,15 @@ def main():
276
304
 
277
305
  matrix_job_ids = []
278
306
  for matrix_opt in matrix_options:
279
- _, final_id = builder.submit_matrix_processing(manifest["files"], matrix_opt, f"{cell_job_id},{gene_job_id}")
307
+ _, final_id = builder.submit_matrix_processing(
308
+ manifest["files"], matrix_opt, f"{cell_job_id},{gene_job_id}", manifest["python_env"]
309
+ )
280
310
  matrix_job_ids.append(final_id)
281
311
 
282
312
  # Submit final assembly
283
- builder.submit_final_assembly([opt["matrix_name"] for opt in matrix_options], matrix_job_ids)
313
+ builder.submit_final_assembly(
314
+ [opt["matrix_name"] for opt in matrix_options], matrix_job_ids, manifest["python_env"]
315
+ )
284
316
 
285
317
 
286
318
  if __name__ == "__main__":
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import sys
2
3
 
3
4
  from cellarr import CellArrDataset
4
5
 
@@ -18,3 +19,11 @@ def final_assembly(args_json: str):
18
19
 
19
20
  with open(f"{args['output_dir']}/metadata.json", "w") as f:
20
21
  json.dump(metadata, f)
22
+
23
+
24
+ if __name__ == "__main__":
25
+ if len(sys.argv) != 2:
26
+ print("Usage: python final_assembly.py '<json_args>'")
27
+ sys.exit(1)
28
+
29
+ final_assembly(sys.argv[1])
@@ -15,7 +15,7 @@ def finalize_matrix(args_json: str):
15
15
 
16
16
  # Verify all tasks completed
17
17
  completed_dir = Path(args["temp_dir"]) / "completed"
18
- expected_tasks = len(args["file_infos"])
18
+ expected_tasks = len(args["files"])
19
19
  completed_tasks = len(list(completed_dir.glob("task_*.json")))
20
20
 
21
21
  if completed_tasks != expected_tasks:
@@ -30,7 +30,7 @@ def finalize_matrix(args_json: str):
30
30
  json.dump(
31
31
  {
32
32
  "matrix_name": args["matrix_options"]["matrix_name"],
33
- "files_processed": len(args["file_infos"]),
33
+ "files_processed": len(args["files"]),
34
34
  "uri": matrix_uri,
35
35
  },
36
36
  f,
@@ -1,4 +1,6 @@
1
1
  import json
2
+ import sys
3
+ from pathlib import Path
2
4
 
3
5
  import pandas as pd
4
6
 
@@ -15,7 +17,7 @@ def process_gene_annotation(args_json: str):
15
17
 
16
18
  # Extract gene information from all files
17
19
  files_cache = uad.extract_anndata_info(
18
- args["files"], var_feature_column=args["gene_options"].get("feature_column", "index")
20
+ args["files"], var_feature_column=args.get("gene_options", {}).get("feature_column", "index")
19
21
  )
20
22
 
21
23
  # Scan for features
@@ -24,10 +26,20 @@ def process_gene_annotation(args_json: str):
24
26
 
25
27
  # Create gene annotation dataframe
26
28
  gene_annotation = pd.DataFrame({"cellarr_gene_index": gene_set}, index=gene_set)
29
+ gene_annotation.reset_index(drop=True, inplace=True)
30
+
31
+ # Create TileDB store
32
+ create_tiledb_frame_from_dataframe(f"{args['output_dir']}/gene_annotation", gene_annotation)
27
33
 
28
34
  # Save gene set for later use
35
+ Path(args["temp_dir"]).mkdir(parents=True, exist_ok=True)
29
36
  with open(f"{args['temp_dir']}/gene_set.json", "w") as f:
30
37
  json.dump(gene_set, f)
31
38
 
32
- # Create TileDB store
33
- create_tiledb_frame_from_dataframe(f"{args['output_dir']}/gene_annotation", gene_annotation)
39
+
40
+ if __name__ == "__main__":
41
+ if len(sys.argv) != 2:
42
+ print("Usage: python process_gene_annotation.py '<json_args>'")
43
+ sys.exit(1)
44
+
45
+ process_gene_annotation(sys.argv[1])
@@ -3,6 +3,8 @@ import os
3
3
  import sys
4
4
  from pathlib import Path
5
5
 
6
+ import tiledb
7
+
6
8
  from cellarr import utils_anndata as uad
7
9
  from cellarr.buildutils_tiledb_array import write_csr_matrix_to_tiledb
8
10
 
@@ -24,9 +26,13 @@ def process_matrix_file(args_json: str):
24
26
  gene_map = {gene: idx for idx, gene in enumerate(gene_set)}
25
27
 
26
28
  # Get file and offset for this task
27
- file_info = args["file_infos"][task_id]
28
- input_file = file_info["file"]
29
- row_offset = file_info["offset"]
29
+ file_info = args["files"][task_id]
30
+ input_file = file_info
31
+
32
+ # get sample offset
33
+ sample_uri = tiledb.open(f"{args['output_dir']}/sample_metadata", "r")
34
+ sample_row = sample_uri.df[task_id]
35
+ row_offset = sample_row["cellarr_sample_start_index"]
30
36
 
31
37
  # Process the file
32
38
  matrix = uad.remap_anndata(input_file, gene_map, layer_matrix_name=args["matrix_options"]["matrix_name"])
@@ -36,6 +42,8 @@ def process_matrix_file(args_json: str):
36
42
  write_csr_matrix_to_tiledb(matrix_uri, matrix[args["matrix_options"]["matrix_name"]], row_offset=row_offset)
37
43
 
38
44
  # Save task completion marker
45
+ Path(args["temp_dir"]).mkdir(parents=True, exist_ok=True)
46
+ Path(args["temp_dir"] + "/completed").mkdir(parents=True, exist_ok=True)
39
47
  task_marker = Path(args["temp_dir"]) / "completed" / f"task_{task_id}.json"
40
48
  task_marker.parent.mkdir(exist_ok=True)
41
49
  with open(task_marker, "w") as f:
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import sys
3
+ from pathlib import Path
3
4
 
4
5
  import pandas as pd
5
6
 
@@ -64,6 +65,7 @@ def process_sample_metadata(args_json: str):
64
65
  # Save metadata for subsequent steps
65
66
  metadata = {"num_samples": len(sample_names), "total_cells": int(sample_metadata["cellarr_cell_counts"].sum())}
66
67
 
68
+ Path(args["temp_dir"]).mkdir(parents=True, exist_ok=True)
67
69
  with open(f"{args['temp_dir']}/sample_metadata.json", "w") as f:
68
70
  json.dump(metadata, f)
69
71
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cellarr
3
- Version: 0.4.1.dev1
3
+ Version: 0.4.1.dev2
4
4
  Summary: TileDB-based array storage for genomics data collections.
5
5
  Home-page: https://github.com/BiocPy/cellarr
6
6
  Author: Jayaram Kancherla
@@ -45,7 +45,7 @@ datasets but can be generalized to store any 2-dimensional experimental data.
45
45
 
46
46
  To get started, install the package from [PyPI](https://pypi.org/project/cellarr/)
47
47
 
48
- ```bash
48
+ ```sh
49
49
  pip install cellarr
50
50
 
51
51
  ## to include optional dependencies
@@ -156,6 +156,47 @@ print(dataset)
156
156
 
157
157
  Check out the [documentation](https://biocpy.github.io/cellarr/tutorial.html) for more details.
158
158
 
159
+ ### Building on HPC environments with `slurm`
160
+
161
+ To simplify building TileDB files on HPC environments that use `slurm`, there are a few steps you need to follow.
162
+
163
+ - Step 1: Construct a manifest file
164
+ A minimal manifest file (json) must contain the following fields
165
+ - `"files"`: A list of file path to the input `h5ad` objects.
166
+ - `"python_env"`: A set of commands to activate the Python environment containing this package and its dependencies.
167
+
168
+ Here’s an example of the manifest file:
169
+ ```json
170
+ manifest = {
171
+ "files": your/list/of/files,
172
+ "python_env": """
173
+ ml Miniforge3
174
+ conda activate cellarr
175
+
176
+ python --version
177
+ which python
178
+ """,
179
+ "matrix_options": [
180
+ {
181
+ "matrix_name": "non_zero_cells",
182
+ "dtype": "uint32"
183
+ },
184
+ {
185
+ "matrix_name": "pseudo_bulk_log_normed",
186
+ "dtype": "float32"
187
+ }
188
+ ],
189
+ }
190
+ ```
191
+ For more options, check out the [README](./src/cellarr/slurm/README.md).
192
+
193
+ - Step 2: Submit the job
194
+ Once your manifest file is ready, you can submit the necessary jobs using the `cellarr_build` CLI. Run the following command:
195
+
196
+ ```sh
197
+ cellarr_build --input-manifest your/path/to/test_manifest.json --output-dir your/path/to/output --memory-per-job 8 --cpus-per-task 2
198
+ ```
199
+
159
200
  ### Query a `CellArrDataset`
160
201
 
161
202
  Users have the option to reuse the `dataset` object returned when building the dataset or by creating a `CellArrDataset` object by initializing it to the path where the files were created.
@@ -42,13 +42,13 @@ src/cellarr/utils_anndata.py
42
42
  src/cellarr.egg-info/PKG-INFO
43
43
  src/cellarr.egg-info/SOURCES.txt
44
44
  src/cellarr.egg-info/dependency_links.txt
45
+ src/cellarr.egg-info/entry_points.txt
45
46
  src/cellarr.egg-info/not-zip-safe
46
47
  src/cellarr.egg-info/requires.txt
47
48
  src/cellarr.egg-info/top_level.txt
48
49
  src/cellarr/slurm/README.md
49
50
  src/cellarr/slurm/__init__.py
50
51
  src/cellarr/slurm/build_cellarr_steps.py
51
- src/cellarr/slurm/build_cellarr_steps_bkp.py
52
52
  src/cellarr/slurm/final_assembly.py
53
53
  src/cellarr/slurm/finalize_matrix.py
54
54
  src/cellarr/slurm/process_cell_metadata.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cellarr_build = cellarr.slurm.build_cellarr_steps:main
@@ -1,185 +0,0 @@
1
- import argparse
2
- import json
3
- import os
4
- import subprocess
5
- from pathlib import Path
6
- from typing import Dict, Optional
7
-
8
-
9
- __author__ = "Jayaram Kancherla"
10
- __copyright__ = "Jayaram Kancherla"
11
- __license__ = "MIT"
12
-
13
-
14
- def create_slurm_script(
15
- job_name: str,
16
- log_dir: str,
17
- python_script: str,
18
- args: Dict,
19
- memory_gb: int = 64,
20
- time_hours: int = 24,
21
- cpus_per_task: int = 4,
22
- dependencies: Optional[str] = None,
23
- ) -> str:
24
- """Create SLURM job submission script."""
25
- script = f"""#!/bin/bash
26
- #SBATCH --job-name={job_name}
27
- #SBATCH --output={log_dir}/{job_name}_%j.out
28
- #SBATCH --error={log_dir}/{job_name}_%j.err
29
- #SBATCH --time={time_hours}:00:00
30
- #SBATCH --mem={memory_gb}G
31
- #SBATCH --cpus-per-task={cpus_per_task}
32
- """
33
- if dependencies:
34
- script += f"#SBATCH --dependency={dependencies}\n"
35
-
36
- script += """
37
- source ~/.bashrc
38
- conda activate cellarr_env # Modify as needed for your environment
39
-
40
- python {python_script} '{json.dumps(args)}'
41
- """
42
- script_path = os.path.join(log_dir, f"{job_name}_submit.sh")
43
- with open(script_path, "w") as f:
44
- f.write(script)
45
- return script_path
46
-
47
-
48
- def submit_slurm_job(script_path: str, dependency: Optional[str] = None) -> str:
49
- """Submit SLURM job and return job ID."""
50
- cmd = ["sbatch"]
51
- if dependency:
52
- cmd.extend(["--dependency", dependency])
53
- cmd.append(script_path)
54
-
55
- result = subprocess.run(cmd, capture_output=True, text=True)
56
- job_id = result.stdout.strip().split()[-1]
57
- return job_id
58
-
59
-
60
- def main():
61
- parser = argparse.ArgumentParser(description="Build CellArrDataset using SLURM steps")
62
- parser.add_argument("--input-manifest", required=True, help="Path to JSON manifest file")
63
- parser.add_argument("--output-dir", required=True, help="Base output directory")
64
- parser.add_argument("--memory-per-job", type=int, default=64, help="Memory in GB per job")
65
- parser.add_argument("--time-per-job", type=int, default=24, help="Time in hours per job")
66
- parser.add_argument("--cpus-per-task", type=int, default=4, help="CPUs per task")
67
- args = parser.parse_args()
68
-
69
- # Create output directories
70
- base_dir = Path(args.output_dir)
71
- log_dir = base_dir / "logs"
72
- temp_dir = base_dir / "temp"
73
- final_dir = base_dir / "final"
74
-
75
- for d in [log_dir, temp_dir, final_dir]:
76
- d.mkdir(parents=True, exist_ok=True)
77
-
78
- # Read manifest file
79
- with open(args.input_manifest) as f:
80
- manifest = json.load(f)
81
-
82
- # Step 1: Gene Annotation Job
83
- gene_args = {
84
- "files": manifest["files"],
85
- "output_dir": str(final_dir),
86
- "gene_options": manifest.get("gene_options", {}),
87
- "temp_dir": str(temp_dir / "gene_annotation"),
88
- }
89
-
90
- gene_script = create_slurm_script(
91
- job_name="cellarr_gene_annot",
92
- log_dir=str(log_dir),
93
- python_script="process_gene_annotation.py",
94
- args=gene_args,
95
- memory_gb=args.memory_per_job,
96
- cpus_per_task=args.cpus_per_task,
97
- )
98
- gene_job_id = submit_slurm_job(gene_script)
99
-
100
- # Step 2: Sample Metadata Job
101
- sample_args = {
102
- "files": manifest["files"],
103
- "output_dir": str(final_dir),
104
- "sample_options": manifest.get("sample_options", {}),
105
- "temp_dir": str(temp_dir / "sample_metadata"),
106
- }
107
-
108
- sample_script = create_slurm_script(
109
- job_name="cellarr_sample_meta",
110
- log_dir=str(log_dir),
111
- python_script="process_sample_metadata.py",
112
- args=sample_args,
113
- memory_gb=args.memory_per_job,
114
- cpus_per_task=args.cpus_per_task,
115
- # dependencies=f"afterok:{gene_job_id}",
116
- )
117
- sample_job_id = submit_slurm_job(sample_script)
118
-
119
- # Step 3: Cell Metadata Job
120
- cell_args = {
121
- "files": manifest["files"],
122
- "output_dir": str(final_dir),
123
- "cell_options": manifest.get("cell_options", {}),
124
- "temp_dir": str(temp_dir / "cell_metadata"),
125
- }
126
-
127
- cell_script = create_slurm_script(
128
- job_name="cellarr_cell_meta",
129
- log_dir=str(log_dir),
130
- python_script="process_cell_metadata.py",
131
- args=cell_args,
132
- memory_gb=args.memory_per_job,
133
- cpus_per_task=args.cpus_per_task,
134
- # dependencies=f"afterok:{sample_job_id}",
135
- )
136
- cell_job_id = submit_slurm_job(cell_script)
137
-
138
- # Step 4: Matrix Jobs (one per matrix type)
139
- matrix_job_ids = []
140
- matrix_options = manifest.get("matrix_options", [{"matrix_name": "counts"}])
141
- if not isinstance(matrix_options, list):
142
- matrix_options = [matrix_options]
143
-
144
- for matrix_opt in matrix_options:
145
- matrix_args = {
146
- "files": manifest["files"],
147
- "output_dir": str(final_dir),
148
- "matrix_options": matrix_opt,
149
- "gene_annotation_file": str(temp_dir / "gene_annotation/gene_set.json"),
150
- "temp_dir": str(temp_dir / f"matrix_{matrix_opt['matrix_name']}"),
151
- }
152
-
153
- matrix_script = create_slurm_script(
154
- job_name=f"cellarr_matrix_{matrix_opt['matrix_name']}",
155
- log_dir=str(log_dir),
156
- python_script="process_matrix.py",
157
- args=matrix_args,
158
- memory_gb=args.memory_per_job * 2, # More memory for matrix processing
159
- cpus_per_task=args.cpus_per_task,
160
- dependencies=f"afterok:{cell_job_id},{gene_job_id}",
161
- )
162
- job_id = submit_slurm_job(matrix_script)
163
- matrix_job_ids.append(job_id)
164
-
165
- # Step 5: Final Assembly Job
166
- final_args = {
167
- "input_dir": str(final_dir),
168
- "output_dir": str(final_dir),
169
- "matrix_names": [opt["matrix_name"] for opt in matrix_options],
170
- }
171
-
172
- final_script = create_slurm_script(
173
- job_name="cellarr_final_assembly",
174
- log_dir=str(log_dir),
175
- python_script="final_assembly.py",
176
- args=final_args,
177
- memory_gb=args.memory_per_job,
178
- cpus_per_task=args.cpus_per_task,
179
- dependencies=f"afterok:{','.join(matrix_job_ids)}",
180
- )
181
- submit_slurm_job(final_script)
182
-
183
-
184
- if __name__ == "__main__":
185
- main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes