Skip to content

Commit 87426f8

Browse files
committed
refactor: Simplify entry points. Suppress unneeded stdout and font errors
1 parent 042abef commit 87426f8

7 files changed

Lines changed: 118 additions & 102 deletions

File tree

compose.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ services:
1010
- ./configs:/configs:ro
1111
- ./src/downloader:/app:ro
1212
user: "${UID}:${GID}"
13-
command: python /app/main.py --config /configs/download.yaml
13+
command: python /app/downloader.py --config /configs/download.yaml
1414
restart: "no"
1515

1616
simulator:
@@ -23,5 +23,5 @@ services:
2323
environment:
2424
- MPLCONFIGDIR=/tmp/matplotlib
2525
user: "${UID}:${GID}"
26-
command: python -m simulator.run --config /app/configs/simulator.yaml
26+
command: python -m simulator.diffraction_generator --config /app/configs/simulator.yaml
2727
restart: "no"

docker/downloader.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ RUN pip install --no-cache-dir -r /tmp/requirements.txt
1313
# Code and configs are mounted at runtime via docker-compose volumes:
1414
# - ./src/downloader -> /app
1515
# - ./configs -> /configs
16-
CMD ["python", "/app/main.py", "--config", "/configs/download.yaml"]
16+
CMD ["python", "/app/downloader.py", "--config", "/configs/download.yaml"]

docker/simulator.Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ SHELL ["bash", "-lc"]
99
ENV MAMBA_DEFAULT_ENV=sim
1010
ENV PATH="/opt/conda/envs/sim/bin:${PATH}"
1111
ENV GSAS_II_PATH=/opt/conda/envs/sim/GSAS-II
12+
ENV XDG_CACHE_HOME=/app/.cache
13+
ENV MPLCONFIGDIR=/app/.cache/matplotlib
1214

1315

1416

1517
# App layout
1618
WORKDIR /app
19+
# Ensure writable cache directories for fontconfig and matplotlib
20+
RUN mkdir -p /app/.cache/fontconfig /app/.cache/matplotlib && chmod -R 777 /app/.cache
1721
COPY src/simulator /app/simulator
1822

1923
# One-shot job entrypoint
20-
CMD ["python", "-m", "simulator.run", "--config", "/app/configs/simulator.yaml"]
24+
CMD ["python", "-m", "simulator.diffraction_generator", "--config", "/app/configs/simulator.yaml"]

src/simulator/diffraction_generator.py

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import time
77
import shutil
88
from tqdm import tqdm
9+
import argparse
10+
import yaml
11+
from typing import Dict, Any
912

