Skip to content

Commit 71ba4f2

Browse files
authored
add initial version of QE simulations (#16)
* add initial version of QE simulations
1 parent 2486cdb commit 71ba4f2

36 files changed

+1472
-31
lines changed

database/tables.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,17 @@ CREATE TABLE fdmnes_simulation (
146146

147147
COMMENT ON TABLE fdmnes_simulation IS 'Specific information for a FDMNES simulation';
148148

149+
CREATE TYPE qe_edge_enum AS ENUM('k', 'l1', 'l2', 'l23');
150+
151+
CREATE TYPE conductivity_enum AS ENUM('metallic', 'semiconductor', 'insulator');
152+
149153
CREATE TABLE qe_simulation (
150154
simulation_id INTEGER PRIMARY KEY,
151155
simulation_type_id INTEGER GENERATED ALWAYS AS (3) STORED,
156+
crystal_structure_id INTEGER NOT NULL,
157+
absorbing_atom INTEGER NOT NULL,
158+
edge qe_edge_enum NOT NULL,
159+
conductivity conductivity_enum NOT NULL,
152160
FOREIGN KEY(simulation_id, simulation_type_id) REFERENCES simulation (id,simulation_type_id)
153161
);
154162

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22

33
from sqlmodel import or_, select
44

5-
from web_conexs_api.jobfilebuilders import build_fdmnes_inputfile, build_orca_input_file
5+
from web_conexs_api.jobfilebuilders import (
6+
build_fdmnes_inputfile,
7+
build_orca_input_file,
8+
build_qe_inputfile,
9+
)
610
from web_conexs_api.models.models import (
711
CrystalStructure,
812
FdmnesSimulation,
913
MolecularStructure,
1014
OrcaSimulation,
15+
QESimulation,
1116
Simulation,
1217
SimulationStatus,
1318
)
@@ -91,3 +96,19 @@ def update_simulation(session, simulation: Simulation):
9196
session.commit()
9297
session.refresh(simulation)
9398
return simulation
99+
100+
101+
def get_qe_simulation(session, id) -> QESimulation:
102+
simulation = session.get(QESimulation, id)
103+
104+
if simulation:
105+
return simulation
106+
else:
107+
raise RuntimeError("FDMNES simulation not found")
108+
109+
110+
def get_qe_jobfile(session, id):
111+
qe_simulation = get_qe_simulation(session, id)
112+
structure = get_crystal_structure(session, qe_simulation.crystal_structure_id)
113+
114+
return build_qe_inputfile(qe_simulation, structure)

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

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import json
33
import logging
44
import os
5+
import shutil
56
import sys
67
import time
78
import uuid
@@ -12,12 +13,14 @@
1213
import requests
1314

1415
from web_conexs_api.database import get_session
16+
from web_conexs_api.jobfilebuilders import build_qe_xspectra_input
1517
from web_conexs_api.models.models import OrcaCalculation, Simulation, SimulationStatus
1618

1719
from .crud import (
1820
get_active_simulations,
1921
get_fdmnes_jobfile,
2022
get_orca_jobfile_with_technique,
23+
get_qe_jobfile,
2124
get_submitted_simulations,
2225
update_simulation,
2326
)
@@ -32,10 +35,14 @@
3235
SLURM_USER = os.environ.get("SLURM_USER")
3336
SLURM_API = os.environ.get("SLURM_API")
3437
SLURM_PARTITION = os.environ.get("SLURM_PARTITION")
38+
SLURM_RESPONSE_KEY = os.environ.get("SLURM_RESPONSE_KEY", "account")
3539

3640
ORCA_IMAGE = os.environ.get("ORCA_IMAGE")
3741
FDMNES_IMAGE = os.environ.get("FDMNES_IMAGE")
42+
QE_IMAGE = os.environ.get("QE_IMAGE")
3843
CONTAINER_IMAGE_DIR = os.environ.get("CONTAINER_IMAGE_DIR")
44+
PP_DIR = os.environ.get("PP_DIR")
45+
WFC_DIR = os.environ.get("WFC_DIR")
3946

4047
JOB_RUNNING = "RUNNING"
4148
JOB_COMPLETED = "COMPLETED"
@@ -225,6 +232,8 @@ def run_update():
225232
submit_orca(session, sim)
226233
if sim.simulation_type_id == 2:
227234
submit_fdmnes(session, sim)
235+
if sim.simulation_type_id == 3:
236+
submit_qe(session, sim)
228237

229238
# orca = get_orca_jobfile(session, sim.id)
230239
# determine workspace
@@ -256,7 +265,8 @@ def run_update():
256265
job_map = {}
257266

258267
for j in jobs:
259-
if j["account"] == SLURM_USER:
268+
# NEED TO CHANGE TO USER
269+
if j[SLURM_RESPONSE_KEY] == SLURM_USER:
260270
job_map[j["job_id"]] = {"state": j["job_state"][0]}
261271

262272
if len(active) != 0:
@@ -290,6 +300,60 @@ def run_update():
290300
update_simulation(session, a)
291301

292302

303+
def submit_qe(session, sim: Simulation):
304+
jobfile, absorbing_atom, abs_edge, pp, pp_abs = get_qe_jobfile(session, sim.id)
305+
application_name = "qe"
306+
user = sim.person.identifier
307+
uid = uuid.uuid4()
308+
309+
job_name = application_name + "-" + str(uid)
310+
working_dir = ROOT_DIR + user + "/" + job_name
311+
cluster_dir = CLUSTER_ROOT_DIR + user + "/" + job_name
312+
313+
Path(working_dir).mkdir(parents=True)
314+
315+
with open(working_dir / Path("job.inp"), "w+") as f:
316+
f.write(jobfile)
317+
318+
for pp_filename in pp:
319+
shutil.copy(os.path.join(PP_DIR, pp_filename), working_dir)
320+
321+
xspectra_input_file = working_dir / Path("xspectra_input.inp")
322+
323+
core_file = Path(pp_abs).with_suffix(".wfc")
324+
325+
wffile = WFC_DIR / core_file
326+
shutil.copy(wffile, working_dir)
327+
328+
xspectra_jobfile = build_qe_xspectra_input(absorbing_atom, abs_edge, str(core_file))
329+
330+
with open(xspectra_input_file, "w+") as ff:
331+
ff.write(xspectra_jobfile)
332+
333+
qe_sif = os.path.join(CONTAINER_IMAGE_DIR, QE_IMAGE)
334+
335+
script = (
336+
"#!/bin/bash\n"
337+
+ f"singularity exec {qe_sif} mpirun"
338+
+ f" -np {sim.n_cores} pw.x -in job.inp > result.pwo \n"
339+
+ f"singularity exec {qe_sif} mpirun"
340+
+ f" -np {sim.n_cores} xspectra.x -in xspectra_input.inp"
341+
)
342+
343+
try:
344+
job_id = build_job_and_run(
345+
script, user, working_dir, job_name, sim.n_cores, sim.memory, cluster_dir
346+
)
347+
sim.job_id = job_id
348+
sim.working_directory = working_dir
349+
sim.submission_date = datetime.datetime.now()
350+
sim.status = SimulationStatus.submitted
351+
update_simulation(session, sim)
352+
except Exception:
353+
sim.status = SimulationStatus.failed
354+
update_simulation(session, sim)
355+
356+
293357
def test_read():
294358
with contextmanager(get_session)() as session:
295359
sims = get_submitted_simulations(session)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from fastapi import FastAPI
22
from fastapi_pagination import add_pagination
33

4-
from .routers import crystals, fdmnes, molecules, orca, simulations, user
4+
from .routers import crystals, fdmnes, molecules, orca, qe, simulations, user
55

66
app = FastAPI()
77

88
app.include_router(orca.router)
99
app.include_router(fdmnes.router)
10+
app.include_router(qe.router)
1011
app.include_router(molecules.router)
1112
app.include_router(crystals.router)
1213
app.include_router(simulations.router)

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

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
from fastapi_pagination.ext.sqlalchemy import paginate
99
from sqlmodel import Session, and_, select
1010

11-
from .jobfilebuilders import build_fdmnes_inputfile, build_orca_input_file
11+
from .jobfilebuilders import (
12+
build_fdmnes_inputfile,
13+
build_orca_input_file,
14+
build_qe_inputfile,
15+
)
1216
from .models.models import (
1317
CrystalStructure,
1418
CrystalStructureInput,
@@ -20,6 +24,8 @@
2024
OrcaSimulationInput,
2125
Person,
2226
PersonInput,
27+
QESimulation,
28+
QESimulationInput,
2329
Simulation,
2430
)
2531

@@ -339,3 +345,82 @@ def get_user(session, user_id):
339345
person = Person(id=None, identifier=user_id, admin=False)
340346

341347
return person
348+
349+
350+
def submit_qe_simulation(
351+
qe_input: QESimulationInput, session: Session, user_id: str
352+
) -> QESimulation:
353+
person = get_or_create_person(session, user_id)
354+
355+
smodel = {
356+
"person_id": person.id,
357+
"simulation_type_id": 3,
358+
"request_date": datetime.datetime.now(),
359+
}
360+
361+
simulation = Simulation.model_validate(smodel)
362+
qe = QESimulation.model_validate(qe_input)
363+
qe.simulation = simulation
364+
365+
session.add(qe)
366+
session.commit()
367+
session.refresh(qe)
368+
369+
return qe
370+
371+
# get_qe_xas,
372+
373+
374+
def get_qe_simulation(session, id, user_id) -> QESimulation:
375+
statement = (
376+
select(QESimulation)
377+
.join(Simulation)
378+
.join(Person)
379+
.where(and_(Person.identifier == user_id, QESimulation.simulation_id == id))
380+
)
381+
382+
simulation = session.exec(statement).first()
383+
384+
if simulation:
385+
return simulation
386+
else:
387+
raise HTTPException(status_code=404, detail=f"No simulation with id={id}")
388+
389+
390+
def get_qe_jobfile(session, id, user_id):
391+
qe_simulation = get_qe_simulation(session, id, user_id)
392+
structure = get_crystal_structure(
393+
session, qe_simulation.crystal_structure_id, user_id
394+
)
395+
396+
return build_qe_inputfile(qe_simulation, structure)
397+
398+
399+
def get_qe_output(session, id, user_id):
400+
qe_simulation = get_qe_simulation(session, id, user_id)
401+
402+
wd = qe_simulation.simulation.working_directory
403+
404+
result_file = wd + "/result.pwo"
405+
406+
with open(result_file) as fh:
407+
file = fh.read()
408+
409+
return file
410+
411+
412+
def get_qe_xas(session, id, user_id):
413+
qe_simulation = get_qe_simulation(session, id, user_id)
414+
415+
wd = qe_simulation.simulation.working_directory
416+
417+
result_file = wd + "/xanes.dat"
418+
419+
if not os.path.isfile(result_file):
420+
raise HTTPException(status_code=404, detail="Item not found")
421+
422+
out = np.loadtxt(result_file)
423+
424+
output = {"energy": out[:, 0].tolist(), "xas": out[:, 1].tolist()}
425+
426+
return output

0 commit comments

Comments
 (0)