Skip to content

Commit a93e13a

Browse files
committed
Add helper script to generate SLURM cluster configuration from annotation config
1 parent 9da08f7 commit a93e13a

File tree

3 files changed

+109
-1
lines changed

3 files changed

+109
-1
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ snakemake -n --snakefile bin/Snakefile_annotate
109109
./bin/annotate_toydata.sh
110110
```
111111

112+
Helper script:
113+
- `bin/generate_cluster_from_config.py`: regenerate `cluster_annotate.yml` from `config_annotate.yml` so SLURM 요청 자원이 파이프라인 threads/memory와 동기화될 때 사용.
114+
112115
For a detailed tutorial with toy data, see the **[Wiki](Wiki.md)**.
113116

114117
## Installation
@@ -168,10 +171,12 @@ snakemake -n --snakefile bin/Snakefile_annotate
168171
# Submit to SLURM
169172
sbatch -A [account] -p [partition] -c 1 --mem=1g \
170173
-J annotate -o annotate.out -e annotate.err \
171-
--wrap="./bin/annotate_toydata.sh"
174+
--wrap="./bin/annotate_toydata.sh
175+
"
172176

173177
# Or run directly
174178
./bin/annotate_toydata.sh
179+
175180
```
176181

177182
**Output:** `results/complete_draft.gff3`

Wiki.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,3 +547,16 @@ With the test environment above (4 nodes, 256 CPUs, 256 GB/node), the toy datase
547547

548548
- Issues: https://github.com/plantgenomicslab/Sylvan/issues
549549
- See also: [README.md](README.md) for configuration reference
550+
551+
552+
### Generate cluster config from config_annotate
553+
```
554+
python bin/generate_cluster_from_config.py --config config/config_annotate.yml --out config/cluster_annotate.yml --account cpu-s1-pgl-0 --partition cpu-s1-pgl-0
555+
```
556+
(To regenerate toydata cluster config, point --config/--out to toydata paths.)
557+
```
558+
python bin/generate_cluster_from_config.py --config toydata/config/config_annotate.yml --out toydata/config/cluster_annotate.yml --account cpu-s1-pgl-0 --partition cpu-s1-pgl-0
559+
```
560+
```
561+
chmod 775 bin/generate_cluster_from_config.py
562+
```
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python
2+
"""
3+
Generate a cluster_annotate.yml from a config_annotate.yml.
4+
5+
Usage:
6+
python bin/generate_cluster_from_config.py \
7+
--config config/config_annotate.yml \
8+
--out config/cluster_annotate.yml \
9+
--account cpu-s1-pgl-0 --partition cpu-s1-pgl-0
10+
11+
Only top-level sections that contain ncpus/threads/memory/time are copied.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import argparse
17+
from pathlib import Path
18+
from typing import Any, Dict
19+
20+
import yaml
21+
22+
23+
def build_arg_parser() -> argparse.ArgumentParser:
24+
p = argparse.ArgumentParser(description="Generate cluster_annotate.yml from config_annotate.yml")
25+
p.add_argument("--config", required=True, help="Path to config_annotate.yml")
26+
p.add_argument("--out", required=True, help="Output path for cluster_annotate.yml")
27+
p.add_argument("--account", default="placeholder", help="SLURM account (default: placeholder)")
28+
p.add_argument("--partition", default="placeholder", help="SLURM partition (default: placeholder)")
29+
p.add_argument("--time", default="14-00:00:00", help="Walltime (default: 14-00:00:00)")
30+
p.add_argument("--default-memory", default="4g", help="Default memory for __default__ (default: 4g)")
31+
p.add_argument("--default-ncpus", type=int, default=1, help="Default CPUs for __default__ (default: 1)")
32+
p.add_argument(
33+
"--output-pattern",
34+
default="results/logs/{rule}_{wildcards}.out",
35+
help="Output log pattern for __default__",
36+
)
37+
p.add_argument(
38+
"--error-pattern",
39+
default="results/logs/{rule}_{wildcards}.err",
40+
help="Error log pattern for __default__",
41+
)
42+
return p
43+
44+
45+
def load_config(path: Path) -> Dict[str, Any]:
46+
with path.open() as fh:
47+
return yaml.safe_load(fh) or {}
48+
49+
50+
def should_copy_section(section: Dict[str, Any]) -> bool:
51+
return any(key in section for key in ("ncpus", "threads", "memory", "time"))
52+
53+
54+
def main() -> None:
55+
args = build_arg_parser().parse_args()
56+
cfg_path = Path(args.config)
57+
out_path = Path(args.out)
58+
59+
cfg = load_config(cfg_path)
60+
61+
cluster: Dict[str, Any] = {
62+
"__default__": {
63+
"account": args.account,
64+
"partition": args.partition,
65+
"memory": args.default_memory,
66+
"ncpus": args.default_ncpus,
67+
"nodes": 1,
68+
"time": args.time,
69+
"name": "{rule}.{wildcards}",
70+
"output": args.output_pattern,
71+
"error": args.error_pattern,
72+
}
73+
}
74+
75+
for name, section in cfg.items():
76+
if isinstance(section, dict) and should_copy_section(section):
77+
cluster[name] = {}
78+
for key in ("ncpus", "threads", "memory", "time"):
79+
if key in section:
80+
cluster[name][key] = section[key]
81+
82+
out_path.parent.mkdir(parents=True, exist_ok=True)
83+
with out_path.open("w") as fh:
84+
yaml.safe_dump(cluster, fh, sort_keys=False, default_flow_style=False)
85+
86+
print(f"Wrote cluster config to {out_path}")
87+
88+
89+
if __name__ == "__main__":
90+
main()

0 commit comments

Comments
 (0)