Generating COSMO-RS Compounds from XYZ Files and SMILES
*******************************************************

COSMO-RS ADF Conformers

The "ADFCOSMORSCompoundJob" class generates results equivalent to the
GUI task "COSMO-RS Compound" and provides a convenient Python
interface for creating ".coskf" files for one or many structures.

**Requires:** AMS2026 or later

-[ Related examples ]-

* ADF and COSMO-RS Workflow

* COSMO-RS Conformer Workflows for Acetic Acid

* Property Prediction from SMILES with pyCRS

* Conformer Generation, Rescoring, and Filtering

* Conformer Generation for Multiple Molecules

-[ Related tutorials ]-

* PLAMS with COSMO-RS tutorial

* COSMO-RS compound database tutorial

-[ Related documentation ]-

* COSMO-RS compounds

* ADF COSMO calculation for COSMO-RS


Generating coskf files from xyz
===============================

The example will first load all the molecules in the folder
"compounds_xyz" and then optimize the gas geometry using ADF, and
perform the ADF COSMO calculation for each compound. When the
calculations are finished, we will find all the .coskf files in the
"test_coskfs_xyz" directory.

   from scm.plams import from_smiles, read_molecules, JobRunner, config
   from scm.plams.recipes.adfcosmorscompound import ADFCOSMORSCompoundJob
   import os
   from pathlib import Path

Enable the parallel calculation through "JobRunner". Here, we’ll
assign one core to each job, and we can have up to eight jobs running
all at once.

   config.default_jobrunner = JobRunner(
       parallel=True, maxjobs=8
   )  # Set the default jobrunner to be parallel
   config.default_jobmanager.settings.hashing = None  # Disable rerun prevention
   config.job.runscript.nproc = 1  # Number of cores for each job
   config.log.stdout = 1  # Suppress plams output

Optional compound metadata keyed by molecule name. This metadata is
passed to ADFCOSMORSCompoundJob via "mol_info", written to the
“Compound Data” section of the generated COSKF file, and used by
AMSCrs GUI and "pyCRS.Database" to search compounds.

   mol_info_by_name = {
       "CO": {"CAS": "630-08-0", "IUPAC": "Carbon monoxide", "Other Name": ""},
       "H2O": {"CAS": "7732-18-5", "IUPAC": "Water", "Other Name": ""},
   }

   compounds_dir = Path("./compounds_xyz")
   compounds_dir.mkdir(exist_ok=True)

   co_xyz = compounds_dir / "CO.xyz"
   h2o_xyz = compounds_dir / "H2O.xyz"

   co_xyz.write_text("""\
   6

   C      -0.36450646      -0.00849588       0.01778348
   O       0.93310187      -0.11594431      -0.58640848
   H      -1.09583501      -0.23127873      -0.76871971
   H      -0.56296738       1.00752571       0.39904705
   H      -0.50297820      -0.73401384       0.83725517
   H       1.59318518       0.08220705       0.10104249
   """)
   h2o_xyz.write_text("""\
   3

   O       0.00615254       0.40173439       0.00000000
   H      -0.76825199      -0.18914855       0.00000000
   H       0.76209945      -0.21258584       0.00000000
   """);

   molecules = read_molecules("./compounds_xyz")

   results = []
   for name, mol in molecules.items():
       mol_info = mol_info_by_name.get(name)
       job = ADFCOSMORSCompoundJob(
           molecule=mol, coskf_name=name, coskf_dir="test_coskfs_xyz", mol_info=mol_info
       )
       results.append(job.run())

   [09.04|16:23:22] JOB plamsjob STARTED
   [09.04|16:23:22] JOB plamsjob STARTED
   [09.04|16:23:22] JOB plamsjob/gas STARTED
   [09.04|16:23:22] JOB plamsjob/solv STARTED

   for result in results:
       result.wait()

   [09.04|16:23:22] JOB plamsjob/sigma STARTED
   [09.04|16:23:22] Waiting for job plamsjob to finish
   [09.04|16:23:22] JOB plamsjob.002/gas STARTED
   [09.04|16:23:22] JOB plamsjob.002/solv STARTED
   [09.04|16:23:22] Waiting for job gas to finish
   [09.04|16:23:22] JOB plamsjob.002/sigma STARTED
   [09.04|16:23:22] Waiting for job solv to finish
   [09.04|16:23:22] Waiting for job gas to finish
   [09.04|16:23:22] Waiting for job solv to finish
   [09.04|16:23:26] JOB plamsjob.002/gas SUCCESSFUL
   [09.04|16:23:29] JOB plamsjob.002/solv SUCCESSFUL
   [09.04|16:23:29] JOB plamsjob.002/sigma SUCCESSFUL
   [09.04|16:23:32] JOB plamsjob.002 SUCCESSFUL
   [09.04|16:23:36] JOB plamsjob/gas SUCCESSFUL
   [09.04|16:23:48] JOB plamsjob/solv SUCCESSFUL
   [09.04|16:23:48] JOB plamsjob/sigma SUCCESSFUL
   [09.04|16:23:52] JOB plamsjob SUCCESSFUL

   print(f"coskf files generated: {', '.join([f for f in os.listdir('./test_coskfs_xyz')])}")

   coskf files generated: CO.coskf, H2O.coskf


