def gamess_quantum_pipeline(request, molinfo): """ Assumed that rdkit understands the molecule """ # TODO Read gamess settings from ini # Read input molobj = molinfo["molobj"] sdfstr = molinfo["sdfstr"] if "name " in request.POST: name = request.POST["name"].encode('utf-8') else: name = None # Get that smile on your face smiles = cheminfo.molobj_to_smiles(molobj, remove_hs=True) # hash on sdf (conformer) hshobj = hashlib.md5(sdfstr.encode()) hashkey = hshobj.hexdigest() # Start respond message msg = {"smiles": smiles, "hashkey": hashkey} # Check if calculation already exists if False: calculation = request.dbsession.query(models.GamessCalculation) \ .filter_by(hashkey=hashkey).first() if calculation is not None: calculation.created = datetime.datetime.now() return msg # Create new calculation calculation = models.GamessCalculation() # check if folder exists here = os.path.abspath(os.path.dirname(__file__)) + "/" datahere = here + "data/" if not os.path.isdir(datahere + hashkey): os.mkdir(datahere + hashkey) os.chdir(datahere + hashkey) # GAMESS DEBUG # TODO Add error messages when gamess fails # TODO add timeouts for all gamess calls # Optimize molecule gmsargs = { "scr": datahere + hashkey, "autoclean": True, "debug": False, } properties = gamess.calculate_optimize(molobj, **gmsargs) if properties is None: return { 'error': 'Error g-80 - gamess optimization error', 'message': "Error. Server was unable to optimize molecule" } print(smiles, list(properties.keys())) # Save and set coordinates coord = properties["coord"] calculation.coordinates = save_array(coord) calculation.enthalpy = properties["h"] cheminfo.molobj_set_coordinates(molobj, coord) # Optimization is finished, do other calculation async-like # Vibrate molecule vibheader = """ $basis gbasis=PM3 $end $contrl scftyp=RHF runtyp=hessian icharg={:} maxit=60 $end """ orbheader = """ $contrl coord=cart units=angs scftyp=rhf icharg={:} maxit=60 $end $basis gbasis=sto ngauss=3 $end """ solheader = """ $system mwords=125 $end $basis gbasis=PM3 $end $contrl scftyp=RHF runtyp=energy icharg={:} $end $pcm solvnt=water mxts=15000 icav=1 idisp=1 $end $tescav mthall=4 ntsall=60 $end """ headers = [vibheader, orbheader, solheader] readers = [ gamess.read_properties_vibration, gamess.read_properties_orbitals, gamess.read_properties_solvation ] def procfunc(conn, reader, *args, **kwargs): stdout, status = gamess.calculate(*args, **kwargs) try: properties = reader(stdout) except: # TODO Error reading properties properties = None conn.send(properties) conn.close() procs = [] conns = [] for header, reader in zip(headers, readers): parent_conn, child_conn = Pipe() p = Process(target=procfunc, args=(child_conn, reader, molobj, header), kwargs=gmsargs) p.start() procs.append(p) conns.append(parent_conn) for proc in procs: proc.join() properties_vib = conns[0].recv() properties_orb = conns[1].recv() properties_sol = conns[2].recv() if properties_vib is None: return { 'error': 'Error g-104 - gamess vibration error', 'message': "Error. Server was unable to vibrate molecule" } print(smiles, list(properties_vib.keys())) calculation.islinear = properties_vib["linear"] calculation.vibjsmol = properties_vib["jsmol"] calculation.vibfreq = save_array(properties_vib["freq"]) calculation.vibintens = save_array(properties_vib["intens"]) calculation.thermo = save_array(properties_vib["thermo"]) if properties_orb is None: return { 'error': 'Error g-128 - gamess orbital error', 'message': "Error. Server was unable to orbital the molecule" } print(smiles, list(properties_orb.keys())) calculation.orbitals = save_array(properties_orb["orbitals"]) calculation.orbitalstxt = properties_orb["stdout"] if properties_sol is None: return { 'error': 'Error g-159 - gamess solvation error', 'message': "Error. Server was unable to run solvation calculation" } # 'charges', 'solvation_total', 'solvation_polar', 'solvation_nonpolar', # 'surface', 'total_charge', 'dipole', 'dipole_total' print(smiles, list(properties_sol.keys())) charges = properties_sol["charges"] calculation.charges = save_array(charges) calculation.soltotal = properties_sol["solvation_total"] calculation.solpolar = properties_sol["solvation_polar"] calculation.solnonpolar = properties_sol["solvation_nonpolar"] calculation.solsurface = properties_sol["surface"] calculation.soldipole = save_array(properties_sol["dipole"]) calculation.soldipoletotal = properties_sol["dipole_total"] # GAMESS DEBUG os.chdir(here) # Saveable sdf and reset title sdfstr = cheminfo.molobj_to_sdfstr(molobj) sdfstr = str(sdfstr) for _ in range(2): i = sdfstr.index('\n') sdfstr = sdfstr[i + 1:] sdfstr = "\n\n" + sdfstr # Save mol2 fmt mol2 = cheminfo.molobj_to_mol2(molobj, charges=charges) calculation.mol2 = mol2 # Get a 2D Picture # TODO Compute 2D coordinates svgstr = cheminfo.molobj_to_svgstr(molobj, removeHs=True) # Success, setup database # calculation = models.GamessCalculation() calculation.smiles = smiles calculation.hashkey = hashkey calculation.sdf = sdfstr calculation.svg = svgstr calculation.created = datetime.datetime.now() # Add calculation to the database request.dbsession.add(calculation) # Add smiles to counter countobj = request.dbsession.query(models.Counter) \ .filter_by(smiles=smiles).first() if countobj is None: counter = models.Counter() counter.smiles = smiles counter.count = 1 request.dbsession.add(counter) else: countobj.count += 1 return msg
def ajax_submitquantum(request): """ Setup quantum calculation """ if not request.POST: return { 'error': 'Error 128 - empty post', 'message': "Error. Empty post." } if not request.POST["sdf"]: return { 'error': 'Error 132 - sdf key error', 'message': "Error. Missing information." } # Get coordinates from request sdfstr = request.POST["sdf"].encode('utf-8') # Get rdkit molobj, status = cheminfo.sdfstr_to_molobj(sdfstr) if molobj is None: status = status.split("]") status = status[-1] return {'error': 'Error 141 - rdkit error', 'message': status} try: conf = molobj.GetConformer() except ValueError: # Error return { 'error': 'Error 141 - rdkit error', 'message': "Error. Server was unable to generate conformations for this molecule" } # If hydrogens not added, assume graph and optimize with forcefield atoms = cheminfo.molobj_to_atoms(molobj) if 1 not in atoms: molobj = cheminfo.molobj_add_hydrogens(molobj) cheminfo.molobj_optimize(molobj) # TODO Check lengths of atoms # TODO Define max in settings # Fix sdfstr sdfstr = sdfstr.decode('utf8') for _ in range(3): i = sdfstr.index('\n') sdfstr = sdfstr[i + 1:] sdfstr = "\n" * 3 + sdfstr # hash on sdf (conformer) hshobj = hashlib.md5(sdfstr.encode()) hashkey = hshobj.hexdigest() calculation = request.dbsession.query(models.GamessCalculation) \ .filter_by(hashkey=hashkey).first() if calculation is not None: msg = {'hashkey': hashkey} calculation.created = datetime.datetime.now() return msg print("new:", hashkey) molecule_info = {"sdfstr": sdfstr, "molobj": molobj, "hashkey": hashkey} msg = pipelines.gamess_quantum_pipeline(request, molecule_info) return msg # # # calculation = request.dbsession.query(models.GamessCalculation) \ .filter_by(hashkey=hashkey).first() if calculation is not None: calculation.created = datetime.datetime.now() return msg else: pass # check if folder exists here = os.path.abspath(os.path.dirname(__file__)) + "/" datahere = here + "data/" if os.path.isdir(datahere + hashkey): # return msg pass else: os.mkdir(datahere + hashkey) os.chdir(datahere + hashkey) # Minimize with forcefield first molobj = cheminfo.molobj_add_hydrogens(molobj) cheminfo.molobj_optimize(molobj) header = """ $basis gbasis=pm3 $end $contrl runtyp=optimize icharg=0 $end $statpt opttol=0.0005 nstep=200 projct=.F. $end """ # Prepare gamess input # inpstr = gamess.molobj_to_gmsinp(molobj, header) # Save and run file # with open("optimize.inp", "w") as f: # f.write(inpstr) # # stdout, stderr = gamess.calculate(hashkey+".inp", store_output=False) # with open("start.sdf", 'w') as f: # f.write(cheminfo.molobj_to_sdfstr(molobj)) # Check output # status, message = gamess.check_output(stdout) os.chdir(here) # if not status: # msg["error"] = "error 192: QM Calculation fail" # msg["message"] = message # return msg # Saveable sdf and reset title sdfstr = cheminfo.molobj_to_sdfstr(molobj) sdfstr = str(sdfstr) for _ in range(2): i = sdfstr.index('\n') sdfstr = sdfstr[i + 1:] sdfstr = "\n\n" + sdfstr # Get a 2D Picture # TODO Compute 2D coordinates svgstr = cheminfo.molobj_to_svgstr(molobj, removeHs=True) # Success, setup database calculation = models.GamessCalculation() calculation.smiles = smiles calculation.hashkey = hashkey calculation.sdf = sdfstr calculation.svg = svgstr calculation.created = datetime.datetime.now() # Add calculation to the database request.dbsession.add(calculation) # Add smiles to counter countobj = request.dbsession.query(models.Counter) \ .filter_by(smiles=smiles).first() if countobj is None: counter = models.Counter() counter.smiles = smiles counter.count = 1 request.dbsession.add(counter) print(counter) else: countobj.count += 1 return msg
def calculation_pipeline(molinfo, settings): """ Assumed that rdkit understands the molecule args: molinfo - dict settings - """ # Read input molobj = molinfo["molobj"] sdfstr = molinfo["sdfstr"] hashkey = molinfo["hashkey"] scratch_dir = settings["scr.scr"] scratch_dir = pathlib.Path(scratch_dir) # TODO Get molecule names # Get that smile on your face try: smiles = chembridge.molobj_to_smiles(molobj, remove_hs=True) except Exception: smiles = chembridge.molobj_to_smiles(molobj) # Start respond message msg = {"smiles": smiles, "hashkey": hashkey} atoms = chembridge.molobj_to_atoms(molobj) _logger.info(f"{hashkey} '{smiles}' {atoms}") # Create new calculation calculation = models.GamessCalculation() # Switch to scrdir / hashkey hashdir = scratch_dir / hashkey hashdir.mkdir(parents=True, exist_ok=True) gamess_options = { "cmd": settings["gamess.rungms"], "gamess_scr": settings["gamess.scr"], "gamess_userscr": settings["gamess.userscr"], "scr": hashdir, "filename": hashkey, } # TODO Add error messages when gamess fails # TODO add timeouts for all gamess calls # Optimize molecule try: properties = gamess_calculations.optimize_coordinates( molobj, gamess_options) except Exception: # TODO Logger + rich should store these exceptions somewhere. One file # per exception for easy debugging. # TODO Should store SDF of the molecule if exception sdfstr = chembridge.molobj_to_sdfstr(molobj) _logger.error(f"{hashkey} OptimizationError", exc_info=True) _logger.error(sdfstr) properties = None if properties is None: return { "error": "Error g-80 - gamess optimization error", "message": "Error. Unable to optimize molecule", }, None if "error" in properties: return { "error": "Error g-93 - gamess optimization error known", "message": properties["error"], }, None if (COLUMN_COORDINATES not in properties or properties[COLUMN_COORDINATES] is None): return { "error": "Error g-104 - gamess optimization error", "message": "Error. Unable to optimize molecule", }, None _logger.info(f"{hashkey} OptimizationSucces") # Save and set coordinates coord = properties[ppqm.constants.COLUMN_COORDINATES] calculation.coordinates = misc.save_array(coord) calculation.enthalpy = properties[ppqm.constants.COLUMN_ENERGY] chembridge.molobj_set_coordinates(molobj, coord) # Optimization is finished, do other calculation async-like ( properties_vib, properties_orb, properties_sol, ) = gamess_calculations.calculate_all_properties(molobj, gamess_options) # Check results if properties_vib is None or "error" in properties_vib: return { "error": "Error g-104 - gamess vibration error", "message": "Error. Unable to vibrate molecule", }, None _logger.info(f"{hashkey} VibrationSuccess") # TODO Make a custom reader and move this out of ppqm calculation.islinear = properties_vib["linear"] calculation.vibjsmol = properties_vib["jsmol"] calculation.vibfreq = misc.save_array(properties_vib["freq"]) calculation.vibintens = misc.save_array(properties_vib["intens"]) calculation.thermo = misc.save_array(properties_vib["thermo"]) if properties_orb is None or "error" in properties_orb: return { "error": "Error g-128 - gamess orbital error", "message": "Error. Unable to calculate molecular orbitals", }, None _logger.info(f"{hashkey} OrbitalsSuccess") calculation.orbitals = misc.save_array(properties_orb["orbitals"]) calculation.orbitalstxt = properties_orb["stdout"] if properties_sol is None or "error" in properties_sol: # Is okay solvation didn't converge, just warn. _logger.warning(f"{hashkey} SolvationError") else: # 'charges', 'solvation_total', 'solvation_polar', # 'solvation_nonpolar', 'surface', 'total_charge', 'dipole', # 'dipole_total' _logger.info(f"{hashkey} SolvationSuccess") charges = properties_sol["charges"] calculation.charges = misc.save_array(charges) calculation.soltotal = properties_sol["solvation_total"] calculation.solpolar = properties_sol["solvation_polar"] calculation.solnonpolar = properties_sol["solvation_nonpolar"] calculation.solsurface = properties_sol["surface"] calculation.soldipole = misc.save_array(properties_sol["dipole"]) calculation.soldipoletotal = properties_sol["dipole_total"] # Save mol2 fmt mol2 = chembridge.molobj_to_mol2(molobj, charges=charges) calculation.mol2 = mol2 # Saveable sdf and reset title sdfstr = chembridge.molobj_to_sdfstr(molobj) sdfstr = chembridge.clean_sdf_header(sdfstr) # Get a 2D Picture svgstr = chembridge.molobj_to_svgstr(molobj, removeHs=True, use_2d=True) # Success, store results database calculation.smiles = smiles calculation.hashkey = hashkey calculation.sdf = sdfstr calculation.svg = svgstr calculation.created = datetime.datetime.now() return msg, calculation