1013
# Container: insert parent GSAS-II directory and import package
1114
GSAS_II_PARENT = os.environ.get("GSAS_II_PATH", "/opt/conda/envs/sim/GSAS-II")
@@ -101,7 +104,7 @@ def run(self, n_sims_per_file, master_seed=12345, cleanup_worker_dirs=True, **kw
101104
simulation_start_time = time.time()
102105
success_count = 0
103106

104-
with Pool(self.n_parallel_sims, maxtasksperchild=1000) as p:
107+
with Pool(self.n_parallel_sims, maxtasksperchild=1000, initializer=simulation_worker.suppress_worker_stdout) as p:
105108
results = tqdm(p.imap_unordered(simulation_worker.run_single_simulation, tasks), total=total_jobs_to_run)
106109
for result in results:
107110
if result: success_count += 1
@@ -129,3 +132,91 @@ def run(self, n_sims_per_file, master_seed=12345, cleanup_worker_dirs=True, **kw
129132
print(f"Throughput: {sims_per_sec:.2f} simulations/sec")
130133
print(f"Avg. Time/Simulation: {avg_time_per_sim:.3f} seconds")
131134
print("---------------------------")
135+
136+
137+
def load_config(path: Path) -> Dict[str, Any]:
138+
if not path.exists():
139+
raise FileNotFoundError(f"Config file not found: {path}")
140+
with open(path, "r") as f:
141+
cfg = yaml.safe_load(f) or {}
142+
return cfg
143+
144+
145+
def parse_args() -> argparse.Namespace:
146+
parser = argparse.ArgumentParser(description="AlphaDiffract simulator runner")
147+
parser.add_argument("--config", type=str, default="/app/configs/simulator.yaml", help="Path to YAML config file")
148+
return parser.parse_args()
149+
150+
151+
def main() -> None:
152+
args = parse_args()
153+
cfg = load_config(Path(args.config))
154+
155+
# Paths
156+
input_directory = str(Path(cfg["input_directory"]))
157+
output_directory = str(Path(cfg["output_directory"]))
158+
instprm_file = str(Path(cfg["instprm_file"]))
159+
error_directory = str(Path(cfg["error_directory"]))
160+
worker_base_dir = str(Path(cfg["worker_base_dir"]))
161+
162+
# Execution controls
163+
parallel_jobs = int(cfg["parallel_jobs"])
164+
sims_per_file = int(cfg["sims_per_file"])
165+
master_seed = int(cfg["master_seed"])
166+
cleanup_worker_dirs = bool(cfg["cleanup_worker_dirs"])
167+
168+
# Parameter ranges
169+
ranges = {
170+
"strain_range": tuple(map(float, cfg["strain_range"])),
171+
"size_range": tuple(map(float, cfg["size_range"])),
172+
"U_range": tuple(map(float, cfg["U_range"])),
173+
"V_range": tuple(map(float, cfg["V_range"])),
174+
"W_range": tuple(map(float, cfg["W_range"])),
175+
"st_range": tuple(map(float, cfg["st_range"])),
176+
"en_range": tuple(map(float, cfg["en_range"])),
177+
"Npoints_range": tuple(map(int, cfg["Npoints_range"])),
178+
"scaler_range": tuple(map(float, cfg["scaler_range"])),
179+
"wl_range": tuple(map(float, cfg["wl_range"])),
180+
"proportional_noise_range": tuple(map(float, cfg["proportional_noise_range"])),
181+
"constant_noise_range": tuple(map(float, cfg["constant_noise_range"])),
182+
}
183+
184+
# Log planned operation
185+
print("\n--- Simulator configuration ---")
186+
print(f"Input directory: {input_directory}")
187+
print(f"Output directory: {output_directory}")
188+
print(f"Error directory: {error_directory}")
189+
print(f"Worker base dir: {worker_base_dir}")
190+
print(f"Instrument file: {instprm_file}")
191+
print(f"Parallel jobs: {parallel_jobs}")
192+
print(f"Sims per file: {sims_per_file}")
193+
print(f"Master seed: {master_seed}")
194+
print(f"Cleanup worker dirs: {cleanup_worker_dirs}")
195+
print("Parameter ranges:")
196+
for k, v in ranges.items():
197+
print(f" {k}: {v}")
198+
print("--------------------------------\n")
199+
200+
# Run simulations
201+
try:
202+
gen = DiffractionGenerator(
203+
input_dir=input_directory,
204+
output_dir=output_directory,
205+
instprm_file=instprm_file,
206+
n_parallel_sims=parallel_jobs,
207+
error_dir=error_directory,
208+
worker_base_dir=worker_base_dir,
209+
)
210+
gen.run(
211+
n_sims_per_file=sims_per_file,
212+
master_seed=master_seed,
213+
cleanup_worker_dirs=cleanup_worker_dirs,
214+
**ranges,
215+
)
216+
except Exception as e:
217+
print(f"FATAL: Simulator run failed: {e}", file=sys.stderr)
218+
sys.exit(1)
219+
220+
221+
if __name__ == "__main__":
222+
main()

src/simulator/run.py

Lines changed: 0 additions & 97 deletions
This file was deleted.

src/simulator/simulation_worker.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@
1717
except ImportError:
1818
sys.exit(f"FATAL ERROR: Could not import GSASII.GSASIIscriptable. Checked parent '{GSAS_II_PARENT}'. Ensure gsas2pkg is installed or set GSAS_II_PATH correctly.")
1919

20+
def suppress_worker_stdout():
21+
"""
22+
Redirect stdout to /dev/null in worker processes without affecting stderr.
23+
This silences Python-level prints and C-extension writes to fd=1.
24+
"""
25+
# Redirect Python-level prints
26+
try:
27+
sys.stdout = open(os.devnull, 'w')
28+
except Exception:
29+
pass
30+
# Redirect OS-level fd 1 (stdout) to /dev/null for any C-level writes
31+
try:
32+
devnull_fd = os.open(os.devnull, os.O_WRONLY)
33+
os.dup2(devnull_fd, 1)
34+
os.close(devnull_fd)
35+
except Exception:
36+
pass
37+
2038
# --- START: Metadata Parsing Logic ---
2139
# This section contains all functions needed to parse metadata directly from
2240
# the source .cif and .dif/.txt files, which is more reliable than using

0 commit comments

Comments
 (0)