Generating .coskf files from smiles
===================================

Now, we will specify the smiles and name of a set of compounds and
generate the initial geometry of each compound using "from_smiles"
function. With the setting, "nconfs=100" and "forcefield='uff'", we
will generate 100 conformers and find the one with the lowest energy
using ‘uff’ forcefield. When the calculations are finished, we will
find all the .coskf file in the "test_coskfs_smiles" directory.

   rd_smiles = ["O", "CO"]
   rd_names = ["H2O", "CO"]
   molecules = {}
   for name, smiles in zip(rd_names, rd_smiles):
       molecules[name] = from_smiles(smiles, nconfs=100, forcefield="uff")[
           0
       ]  # lowest energy one in 100 conformers

Lastly, we give this information to the "ADFCOSMORSCompoundJob" class,
including the name of the coskf files as well as the directory in
which we’ll find them after the calculations complete. Using the
setting, "preoptimization='GFN1-xTB'" and "singlepoint=False", it will
utilize the DFTB for a quick pre-optimization. Subsequently, it will
execute a gas phase optimization using ADF, followed by the solvation
calculation.

   results = []
   for name, mol in molecules.items():
       mol_info = mol_info_by_name.get(name)
       job = ADFCOSMORSCompoundJob(
           molecule=mol,  # The initial structure
           coskf_name=name,  # a name to be used for coskf file
           coskf_dir="test_coskfs_smiles",  # a directory to put the .coskf files generated
           preoptimization="GFN1-xTB",  # perform preoptimize or not
           singlepoint=False,  # run a singlepoint in gasphase and solvation calculation without geometry optimization. Cannot be combined with `preoptimization`
           name=name,  # an optional name for the calculation directory
           mol_info=mol_info,  # compound information to be stored in the Compound Data section of the COSKF file
       )
       results.append(job.run())

   [09.04|16:23:52] JOB H2O STARTED
   [09.04|16:23:52] JOB CO STARTED
   [09.04|16:23:52] JOB H2O/preoptimization STARTED
   [09.04|16:23:52] JOB H2O/gas STARTED

   for result in results:
       result.wait()

   [09.04|16:23:52] Waiting for job H2O to finish
   [09.04|16:23:52] JOB H2O/solv STARTED
   [09.04|16:23:52] JOB H2O/sigma STARTED
   [09.04|16:23:52] JOB CO/preoptimization STARTED
   [09.04|16:23:52] JOB CO/gas STARTED
   [09.04|16:23:52] JOB CO/solv STARTED
   [09.04|16:23:52] JOB CO/sigma STARTED
   [09.04|16:23:52] Waiting for job gas to finish
   [09.04|16:23:52] Waiting for job preoptimization to finish
   [09.04|16:23:52] Waiting for job solv to finish
   ... output trimmed ....
   [09.04|16:23:52] JOB CO/preoptimization SUCCESSFUL
   [09.04|16:23:59] JOB H2O/gas SUCCESSFUL
   [09.04|16:24:04] JOB H2O/solv SUCCESSFUL
   [09.04|16:24:04] JOB H2O/sigma SUCCESSFUL
   [09.04|16:24:07] JOB H2O SUCCESSFUL
   [09.04|16:24:07] Waiting for job CO to finish
   [09.04|16:24:21] JOB CO/gas SUCCESSFUL
   [09.04|16:24:33] JOB CO/solv SUCCESSFUL
   [09.04|16:24:34] JOB CO/sigma SUCCESSFUL
   [09.04|16:24:37] JOB CO SUCCESSFUL

   print(f"coskf files generated: {', '.join([f for f in os.listdir('./test_coskfs_smiles')])}")

   coskf files generated: CO.coskf, H2O.coskf


See also
========


Related examples
----------------

* ADF and COSMO-RS Workflow

* COSMO-RS Conformer Workflows for Acetic Acid

* Property Prediction from SMILES with pyCRS

* Conformer Generation, Rescoring, and Filtering

* Conformer Generation for Multiple Molecules


Related tutorials
-----------------

* PLAMS with COSMO-RS tutorial

* COSMO-RS compound database tutorial


Related documentation
---------------------

* COSMO-RS compounds

* ADF COSMO calculation for COSMO-RS


