-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmain.py
More file actions
175 lines (151 loc) · 6.22 KB
/
main.py
File metadata and controls
175 lines (151 loc) · 6.22 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
from fastapi import FastAPI, Request, Form, BackgroundTasks, HTTPException
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
import runSimulation
import os
import sys
import io
from contextlib import redirect_stdout
# Check Environment Variables on startup
if not os.environ.get("SOFTWARE_SIF_FILE") and not os.environ.get("SOFTWARE_SANDBOX_DIR"):
print("\033[91mCRITICAL ERROR: Environment not configured.\033[0m")
print("You must source 'setup.sh' before starting this server.")
print("Usage: source setup.sh <sif_file> [sandbox_dir]")
sys.exit(1)
app = FastAPI()
# Ensure templates directory exists
if not os.path.exists("templates"):
os.makedirs("templates")
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request, "condor_flavours": runSimulation.CONDOR_FLAVOURS})
@app.post("/submit")
async def submit_simulation(
particle_name: str = Form(...),
mode: str = Form("beam"),
energy: float = Form(100),
wall_distance: float = Form(0),
energy_low: float = Form(0),
energy_high: float = Form(2000),
nevs: int = Form(1000),
nfiles: int = Form(100),
batch_system: str = Form("none"), # none, sukap, cedar, condor
rap_account: str = Form(""),
sukap_queue: str = Form("all"),
condor_queue: str = Form("tomorrow"),
seed: int = Form(20260129),
# Boolean flags: Default to False so that if unchecked (sending nothing), they are False.
# The HTML form will have them checked by default, sending 'true'.
run_wcsim: bool = Form(False),
run_mdt: bool = Form(False),
run_fq: bool = Form(False)
):
# Check Environment Variables manually to avoid sys.exit() in SimulationConfig.validate()
siffile = os.environ.get("SOFTWARE_SIF_FILE")
sandbox = os.environ.get("SOFTWARE_SANDBOX_DIR")
if not siffile and not sandbox:
msg = ("Server Error: SOFTWARE_SIF_FILE and SOFTWARE_SANDBOX_DIR are not set in the environment.<br>"
"Please source the setup script before running the server:<br>"
"<code>source setup.sh <sif_file> [sandbox_dir] [--build]</code>")
raise HTTPException(status_code=500, detail=msg)
# Initialize Configuration
config = runSimulation.SimulationConfig()
config.ParticleName = particle_name
config.nevs = nevs
config.nfiles = nfiles
config.rngseed = seed
# Set Toggles
config.runWCSim = run_wcsim
config.runMDT = run_mdt
config.runFQ = run_fq
# Configure Mode
if mode == "beam":
config.useBeam = True
config.useUniform = False
config.useCosmics = False
config.ParticleKE = energy
config.wallD = wall_distance
config.ParticlePosz = -(config.TankRadius - config.wallD)
elif mode == "uniform":
config.useBeam = False
config.useUniform = True
config.useCosmics = False
config.ParticleKELow = energy_low
config.ParticleKEHigh = energy_high
elif mode == "cosmics":
config.useBeam = False
config.useUniform = False
config.useCosmics = True
# Configure Batch System
if batch_system == "sukap":
if not sandbox:
raise HTTPException(status_code=400, detail="Configuration Error: SOFTWARE_SANDBOX_DIR is required for Sukap submission.")
config.submit_sukap_jobs = True
config.sukap_queue = sukap_queue
elif batch_system == "cedar":
config.submit_cedar_jobs = True
config.rapaccount = rap_account
elif batch_system == "condor":
if condor_queue not in runSimulation.CONDOR_FLAVOURS:
raise HTTPException(status_code=400, detail=f"Invalid Condor JobFlavour. Must be one of: {', '.join(runSimulation.CONDOR_FLAVOURS)}")
config.submit_condor_jobs = True
config.condor_queue = condor_queue
# Generate Files
try:
fgen = runSimulation.FileGenerator(config)
fgen.create_directories()
fgen.generate_mac_files()
fgen.generate_shell_scripts()
except Exception as e:
raise HTTPException(status_code=500, detail=f"File Generation Failed: {str(e)}")
# Submit Jobs
submitter = runSimulation.JobSubmitter(config, fgen)
# Scan jobs to get counts for the response message
n_submit, n_skip = submitter.scan_jobs()
msg_suffix = f" (Submitted {n_submit} jobs, Skipped {n_skip} existing)"
try:
if config.submit_sukap_jobs:
submitter.submit_sukap()
if config.submit_cedar_jobs:
submitter.submit_cedar()
if config.submit_condor_jobs:
submitter.submit_condor()
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
return {
"status": "success",
"message": f"Simulation configured for {particle_name}. Job submission to ({batch_system}) completed.{msg_suffix}",
"config_string": config.get_config_string()
}
@app.get("/status")
async def get_job_status(batch_system: str = "none"):
# Initialize config
config = runSimulation.SimulationConfig()
if batch_system == "sukap":
config.submit_sukap_jobs = True
elif batch_system == "cedar":
config.submit_cedar_jobs = True
elif batch_system == "condor":
config.submit_condor_jobs = True
status_checker = runSimulation.JobStatus(config)
return status_checker.get_jobs()
@app.post("/kill")
async def kill_all_jobs(batch_system: str = Form("none")):
config = runSimulation.SimulationConfig()
if batch_system == "sukap":
config.submit_sukap_jobs = True
elif batch_system == "cedar":
config.submit_cedar_jobs = True
elif batch_system == "condor":
config.submit_condor_jobs = True
else:
return {"status": "error", "message": "Invalid batch system selected."}
status_checker = runSimulation.JobStatus(config)
# Capture the output of the kill_jobs function
f = io.StringIO()
with redirect_stdout(f):
status_checker.kill_jobs()
output = f.getvalue()
return {"status": "success", "message": output}