diff --git a/CAT/__version__.py b/CAT/__version__.py
index e4e49b3b..8969d496 100644
--- a/CAT/__version__.py
+++ b/CAT/__version__.py
@@ -1 +1 @@
-__version__ = '0.9.0'
+__version__ = '0.9.1'
diff --git a/CAT/base.py b/CAT/base.py
index 66920e21..fef47ff2 100644
--- a/CAT/base.py
+++ b/CAT/base.py
@@ -60,16 +60,17 @@
from nanoCAT.bde.bde_workflow import init_bde
from nanoCAT.ligand_solvation import init_solv
from nanoCAT.ff.ff_assignment import init_ff_assignment
+ from nanoCAT.cdft import init_cdft
NANO_CAT: Optional[ImportError] = None
except ImportError as ex:
- NANO_CAT: Optional[ImportError] = ex
+ NANO_CAT = ex
try:
import dataCAT
DATA_CAT: Optional[ImportError] = None
except ImportError as ex:
- DATA_CAT: Optional[ImportError] = ex
+ DATA_CAT = ex
__all__ = ['prep']
@@ -261,6 +262,7 @@ def prep_ligand(ligand_df: SettingsDataFrame) -> SettingsDataFrame:
* Ligand geometry optimization
* Ligand bulkiness calculations
* Ligand COSMO-RS calculations
+ * Ligand conceptual DFT calculations
.. _Nano-CAT: https://github.com/nlesc-nano/nano-CAT
@@ -284,6 +286,7 @@ def prep_ligand(ligand_df: SettingsDataFrame) -> SettingsDataFrame:
forcefield = ligand_df.settings.optional.forcefield
optimize = ligand_df.settings.optional.ligand.optimize
crs = ligand_df.settings.optional.ligand.crs
+ cdft = ligand_df.settings.optional.ligand.cdft
# Identify functional groups within the ligand.
ligand_df = init_ligand_anchoring(ligand_df)
@@ -310,6 +313,11 @@ def prep_ligand(ligand_df: SettingsDataFrame) -> SettingsDataFrame:
"(Multipurpose Atom-Typer for CHARMM) and the nano-CAT package")
init_ff_assignment(ligand_df)
+ # Run conceptual DFT calculations
+ if cdft:
+ val_nano_cat("Ligand conceptual DFT calculations require the nano-CAT package")
+ init_cdft(ligand_df)
+
return ligand_df
diff --git a/CAT/data_handling/validate_input.py b/CAT/data_handling/validate_input.py
index ccb9b9a8..cb1003fe 100644
--- a/CAT/data_handling/validate_input.py
+++ b/CAT/data_handling/validate_input.py
@@ -33,7 +33,8 @@
asa_schema,
ligand_opt_schema,
subset_schema,
- multi_ligand_schema
+ multi_ligand_schema,
+ cdft_schema
)
from .validate_ff import validate_ff, update_ff_jobs
@@ -111,12 +112,14 @@ def validate_input(s: Settings) -> None:
if s.optional.ligand.optimize:
s.optional.ligand.optimize = ligand_opt_schema.validate(s.optional.ligand.optimize)
+ if s.optional.ligand.cdft:
+ s.optional.ligand.cdft = cdft_schema.validate(s.optional.ligand.cdft)
if s.optional.ligand['cosmo-rs']:
crs = s.optional.ligand.pop('cosmo-rs')
s.optional.ligand.crs = crs_schema.validate(crs)
+
if s.optional.qd.optimize:
s.optional.qd.optimize = qd_opt_schema.validate(s.optional.qd.optimize)
-
if s.optional.qd.dissociate:
s.optional.qd.dissociate = bde_schema.validate(s.optional.qd.dissociate)
if s.optional.qd.activation_strain:
diff --git a/CAT/data_handling/validation_schemas.py b/CAT/data_handling/validation_schemas.py
index d58a25f2..c6b0325d 100644
--- a/CAT/data_handling/validation_schemas.py
+++ b/CAT/data_handling/validation_schemas.py
@@ -483,6 +483,13 @@ def _get_crsjob() -> type:
And(bool, Use(lambda n: {'job1': 'AMSJob'} if n else False)),
error='optional.ligand.cosmo-rs expects a boolean or dictionary'
),
+
+ Optional_('cdft', default=False): # Settings specific to ligand conceptual dft calculations
+ Or(
+ dict,
+ And(bool, Use(lambda n: {'job1': 'ADFJob'} if n else False)),
+ error='optional.ligand.cdft expects a boolean or dictionary'
+ ),
})
@@ -962,3 +969,35 @@ def _get_crsjob() -> type:
And(val_float, lambda n: 0 <= float(n) <= 1, Use(float))
)
})
+
+
+#: Schema for validating the ``['optional']['ligand']['cdft']`` block.
+cdft_schema: Schema = Schema({
+ # Delete files after the calculations are finished
+ Optional_('keep_files', default=True):
+ And(bool, error='optional.ligand.cdft.keep_files expects a boolean'),
+
+ # The Job type for the final geometry optimization
+ Optional_('job1', default=lambda n: ADFJob):
+ Or(
+ And(
+ And(type, lambda n: issubclass(n, Job), Use(val_job_type)),
+ error=('optional.ligand.cdft.job1 expects a type object '
+ 'that is a subclass of plams.Job')
+ ),
+ And(
+ str, Use(str_to_job_type),
+ error=('optional.ligand.cdft.job1 expects a string '
+ 'that is a valid plams.Job alias')
+ ),
+ ),
+
+ # The Job Settings for the final geometry optimization
+ Optional_('s2', default=Settings):
+ Or(
+ None,
+ dict,
+ And(str, Use(lambda n: get_template(n, from_cat_data=False))),
+ error='optional.ligand.cdft.s1 expects a string or a dictionary'
+ ),
+})
diff --git a/CAT/jobs.py b/CAT/jobs.py
index c682e87f..4f46a250 100644
--- a/CAT/jobs.py
+++ b/CAT/jobs.py
@@ -108,8 +108,11 @@ def pre_process_settings(mol: Molecule, s: Settings,
ret.input.ams.system.bondorders._1 = adf_connectivity(mol)
if 'uff' not in s.input:
ret.input.ams.system.charge = sum(
- [at.properties.charge for at in mol if 'charge' in at.properties]
+ [at.properties.get('charge', 0) for at in mol]
)
+ elif job_type is ADFJob:
+ if not s.input.charge:
+ s.input.charge = int(sum(at.properties.get('charge', 0) for at in mol))
return ret
diff --git a/CAT/recipes.py b/CAT/recipes.py
index 43db67ab..3c7fc966 100644
--- a/CAT/recipes.py
+++ b/CAT/recipes.py
@@ -18,8 +18,6 @@
"""
-import warnings
-
try:
from nanoCAT import recipes as _recipes
from nanoCAT.recipes import *
@@ -28,12 +26,15 @@
del _recipes
except ImportError as ex:
+ import warnings as _warnings
+
__all__ = []
_warning = ImportWarning(str(ex))
_warning.__cause__ = ex
- warnings.warn(_warning)
+ _warnings.warn(_warning)
del _warning
+ del _warnings
finally:
from . import dye as _dye
@@ -41,4 +42,3 @@
__all__ += _dye.__all__
del _dye
- del warnings
diff --git a/CAT/workflows/__init__.pyi b/CAT/workflows/__init__.pyi
index bd325a8c..eafda580 100644
--- a/CAT/workflows/__init__.pyi
+++ b/CAT/workflows/__init__.pyi
@@ -13,6 +13,22 @@ JOB_SETTINGS_QD_OPT: Tuple[str, str] = ...
JOB_SETTINGS_CRS: Tuple[str, str] = ...
JOB_SETTINGS_BDE: Tuple[str, str] = ...
JOB_SETTINGS_ASA: Tuple[str, str] = ...
+JOB_SETTINGS_CDFT: Tuple[str, str] = ...
+CDFT_MU: Tuple[str, str] = ...
+CDFT_CHI: Tuple[str, str] = ...
+CDFT_ETA: Tuple[str, str] = ...
+CDFT_S: Tuple[str, str] = ...
+CDFT_GAMMA: Tuple[str, str] = ...
+CDFT_OMEGA: Tuple[str, str] = ...
+CDFT_NUCLEOFUGE: Tuple[str, str] = ...
+CDFT_ELECTROFUGE: Tuple[str, str] = ...
+CDFT_W_MINUS: Tuple[str, str] = ...
+CDFT_W_PLUS: Tuple[str, str] = ...
+CDFT_ELECTROPHILICITY: Tuple[str, str] = ...
+CDFT_DELTAF_MINUS: Tuple[str, str] = ...
+CDFT_DELTAF_PLUS: Tuple[str, str] = ...
+CDFT_MU_MINUS: Tuple[str, str] = ...
+CDFT_MU_PLUS: Tuple[str, str] = ...
ASA_INT: Tuple[str, str] = ...
ASA_STRAIN: Tuple[str, str] = ...
ASA_E: Tuple[str, str] = ...
@@ -23,4 +39,5 @@ SETTINGS_SOLV2: Tuple[str, str] = ...
SETTINGS_ASA: Tuple[str, str] = ...
SETTINGS_BDE1: Tuple[str, str] = ...
SETTINGS_BDE2: Tuple[str, str] = ...
+SETTINGS_CDFT: Tuple[str, str] = ...
V_BULK: Tuple[str, str] = ...
diff --git a/CAT/workflows/key_map.py b/CAT/workflows/key_map.py
index 9eacc0a9..e3fe7de5 100644
--- a/CAT/workflows/key_map.py
+++ b/CAT/workflows/key_map.py
@@ -30,6 +30,22 @@
'JOB_SETTINGS_CRS': ('job_settings_crs', ''),
'JOB_SETTINGS_BDE': ('job_settings_BDE', ''),
'JOB_SETTINGS_ASA': ('job_settings_ASA', ''),
+ 'JOB_SETTINGS_CDFT': ('job_settings_cdft', ''),
+ 'CDFT_MU': ('cdft', 'Electronic chemical potential (mu)'),
+ 'CDFT_CHI': ('cdft', 'Electronegativity (chi=-mu)'),
+ 'CDFT_ETA': ('cdft', 'Hardness (eta)'),
+ 'CDFT_S': ('cdft', 'Softness (S)'),
+ 'CDFT_GAMMA': ('cdft', 'Hyperhardness (gamma)'),
+ 'CDFT_OMEGA': ('cdft', 'Electrophilicity index (w=omega)'),
+ 'CDFT_NUCLEOFUGE': ('cdft', 'Dissocation energy (nucleofuge)'),
+ 'CDFT_ELECTROFUGE': ('cdft', 'Dissociation energy (electrofuge)'),
+ 'CDFT_W_MINUS': ('cdft', 'Electrodonating power (w-)'),
+ 'CDFT_W_PLUS': ('cdft', 'Electroaccepting power(w+)'),
+ 'CDFT_ELECTROPHILICITY': ('cdft', 'Net Electrophilicity'),
+ 'CDFT_DELTAF_MINUS': ('cdft', 'Global Dual Descriptor Deltaf-'),
+ 'CDFT_DELTAF_PLUS': ('cdft', 'Global Dual Descriptor Deltaf+'),
+ 'CDFT_MU_MINUS': ('cdft', 'Electronic chemical potential (mu-)'),
+ 'CDFT_MU_PLUS': ('cdft', 'Electronic chemical potential (mu+)'),
'ASA_INT': ('ASA', 'E_int'),
'ASA_STRAIN': ('ASA', 'E_strain'),
'ASA_E': ('ASA', 'E'),
@@ -40,6 +56,7 @@
'SETTINGS_ASA': ('settings', 'ASA 1'),
'SETTINGS_BDE1': ('settings', 'BDE 1'),
'SETTINGS_BDE2': ('settings', 'BDE 2'),
+ 'SETTINGS_CDFT': ('settings', 'cdft 1'),
'V_BULK': ('V_bulk', '')
})
diff --git a/CAT/workflows/key_map.pyi b/CAT/workflows/key_map.pyi
index 792d5e2c..59247efe 100644
--- a/CAT/workflows/key_map.pyi
+++ b/CAT/workflows/key_map.pyi
@@ -12,6 +12,22 @@ JOB_SETTINGS_QD_OPT: Tuple[str, str] = ...
JOB_SETTINGS_CRS: Tuple[str, str] = ...
JOB_SETTINGS_BDE: Tuple[str, str] = ...
JOB_SETTINGS_ASA: Tuple[str, str] = ...
+JOB_SETTINGS_CDFT: Tuple[str, str] = ...
+CDFT_MU: Tuple[str, str] = ...
+CDFT_CHI: Tuple[str, str] = ...
+CDFT_ETA: Tuple[str, str] = ...
+CDFT_S: Tuple[str, str] = ...
+CDFT_GAMMA: Tuple[str, str] = ...
+CDFT_OMEGA: Tuple[str, str] = ...
+CDFT_NUCLEOFUGE: Tuple[str, str] = ...
+CDFT_ELECTROFUGE: Tuple[str, str] = ...
+CDFT_W_MINUS: Tuple[str, str] = ...
+CDFT_W_PLUS: Tuple[str, str] = ...
+CDFT_ELECTROPHILICITY: Tuple[str, str] = ...
+CDFT_DELTAF_MINUS: Tuple[str, str] = ...
+CDFT_DELTAF_PLUS: Tuple[str, str] = ...
+CDFT_MU_MINUS: Tuple[str, str] = ...
+CDFT_MU_PLUS: Tuple[str, str] = ...
ASA_INT: Tuple[str, str] = ...
ASA_STRAIN: Tuple[str, str] = ...
ASA_E: Tuple[str, str] = ...
@@ -22,4 +38,5 @@ SETTINGS_SOLV2: Tuple[str, str] = ...
SETTINGS_ASA: Tuple[str, str] = ...
SETTINGS_BDE1: Tuple[str, str] = ...
SETTINGS_BDE2: Tuple[str, str] = ...
+SETTINGS_CDFT: Tuple[str, str] = ...
V_BULK: Tuple[str, str] = ...
diff --git a/CAT/workflows/workflow_dicts.py b/CAT/workflows/workflow_dicts.py
index 47c57e7f..1afc1d23 100644
--- a/CAT/workflows/workflow_dicts.py
+++ b/CAT/workflows/workflow_dicts.py
@@ -21,6 +21,7 @@
JOB_SETTINGS_CRS,
JOB_SETTINGS_BDE,
JOB_SETTINGS_ASA,
+ JOB_SETTINGS_CDFT,
ASA_INT,
ASA_STRAIN,
ASA_E,
@@ -31,20 +32,33 @@
SETTINGS_ASA,
SETTINGS_BDE1,
SETTINGS_BDE2,
- V_BULK
+ SETTINGS_CDFT,
+ V_BULK,
+ CDFT_MU,
+ CDFT_CHI,
+ CDFT_ETA,
+ CDFT_S,
+ CDFT_GAMMA,
+ CDFT_OMEGA,
+ CDFT_NUCLEOFUGE,
+ CDFT_ELECTROFUGE,
+ CDFT_W_MINUS,
+ CDFT_W_PLUS,
+ CDFT_ELECTROPHILICITY,
+ CDFT_DELTAF_MINUS,
+ CDFT_DELTAF_PLUS,
+ CDFT_MU_MINUS,
+ CDFT_MU_PLUS
)
if TYPE_CHECKING:
- if sys.version_info >= (3, 8):
- from typing import TypedDict
- else:
- from typing_extensions import TypedDict
+ from nanoutils import TypedDict
class _TemplateMapping(TypedDict):
description: str
mol_type: str
template: Mapping[str, Tuple[str, ...]]
- import_columns: Mapping[Tuple[str, str], float]
+ import_columns: Mapping[Tuple[str, str], Any]
export_columns: Tuple[Tuple[str, str], ...]
else:
@@ -89,6 +103,18 @@ def finalize_templates():
'export_columns': (V_BULK,)},
'multi_ligand': {'import_columns': {HDF5_INDEX: -1, OPT: False},
'export_columns': (HDF5_INDEX, OPT)},
+ 'cdft': {'import_columns': {CDFT_MU: np.nan, CDFT_CHI: np.nan, CDFT_ETA: np.nan,
+ CDFT_S: np.nan, CDFT_GAMMA: np.nan, CDFT_OMEGA: np.nan,
+ CDFT_NUCLEOFUGE: np.nan, CDFT_ELECTROFUGE: np.nan,
+ CDFT_W_MINUS: np.nan, CDFT_W_PLUS: np.nan,
+ CDFT_ELECTROPHILICITY: np.nan, CDFT_DELTAF_MINUS: np.nan,
+ CDFT_DELTAF_PLUS: np.nan, CDFT_MU_MINUS: np.nan,
+ CDFT_MU_PLUS: np.nan},
+ 'export_columns': (JOB_SETTINGS_CDFT, SETTINGS_CDFT, CDFT_MU, CDFT_CHI, CDFT_ETA,
+ CDFT_S, CDFT_GAMMA, CDFT_OMEGA, CDFT_NUCLEOFUGE,
+ CDFT_ELECTROFUGE, CDFT_W_MINUS, CDFT_W_PLUS,
+ CDFT_ELECTROPHILICITY, CDFT_DELTAF_MINUS, CDFT_DELTAF_PLUS,
+ CDFT_MU_MINUS, CDFT_MU_PLUS)},
}
templates = _load_templates()
diff --git a/CAT/workflows/workflow_yaml.yaml b/CAT/workflows/workflow_yaml.yaml
index ff08575e..c80e6c4a 100644
--- a/CAT/workflows/workflow_yaml.yaml
+++ b/CAT/workflows/workflow_yaml.yaml
@@ -152,3 +152,17 @@ multi_ligand:
cluster_size: [optional, qd, multi_ligand, cluster_size]
weight: [optional, qd, multi_ligand, weight]
randomness: [optional, qd, multi_ligand, randomness]
+
+cdft:
+ description: Conceptual DFT property calculation
+ mol_type: ligand
+ template:
+ db: [optional, database, db]
+ read: [optional, database, read]
+ write: [optional, database, write]
+ overwrite: [optional, database, overwrite]
+
+ path: [optional, ligand, dirname]
+ keep_files: [optional, ligand, cdft, keep_files]
+ job1: [optional, ligand, cdft, job1]
+ s1: [optional, ligand, cdft, s1]
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d7d4bf6e..fc7cd85a 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -11,6 +11,12 @@ This project adheres to `Semantic Versioning `_.
* WiP: Added an option the import pre-built quantum dots.
+0.9.1
+*****
+* Added a new conceptual DFT (CDFT) workflow to Nano-CAT
+ (https://github.com/nlesc-nano/nano-CAT/pull/57).
+
+
0.9.0
*****
* Moved a number of functions to the `nanoutils `_ package.
diff --git a/README.rst b/README.rst
index c46e7dc9..bec8f6de 100644
--- a/README.rst
+++ b/README.rst
@@ -16,7 +16,7 @@
##############################
-Compound Attachment Tool 0.9.0
+Compound Attachment Tool 0.9.1
##############################
**CAT** is a collection of tools designed for the construction of various chemical compounds.
diff --git a/docs/4_optional.rst b/docs/4_optional.rst
index 3ad10588..d7c57248 100644
--- a/docs/4_optional.rst
+++ b/docs/4_optional.rst
@@ -33,6 +33,7 @@ Option Description
:attr:`optional.ligand.functional_groups` Manually specify SMILES strings representing functional groups.
:attr:`optional.ligand.split` If the ligand should be attached in its entirety to the core or not.
:attr:`optional.ligand.cosmo-rs` Perform a property calculation with COSMO-RS on the ligand.
+:attr:`optional.ligand.cdft` Perform a conceptual DFT calculation with ADF on the ligand.
:attr:`optional.qd.dirname` The name of the directory where all quantum dots will be stored.
:attr:`optional.qd.construct_qd` Whether or not the quantum dot should actually be constructed or not.
@@ -69,6 +70,7 @@ Default Settings
functional_groups: null
split: True
cosmo-rs: False
+ cdft: False
qd:
dirname: qd
@@ -546,6 +548,7 @@ Ligand
functional_groups: null
split: True
cosmo-rs: False
+ cdft: False
|
@@ -588,7 +591,6 @@ Ligand
job2: ADFJob
-
.. attribute:: optional.ligand.functional_groups
:Parameter: * **Type** - :class:`str` or :class:`tuple` [:class:`str`]
@@ -612,6 +614,7 @@ Ligand
.. note::
The yaml format uses ``null`` rather than ``None`` as in Python.
+
.. attribute:: optional.ligand.split
:Parameter: * **Type** - :class:`bool`
@@ -657,6 +660,58 @@ Ligand
dimethyl formamide (DMF), dimethyl sulfoxide (DMSO), ethyl acetate,
ethanol, *n*-hexane, toluene and water.
+
+ .. attribute:: optional.ligand.cdft
+
+ :Parameter: * **Type** - :class:`bool` or :class:`dict`
+ * **Default value** – ``False``
+
+
+ Perform a conceptual DFT (CDFT) calculation with `ADF `_ on the ligand.
+
+ All global descriptors are, if installed, stored in the database.
+ This includes the following properties:
+
+ * Electronic chemical potential (mu)
+ * Electronic chemical potential (mu+)
+ * Electronic chemical potential (mu-)
+ * Electronegativity (chi=-mu)
+ * Hardness (eta)
+ * Softness (S)
+ * Hyperhardness (gamma)
+ * Electrophilicity index (w=omega)
+ * Dissocation energy (nucleofuge)
+ * Dissociation energy (electrofuge)
+ * Electrodonating power (w-)
+ * Electroaccepting power(w+)
+ * Net Electrophilicity
+ * Global Dual Descriptor Deltaf+
+ * Global Dual Descriptor Deltaf-
+
+ This block can be furthermore customized with one or more of the following keys:
+
+ * ``"keep_files"``: Whether or not to delete the ADF output afterwards.
+ * ``"job1"``: The type of PLAMS Job used for running the calculation.
+ The only value that should be supplied here (if any) is ``"ADFJob"``.
+ * ``"s1"``: The job Settings used for running the CDFT calculation.
+ Can be left blank to use the default template (:data:`nanoCAT.cdft.cdft`).
+
+ .. admonition:: Examples
+
+ .. code:: yaml
+
+ optional:
+ ligand:
+ cdft: True
+
+ .. code:: yaml
+
+ optional:
+ ligand:
+ cdft:
+ job1: ADFJob
+ s1: ... # Insert custom settings here
+
|
QD
diff --git a/docs/conf.py b/docs/conf.py
index 9841a872..df42e2e4 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -69,7 +69,7 @@
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the built documents.
-release = '0.9.0' # The full version, including alpha/beta/rc tags.
+release = '0.9.1' # The full version, including alpha/beta/rc tags.
version = release.rsplit('.', maxsplit=1)[0]
diff --git a/tests/test_schemas.py b/tests/test_schemas.py
index a1b8f464..c2d9c007 100644
--- a/tests/test_schemas.py
+++ b/tests/test_schemas.py
@@ -124,7 +124,8 @@ def test_ligand_schema() -> None:
'functional_groups': None,
'optimize': {'job1': None},
'split': True,
- 'cosmo-rs': False
+ 'cosmo-rs': False,
+ 'cdft': False
}
assertion.eq(ligand_schema.validate(lig_dict), ref)
diff --git a/tests/test_validate_input.py b/tests/test_validate_input.py
index 5dd77708..bed2b60e 100644
--- a/tests/test_validate_input.py
+++ b/tests/test_validate_input.py
@@ -45,6 +45,7 @@ def test_validate_input() -> None:
ref.ligand.optimize = {'job1': None, 'job2': None, 's1': None, 's2': Settings(),
'use_ff': False, 'keep_files': True}
ref.ligand.split = True
+ ref.ligand.cdft = False
ref.qd.bulkiness = False
ref.qd.construct_qd = True