-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path04_configuration_profiles.py
More file actions
executable file
·234 lines (188 loc) · 7.26 KB
/
Copy path04_configuration_profiles.py
File metadata and controls
executable file
·234 lines (188 loc) · 7.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/env python3
"""
Example 4: Configuration Profiles
Level: Intermediate
Time: ~2 minutes
Description:
Demonstrates the built-in configuration profiles that provide
pre-parameterised settings for common sequencing scenarios.
Profiles are plain dicts returned by get_profile() or apply_profile().
apply_profile() supports an optional overrides argument so that
individual parameters can be customised without rebuilding the full
configuration from scratch.
Profile field names differ from ReplayConfig field names in two
places:
- "timing_model_params" -> timing_params (ReplayConfig)
- "parallel_processing" -> parallel (ReplayConfig)
- "worker_count" -> workers (ReplayConfig)
This example shows how to map them correctly.
Usage:
python examples/04_configuration_profiles.py
Requirements:
- nanorunner installed (pip install -e .)
- Sample data in examples/sample_data/
Expected Output:
- Lists all available profiles with their descriptions
- Runs three profiles against the singleplex sample data
- Demonstrates per-parameter override on the bursty profile
- Completes in ~10-15 seconds
"""
import shutil
import tempfile
from pathlib import Path
from typing import Any, Dict
from nanopore_simulator import ReplayConfig, run_replay
from nanopore_simulator.profiles import PROFILES, apply_profile, list_profiles
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def profile_params_to_config_kwargs(params: Dict[str, Any]) -> Dict[str, Any]:
"""Translate profile field names to ReplayConfig field names.
Profiles use timing_model_params, parallel_processing, and worker_count.
ReplayConfig uses timing_params, parallel, and workers.
"""
kwargs = dict(params)
if "timing_model_params" in kwargs:
kwargs["timing_params"] = kwargs.pop("timing_model_params")
if "parallel_processing" in kwargs:
kwargs["parallel"] = kwargs.pop("parallel_processing")
if "worker_count" in kwargs:
kwargs["workers"] = kwargs.pop("worker_count")
# "operation" is in both -- no translation needed.
return kwargs
# ---------------------------------------------------------------------------
# Display helpers
# ---------------------------------------------------------------------------
def show_available_profiles() -> None:
"""Print all profiles with their descriptions."""
print("\n" + "=" * 60)
print("Available configuration profiles")
print("=" * 60)
print()
for name, description in list_profiles().items():
print(f" {name}")
print(f" {description}")
print()
def show_profile_details(name: str) -> None:
"""Print the parameter values stored in a profile."""
profile = PROFILES.get(name, {})
print(f" Profile '{name}' parameters:")
for key, value in profile.items():
if key == "description":
continue
print(f" {key}: {value}")
print()
# ---------------------------------------------------------------------------
# Simulation helpers
# ---------------------------------------------------------------------------
def run_with_profile(
profile_name: str,
source_dir: Path,
interval_override: float = 0.5,
) -> None:
"""Run a replay simulation using the named profile.
Args:
profile_name: A key from PROFILES.
source_dir: Path to the source FASTQ directory.
interval_override: Base interval to use (overrides profile default).
"""
print(f"\n{'=' * 60}")
print(f"Profile: {profile_name}")
print(f"{'=' * 60}")
params = apply_profile(profile_name, overrides={"interval": interval_override})
kwargs = profile_params_to_config_kwargs(params)
# Ensure interval and monitor_type are present.
kwargs.setdefault("interval", interval_override)
kwargs.setdefault("monitor_type", "basic")
target_dir = Path(tempfile.mkdtemp(prefix=f"nanorunner_profile_{profile_name}_"))
try:
config = ReplayConfig(
source_dir=source_dir,
target_dir=target_dir,
**kwargs,
)
print(f" timing_model: {config.timing_model}")
print(f" batch_size: {config.batch_size}")
print(f" parallel: {config.parallel}")
print(f" workers: {config.workers}")
print()
run_replay(config)
produced = list(target_dir.rglob("*.fastq"))
print(f" Produced {len(produced)} file(s).")
finally:
shutil.rmtree(target_dir, ignore_errors=True)
def demonstrate_override(source_dir: Path) -> None:
"""Show parameter override on the bursty profile."""
print("\n" + "=" * 60)
print("Profile override: bursty with custom parameters")
print("=" * 60)
print()
# Start from bursty but override several fields.
params = apply_profile(
"bursty",
overrides={
"interval": 0.3,
"worker_count": 2,
"batch_size": 2,
},
)
kwargs = profile_params_to_config_kwargs(params)
kwargs.setdefault("monitor_type", "basic")
print(" Overridden parameters:")
print(f" interval: {kwargs.get('interval')} s")
print(f" workers: {kwargs.get('workers')}")
print(f" batch_size: {kwargs.get('batch_size')}")
print()
target_dir = Path(tempfile.mkdtemp(prefix="nanorunner_profile_custom_"))
try:
config = ReplayConfig(
source_dir=source_dir,
target_dir=target_dir,
**kwargs,
)
run_replay(config)
produced = list(target_dir.rglob("*.fastq"))
print(f" Produced {len(produced)} file(s).")
finally:
shutil.rmtree(target_dir, ignore_errors=True)
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main() -> int:
print("=" * 60)
print("Example 4: Configuration Profiles")
print("=" * 60)
print()
source_dir = Path(__file__).parent / "sample_data" / "singleplex"
if not source_dir.exists():
print(f"Error: sample data not found at {source_dir}")
print("Run this script from the repository root directory.")
return 1
show_available_profiles()
run_with_profile("development", source_dir)
run_with_profile("steady", source_dir)
run_with_profile("bursty", source_dir)
demonstrate_override(source_dir)
print()
print("=" * 60)
print("Summary")
print("=" * 60)
print()
print(" get_profile(name) -- retrieve profile dict (or None)")
print(" apply_profile(name) -- dict ready for config construction")
print(" apply_profile(name, {}) -- same with parameter overrides")
print()
print(" CLI equivalents:")
print(" nanorunner replay /src /dst --profile bursty")
print(" nanorunner replay /src /dst --profile steady --interval 3")
print()
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except KeyboardInterrupt:
print("\nInterrupted by user")
raise SystemExit(1)
except Exception as exc:
print(f"\nError: {exc}")
raise SystemExit(1)