Skip to content

Commit 87111d5

Browse files
authored
refactor routes into separate routers (#3)
1 parent 5a4a7f5 commit 87111d5

File tree

18 files changed

+394
-195
lines changed

18 files changed

+394
-195
lines changed

web-conexs-api/src/slurm_submission_service/submitter.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
JOB_RUNNING = "RUNNING"
3939
JOB_COMPLETED = "COMPLETED"
4040
JOB_FAILED = "FAILED"
41+
JOB_PENDING = "PENDING"
4142

4243

4344
def get_token():
@@ -158,7 +159,6 @@ def submit_orca(session, sim: Simulation):
158159
sim.working_directory = working_dir
159160
sim.submission_date = datetime.datetime.now()
160161
sim.status = SimulationStatus.submitted
161-
print(sim)
162162
update_simulation(session, sim)
163163
except Exception:
164164
sim.status = SimulationStatus.failed
@@ -256,14 +256,13 @@ def run_update():
256256

257257
job_map = {}
258258

259-
print(SLURM_USER)
259+
# print(SLURM_USER)
260260

261261
# for j in jobs:
262262
# print(j["account"])
263263

264264
for j in jobs:
265265
if j["account"] == SLURM_USER:
266-
print("FOUND")
267266
job_map[j["job_id"]] = {"state": j["job_state"][0]}
268267

269268
print(f"Number of active jobs {len(active)}")
@@ -276,6 +275,9 @@ def run_update():
276275
if state == JOB_RUNNING and a.status != SimulationStatus.running:
277276
a.status = SimulationStatus.running
278277
update_simulation(session, a)
278+
elif state == JOB_PENDING and a.status != SimulationStatus.submitted:
279+
a.status = SimulationStatus.completed
280+
update_simulation(session, a)
279281
elif state == JOB_COMPLETED and a.status != SimulationStatus.completed:
280282
a.status = SimulationStatus.completed
281283
update_simulation(session, a)
@@ -284,7 +286,8 @@ def run_update():
284286
update_simulation(session, a)
285287

286288
else:
287-
a.status = SimulationStatus.completed
289+
# TODO better state for whatever slurm might return
290+
a.status = SimulationStatus.failed
288291
update_simulation(session, a)
289292

290293
# try:
Lines changed: 8 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,14 @@
1-
from typing import List
2-
3-
from fastapi import Depends, FastAPI
4-
from fastapi.responses import PlainTextResponse
1+
from fastapi import FastAPI
52
from fastapi_pagination import add_pagination
6-
from fastapi_pagination.cursor import CursorPage
7-
from sqlmodel import Session
83

9-
from .crud import (
10-
get_crystal_structure,
11-
get_crystal_structures,
12-
get_fdmnes_jobfile,
13-
get_fdmnes_output,
14-
get_fdmnes_simulation,
15-
get_fdmnes_xas,
16-
get_molecular_structure,
17-
get_molecular_structures,
18-
get_orca_jobfile,
19-
get_orca_output,
20-
get_orca_simulation,
21-
get_orca_xas,
22-
get_simulation,
23-
get_simulations_page,
24-
submit_fdmnes_simulation,
25-
submit_orca_simulation,
26-
upload_crystal_structure,
27-
upload_molecular_structure,
28-
)
29-
from .database import get_session
30-
from .models.models import (
31-
CrystalStructure,
32-
CrystalStructureInput,
33-
FdmnesSimulation,
34-
FdmnesSimulationInput,
35-
FdmnesSimulationResponse,
36-
MolecularStructure,
37-
MolecularStructureInput,
38-
OrcaSimulation,
39-
OrcaSimulationInput,
40-
OrcaSimulationResponse,
41-
SimulationResponse,
42-
)
4+
from .routers import crystals, fdmnes, molecules, orca, simulations
435

446
app = FastAPI()
457

46-
add_pagination(app)
47-
48-
49-
@app.get("/api/simulations")
50-
def get_simulations_pagination_endpoint(
51-
session: Session = Depends(get_session),
52-
) -> CursorPage[SimulationResponse]:
53-
return get_simulations_page(session)
54-
55-
56-
@app.get("/api/simulations/{id}")
57-
def get_simulation_endpoint(
58-
id: int, session: Session = Depends(get_session)
59-
) -> SimulationResponse:
60-
return get_simulation(session, id)
61-
62-
63-
@app.post("/api/submit/orca")
64-
def submit_orca(
65-
orca_input: OrcaSimulationInput,
66-
session: Session = Depends(get_session),
67-
) -> OrcaSimulation:
68-
return submit_orca_simulation(orca_input, session)
69-
70-
71-
@app.get("/api/orca/{id}")
72-
def get_orca_simulation_endpoint(
73-
id: int, session: Session = Depends(get_session)
74-
) -> OrcaSimulationResponse:
75-
return get_orca_simulation(session, id)
76-
77-
78-
# TODO actually implement
79-
@app.get("/api/orca/{id}/output")
80-
def get_orca_output_endpoint(id: int, session: Session = Depends(get_session)) -> str:
81-
return PlainTextResponse(get_orca_output(session, id))
82-
83-
84-
# TODO replace with an actual implementation
85-
@app.get("/api/orca/{id}/jobfile")
86-
def get_orca_jobfile_endpoint(id: int, session: Session = Depends(get_session)) -> str:
87-
return PlainTextResponse(get_orca_jobfile(session, id))
88-
89-
90-
# TODO further orca endpoint
91-
# mapspc
92-
# @app.get("/api/orca/{id}/spectra")
93-
# @app.get("/api/orca/{id}/spectra/{spectrum_id}")
94-
# request new mapspc call
95-
# @app.post("/api/orca/{id}/spectra/")
96-
# orbital cube files
97-
# @app.get("/api/orca/{id}/orbitals")
98-
# @app.get("/api/orca/{id}/orbitals/{orbital_calculation_id}")
99-
# request new mapspc call
100-
# @app.post("/api/orca/{id}/orbitals/")
101-
102-
103-
@app.get("/api/molecules/{id}")
104-
def get_molecular_endpoint(
105-
id: int, session: Session = Depends(get_session)
106-
) -> MolecularStructure:
107-
return get_molecular_structure(session, id)
108-
109-
110-
@app.post("/api/molecules")
111-
def upload_molecular_endpoint(
112-
structure: MolecularStructureInput, session: Session = Depends(get_session)
113-
) -> MolecularStructure:
114-
return upload_molecular_structure(structure, session)
8+
app.include_router(orca.router)
9+
app.include_router(fdmnes.router)
10+
app.include_router(molecules.router)
11+
app.include_router(crystals.router)
12+
app.include_router(simulations.router)
11513

116-
117-
@app.get("/api/molecules")
118-
def get_molecular_list_endpoint(
119-
session: Session = Depends(get_session),
120-
) -> List[MolecularStructure]:
121-
return get_molecular_structures(session)
122-
123-
124-
@app.get("/api/crystals/{id}")
125-
def get_crystal_endpoint(
126-
id: int, session: Session = Depends(get_session)
127-
) -> CrystalStructure:
128-
return get_crystal_structure(session, id)
129-
130-
131-
@app.post("/api/crystals")
132-
def upload_crystal_endpoint(
133-
structure: CrystalStructureInput, session: Session = Depends(get_session)
134-
) -> CrystalStructure:
135-
return upload_crystal_structure(structure, session)
136-
137-
138-
@app.get("/api/crystals")
139-
def get_crystal_list_endpoint(
140-
session: Session = Depends(get_session),
141-
) -> List[CrystalStructure]:
142-
return get_crystal_structures(session)
143-
144-
145-
@app.post("/api/submit/fdmnes")
146-
def submit_fdmnes(
147-
fdmnes_input: FdmnesSimulationInput,
148-
session: Session = Depends(get_session),
149-
) -> FdmnesSimulation:
150-
return submit_fdmnes_simulation(fdmnes_input, session)
151-
152-
153-
@app.get("/api/fdmnes/{id}")
154-
def get_fdmnes_simulation_endpoint(
155-
id: int, session: Session = Depends(get_session)
156-
) -> FdmnesSimulationResponse:
157-
return get_fdmnes_simulation(session, id)
158-
159-
160-
@app.get("/api/fdmnes/{id}/jobfile")
161-
def get_fdmnes_jobfile_endpoint(
162-
id: int, session: Session = Depends(get_session)
163-
) -> str:
164-
return PlainTextResponse(get_fdmnes_jobfile(session, id))
165-
166-
167-
@app.get("/api/fdmnes/{id}/output")
168-
def get_fdmnes_output_endpoint(id: int, session: Session = Depends(get_session)) -> str:
169-
return PlainTextResponse(get_fdmnes_output(session, id))
170-
171-
172-
@app.get("/api/fdmnes/{id}/xas")
173-
def get_fdmnes_xas_endpoint(id: int, session: Session = Depends(get_session)):
174-
return get_fdmnes_xas(session, id)
175-
176-
177-
@app.get("/api/orca/{id}/xas")
178-
def get_orca_xas_endpoint(id: int, session: Session = Depends(get_session)):
179-
return get_orca_xas(session, id)
14+
add_pagination(app)
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import re
2+
3+
from .models.models import (
4+
CrystalStructure,
5+
FdmnesSimulation,
6+
MolecularStructure,
7+
OrcaCalculation,
8+
OrcaSimulation,
9+
StructureType,
10+
)
11+
from .periodictable import periodic_table
12+
13+
14+
def build_fdmnes_inputfile(
15+
fdmnes_simulation: FdmnesSimulation, structure: CrystalStructure
16+
) -> str:
17+
jobfile = "Filout\nresult\n\nRange\n"
18+
jobfile += "-10. 0.25 50 !E_min, step, E_intermediate, step ...\n\n"
19+
20+
jobfile += "Edge\n"
21+
jobfile += str(fdmnes_simulation.edge.value).capitalize() + "\n\n"
22+
23+
jobfile += "Z_absorber\n"
24+
jobfile += str(fdmnes_simulation.element) + "\n\n"
25+
26+
jobfile += "SCF !Performs self-consistent calculation\n"
27+
jobfile += (
28+
"Energpho !Output energy relative to the photon energy of absorbing atom\n\n"
29+
)
30+
31+
if fdmnes_simulation.greens_approach:
32+
jobfile += "Green\n"
33+
34+
jobfile += "Quadrupole\n"
35+
36+
if fdmnes_simulation.edge.value.startswith("l"):
37+
jobfile += "Spinorbit\n"
38+
39+
jobfile += "\n"
40+
jobfile += "Radius"
41+
jobfile += " ! Radius of the cluster where final state calculation is performed\n"
42+
jobfile += "6" + "\n\n"
43+
44+
if fdmnes_simulation.structure_type == StructureType.crystal:
45+
jobfile += "Crystal ! Periodic material description (unit cell)\n"
46+
else:
47+
jobfile += "Molecule ! Periodic or cylindrical or spherical coordinates\n"
48+
49+
jobfile += f"{structure.a} {structure.b} {structure.c}"
50+
jobfile += f"{structure.alpha} {structure.beta} {structure.gamma}\n"
51+
52+
keys = (re.escape(k) for k in periodic_table.keys())
53+
pattern = re.compile(r"\b(" + "|".join(keys) + r")\b")
54+
55+
result = pattern.sub(lambda x: str(periodic_table[x.group()]), structure.structure)
56+
jobfile += result
57+
58+
jobfile += "\n\nConvolution\n\nEnd"
59+
60+
return jobfile
61+
62+
63+
def build_orca_input_file(
64+
orca_simulation: OrcaSimulation, structure: MolecularStructure
65+
):
66+
calc_type = orca_simulation.calculation_type
67+
68+
if calc_type == OrcaCalculation.xas.value:
69+
prefix = "! "
70+
else:
71+
prefix = "! UKS "
72+
73+
jobfile = (
74+
prefix
75+
+ orca_simulation.functional
76+
+ " DKH2 "
77+
+ orca_simulation.basis_set
78+
+ " SARC/J"
79+
)
80+
81+
if orca_simulation.solvent is not None:
82+
jobfile += "CPCM(" + orca_simulation.solvent + ") "
83+
84+
jobfile += "\n"
85+
86+
jobfile += "%maxcore " + str(orca_simulation.memory_per_core) + "\n\n"
87+
jobfile += "%pal nprocs " + str(orca_simulation.simulation.n_cores) + "\n"
88+
jobfile += "end" + "\n\n"
89+
90+
if calc_type == OrcaCalculation.xas.value:
91+
jobfile += "%tddft" + "\n"
92+
93+
jobfile += (
94+
"orbWin[0] = "
95+
+ str(orca_simulation.orb_win_0_start)
96+
+ ","
97+
+ str(orca_simulation.orb_win_0_stop)
98+
+ ",-1,-1\n"
99+
)
100+
jobfile += (
101+
"orbWin[1] = "
102+
+ str(orca_simulation.orb_win_1_start)
103+
+ ","
104+
+ str(orca_simulation.orb_win_1_stop)
105+
+ ",-1,-1\n"
106+
)
107+
108+
jobfile += "doquad true" + "\n"
109+
jobfile += "nroots 20" + "\n"
110+
jobfile += "maxdim 10" + "\n"
111+
jobfile += "end" + "\n\n"
112+
else:
113+
jobfile += "%xes" + "\n"
114+
jobfile += "CoreOrb 0,1" + "\n"
115+
jobfile += "OrbOp 0,1" + "\n"
116+
jobfile += "DoSOC true" + "\n"
117+
jobfile += "Normalize true" + "\n"
118+
jobfile += "MDOriginAdjustMethod 1" + "\n"
119+
jobfile += "end" + "\n\n"
120+
121+
jobfile += (
122+
"*xyz "
123+
+ str(orca_simulation.charge)
124+
+ " "
125+
+ str(orca_simulation.multiplicity)
126+
+ "\n"
127+
)
128+
129+
jobfile += structure.structure
130+
jobfile += "\nend"
131+
132+
return jobfile

web-conexs-api/src/web_conexs_api/models/models.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ class OrcaSimulationInput(SQLModel):
136136

137137
class OrcaSimulation(OrcaSimulationInput, table=True):
138138
__tablename__: str = "orca_simulation"
139-
simulation_type_id: int = 1
140139
simulation_id: int = Field(
141140
foreign_key="simulation.id",
142141
default=None,

web-conexs-api/src/web_conexs_api/routers/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)