Python Script
=============

   #!/usr/bin/env python
   # coding: utf-8

   # ## Generating coskf files from xyz

   # The example will first load all the molecules in the folder ``compounds_xyz`` and then optimize the gas geometry using ADF, and perform the ADF COSMO calculation for each compound. When the calculations are finished, we will find all the .coskf files in the ``test_coskfs_xyz`` directory.

   from scm.plams import from_smiles, read_molecules, JobRunner, config
   from scm.plams.recipes.adfcosmorscompound import ADFCOSMORSCompoundJob
   import os
   from pathlib import Path


   # Enable the parallel calculation through `JobRunner`. Here, we'll assign one core to each job, and we can have up to eight jobs running all at once.

   config.default_jobrunner = JobRunner(parallel=True, maxjobs=8)  # Set the default jobrunner to be parallel
   config.default_jobmanager.settings.hashing = None  # Disable rerun prevention
   config.job.runscript.nproc = 1  # Number of cores for each job
   config.log.stdout = 1  # Suppress plams output


   # Optional compound metadata keyed by molecule name. This metadata is passed to ADFCOSMORSCompoundJob via `mol_info`, written to the "Compound Data" section of the generated COSKF file, and used by AMSCrs GUI and `pyCRS.Database` to search compounds.

   mol_info_by_name = {
       "CO": {"CAS": "630-08-0", "IUPAC": "Carbon monoxide", "Other Name": ""},
       "H2O": {"CAS": "7732-18-5", "IUPAC": "Water", "Other Name": ""},
   }


   compounds_dir = Path("./compounds_xyz")
   compounds_dir.mkdir(exist_ok=True)

   co_xyz = compounds_dir / "CO.xyz"
   h2o_xyz = compounds_dir / "H2O.xyz"

   co_xyz.write_text("""\
   6

   C      -0.36450646      -0.00849588       0.01778348
   O       0.93310187      -0.11594431      -0.58640848
   H      -1.09583501      -0.23127873      -0.76871971
   H      -0.56296738       1.00752571       0.39904705
   H      -0.50297820      -0.73401384       0.83725517
   H       1.59318518       0.08220705       0.10104249
   """)
   h2o_xyz.write_text("""\
   3

   O       0.00615254       0.40173439       0.00000000
   H      -0.76825199      -0.18914855       0.00000000
   H       0.76209945      -0.21258584       0.00000000
   """)
   molecules = read_molecules("./compounds_xyz")

   results = []
   for name, mol in molecules.items():
       mol_info = mol_info_by_name.get(name)
       job = ADFCOSMORSCompoundJob(molecule=mol, coskf_name=name, coskf_dir="test_coskfs_xyz", mol_info=mol_info)
       results.append(job.run())


   for result in results:
       result.wait()


   print(f"coskf files generated: {', '.join([f for f in os.listdir('./test_coskfs_xyz')])}")


   # ## Generating .coskf files from smiles

   # Now, we will specify the smiles and name of a set of compounds and generate the initial geometry of each compound using `from_smiles` function. With the setting, `nconfs=100` and `forcefield='uff'`, we will generate 100 conformers and find the one with the lowest energy using 'uff' forcefield. When the calculations are finished, we will find all the .coskf file in the ``test_coskfs_smiles`` directory.

   rd_smiles = ["O", "CO"]
   rd_names = ["H2O", "CO"]
   molecules = {}
   for name, smiles in zip(rd_names, rd_smiles):
       molecules[name] = from_smiles(smiles, nconfs=100, forcefield="uff")[0]  # lowest energy one in 100 conformers


   # Lastly, we give this information to the `ADFCOSMORSCompoundJob` class, including the name of the coskf files as well as the directory in which we'll find them after the calculations complete.  Using the setting, `preoptimization='GFN1-xTB'` and `singlepoint=False`, it will utilize the DFTB for a quick pre-optimization. Subsequently, it will execute a gas phase optimization using ADF, followed by the solvation calculation.

   results = []
   for name, mol in molecules.items():
       mol_info = mol_info_by_name.get(name)
       job = ADFCOSMORSCompoundJob(
           molecule=mol,  # The initial structure
           coskf_name=name,  # a name to be used for coskf file
           coskf_dir="test_coskfs_smiles",  # a directory to put the .coskf files generated
           preoptimization="GFN1-xTB",  # perform preoptimize or not
           singlepoint=False,  # run a singlepoint in gasphase and solvation calculation without geometry optimization. Cannot be combined with `preoptimization`
           name=name,  # an optional name for the calculation directory
           mol_info=mol_info,  # compound information to be stored in the Compound Data section of the COSKF file
       )
       results.append(job.run())


   for result in results:
       result.wait()


   print(f"coskf files generated: {', '.join([f for f in os.listdir('./test_coskfs_smiles')])}")
