From def7fb01fee24aee5f1bca5b8ad3e25d46cd9b6b Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Sat, 28 Oct 2023 13:42:47 -0400
Subject: [PATCH 01/37] Update inputs.py
Add "IWAVPR" to the tuple of int_keys.
---
pymatgen/io/vasp/inputs.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/pymatgen/io/vasp/inputs.py b/pymatgen/io/vasp/inputs.py
index 56bbd14a3a3..58c5e48d7ac 100644
--- a/pymatgen/io/vasp/inputs.py
+++ b/pymatgen/io/vasp/inputs.py
@@ -845,6 +845,7 @@ def proc_val(key: str, val: Any):
"ISPIND",
"LDAUTYPE",
"IVDW",
+ "IWAVPR",
)
def smart_int_or_float(numstr):
From 75372bfdeb6664011d704c890532ea93b87dfaa6 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Sat, 4 Nov 2023 18:03:55 -0400
Subject: [PATCH 02/37] Update chargemol_caller.py
Add the mpi function when running the chargemol program.
---
pymatgen/command_line/chargemol_caller.py | 26 +++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index dff7e89f3f6..17f487589e5 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -80,7 +80,9 @@ def __init__(
self,
path=None,
atomic_densities_path=None,
- run_chargemol=True,
+ run_chargemol: bool =True,
+ mpi: bool = False,
+ ncores:int = None,
):
"""
Initializes the Chargemol Analysis.
@@ -97,6 +99,9 @@ def __init__(
run_chargemol (bool): Whether to run the Chargemol analysis. If False,
the existing Chargemol output files will be read from path.
Default: True.
+ mpi (bool): Whether to run the Chargemol in a parallel way.
+ ncores: Use how many cores to run the Chargemol! Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE') or os.environ.get('SLURM_CPUS_ON_NODE')",
+ or "multiprocessing.cpu_count()". Take your own risk! This default value might not suit you! You'd better set your own number!!!
"""
if not path:
path = os.getcwd()
@@ -137,7 +142,7 @@ def __init__(
self.aeccar2 = Chgcar.from_file(self._aeccar2path) if self._aeccar2path else None
if run_chargemol:
- self._execute_chargemol()
+ self._execute_chargemol(mpi=mpi,ncores=ncores)
else:
self._from_data_dir(chargemol_output_path=path)
@@ -169,7 +174,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -178,9 +183,22 @@ def _execute_chargemol(self, **jobcontrol_kwargs):
required by Chargemol. If None, Pymatgen assumes that this is
defined in a "DDEC6_ATOMIC_DENSITIES_DIR" environment variable.
Default: None.
+ mpi(bool): Whether run the Chargemol in a parallel way. Default is False.
+ ncores (int): The number of cores you want to use. Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS') or multiprocessing.cpu_count().
jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
"""
-
+
+ if mpi:
+ if ncores:
+ CHARGEMOLEXE = f"mpirun -n {ncores} {CHARGEMOLEXE}"
+ else:
+ ncores = os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
+ if ncores:
+ ncores = multiprocessing.cpu_count()
+ CHARGEMOLEXE = f"mpirun -n {ncores} {CHARGEMOLEXE}"
+ else:
+ pass
+
with ScratchDir("."):
with zopen(self._chgcarpath, "rt") as f_in:
with open("CHGCAR", "wt") as f_out:
From 5efa8cdba952d0bb634ae8a798b03e10b8f3e47f Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Sat, 4 Nov 2023 18:09:41 -0400
Subject: [PATCH 03/37] Update chargemol_caller.py
import multiprocessing
---
pymatgen/command_line/chargemol_caller.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 17f487589e5..b719ba8f6be 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -53,6 +53,7 @@
import os
import shutil
import subprocess
+import multiprocessing
import warnings
from shutil import which
From d67aa94e2c4539cfcb30bc3277191318fe2098bd Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Sat, 4 Nov 2023 19:23:27 -0400
Subject: [PATCH 04/37] Update chargemol_caller.py
Comment the warning while check the path.
Revise the command of mpirun.
---
pymatgen/command_line/chargemol_caller.py | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index b719ba8f6be..da3a1948d8b 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -77,6 +77,10 @@ class ChargemolAnalysis:
bond orders, and related properties.
"""
+ CHARGEMOLEXE = (
+ which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
+)
+
def __init__(
self,
path=None,
@@ -170,8 +174,8 @@ def _get_filepath(path, filename, suffix=""):
# and this would give 'static' over 'relax2' over 'relax'
# however, better to use 'suffix' kwarg to avoid this!
paths.sort(reverse=True)
- warning_msg = f"Multiple files detected, using {os.path.basename(paths[0])}" if len(paths) > 1 else None
- warnings.warn(warning_msg)
+ # warning_msg = f"Multiple files detected, using {os.path.basename(paths[0])}" if len(paths) > 1 else None
+ # warnings.warn(warning_msg)
fpath = paths[0]
return fpath
@@ -191,12 +195,12 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
if mpi:
if ncores:
- CHARGEMOLEXE = f"mpirun -n {ncores} {CHARGEMOLEXE}"
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
ncores = os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
if ncores:
ncores = multiprocessing.cpu_count()
- CHARGEMOLEXE = f"mpirun -n {ncores} {CHARGEMOLEXE}"
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
pass
From b7e2343954029baebd30f2a362542ab5f4357fd2 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Sat, 4 Nov 2023 19:31:07 -0400
Subject: [PATCH 05/37] Update chargemol_caller.py
Change the command to run chargemol
---
pymatgen/command_line/chargemol_caller.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index da3a1948d8b..49b62595341 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -202,7 +202,7 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
ncores = multiprocessing.cpu_count()
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
- pass
+ CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
with ScratchDir("."):
with zopen(self._chgcarpath, "rt") as f_in:
From 6498f618b863fe1d638f728e2344055b16730558 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Sun, 5 Nov 2023 02:08:14 -0500
Subject: [PATCH 06/37] Update chargemol_caller.py
Change the previous copy to link for CHGCAR, POTCAR, AECCAR0, AECCAR2.
Add the save parameter, to control whether save the output files of chargemol.
---
pymatgen/command_line/chargemol_caller.py | 59 +++++++++++++++--------
1 file changed, 40 insertions(+), 19 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 49b62595341..2a2986c8807 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -56,6 +56,7 @@
import multiprocessing
import warnings
from shutil import which
+from pathlib import Path
import numpy as np
from monty.io import zopen
@@ -87,7 +88,8 @@ def __init__(
atomic_densities_path=None,
run_chargemol: bool =True,
mpi: bool = False,
- ncores:int = None,
+ ncores: int = None,
+ save: bool = False,
):
"""
Initializes the Chargemol Analysis.
@@ -107,6 +109,7 @@ def __init__(
mpi (bool): Whether to run the Chargemol in a parallel way.
ncores: Use how many cores to run the Chargemol! Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE') or os.environ.get('SLURM_CPUS_ON_NODE')",
or "multiprocessing.cpu_count()". Take your own risk! This default value might not suit you! You'd better set your own number!!!
+ save: save (bool): Whether to save the Chargemol output files. Default is False.
"""
if not path:
path = os.getcwd()
@@ -123,7 +126,8 @@ def __init__(
if atomic_densities_path == "":
atomic_densities_path = os.getcwd()
self._atomic_densities_path = atomic_densities_path
-
+ self.save = save
+
self._chgcarpath = self._get_filepath(path, "CHGCAR")
self._potcarpath = self._get_filepath(path, "POTCAR")
self._aeccar0path = self._get_filepath(path, "AECCAR0")
@@ -204,22 +208,14 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
else:
CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
- with ScratchDir("."):
- with zopen(self._chgcarpath, "rt") as f_in:
- with open("CHGCAR", "wt") as f_out:
- shutil.copyfileobj(f_in, f_out)
- with zopen(self._potcarpath, "rt") as f_in:
- with open("POTCAR", "wt") as f_out:
- shutil.copyfileobj(f_in, f_out)
- with zopen(self._aeccar0path, "rt") as f_in:
- with open("AECCAR0", "wt") as f_out:
- shutil.copyfileobj(f_in, f_out)
- with zopen(self._aeccar2path, "rt") as f_in:
- with open("AECCAR2", "wt") as f_out:
- shutil.copyfileobj(f_in, f_out)
-
+ if self.save:
+ save_path = Path(Path.cwd(),"charge")
+ save_path.mkdir(parents=True, exist_ok=True)
+ source = [Path(self._chgcarpath),Path(self._potcarpath),Path(self._aeccar0path),Path(self._aeccar2path)]
+ links = [Path(save_path,"CHGCAR"),Path(save_path,"POTCAR"),Path(save_path,"AECCAR0"),Path(save_path,"AECCAR2")]
+ [links[i].symlink_to(source[i]) for i in range(len(links))]
# write job_script file:
- self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
+ self._write_jobscript_for_chargemol(write_path=str(save_path)+"/job_control.txt",**jobcontrol_kwargs)
# Run Chargemol
with subprocess.Popen(
@@ -227,14 +223,38 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
close_fds=True,
+ cwd=save_path,
) as rs:
rs.communicate()
if rs.returncode != 0:
raise RuntimeError(
f"Chargemol exited with return code {int(rs.returncode)}. Please check your Chargemol installation."
)
+ self._from_data_dir(chargemol_output_path=save_path)
- self._from_data_dir()
+ else:
+ with ScratchDir("."):
+ cwd = Path.cwd()
+ source = [Path(self._chgcarpath),Path(self._potcarpath),Path(self._aeccar0path),Path(self._aeccar2path)]
+ links = [Path(cwd,"CHGCAR"),Path(cwd,"POTCAR"),Path(cwd,"AECCAR0"),Path(cwd,"AECCAR2")]
+ [links[i].symlink_to(source[i]) for i in range(len(links))]
+ # write job_script file:
+ self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
+
+ # Run Chargemol
+ with subprocess.Popen(
+ CHARGEMOLEXE,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ close_fds=True,
+ ) as rs:
+ rs.communicate()
+ if rs.returncode != 0:
+ raise RuntimeError(
+ f"Chargemol exited with return code {int(rs.returncode)}. Please check your Chargemol installation."
+ )
+
+ self._from_data_dir()
def _from_data_dir(self, chargemol_output_path=None):
"""
@@ -388,6 +408,7 @@ def _write_jobscript_for_chargemol(
periodicity=[True, True, True],
method="ddec6",
compute_bond_orders=True,
+ write_path: str = "job_control.txt",
):
"""
Writes job_script.txt for Chargemol execution
@@ -451,7 +472,7 @@ def _write_jobscript_for_chargemol(
bo = ".true." if compute_bond_orders else ".false."
lines += f"\n\n{bo}\n\n"
- with open("job_control.txt", "wt") as fh:
+ with open(write_path, "wt") as fh:
fh.write(lines)
@staticmethod
From ad4809747c842d07e84851b31bf95bf55904ae49 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 6 Nov 2023 21:47:26 +0000
Subject: [PATCH 07/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 59329e08bd9..51bf1796b6a 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -83,7 +83,7 @@ class ChargemolAnalysis:
CHARGEMOLEXE = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
-
+
def __init__(
self,
path=None,
@@ -115,7 +115,7 @@ def __init__(
Default: True.
mpi (bool): Whether to run the Chargemol in a parallel way.
ncores: Use how many cores to run the Chargemol! Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE') or os.environ.get('SLURM_CPUS_ON_NODE')",
- or "multiprocessing.cpu_count()". Take your own risk! This default value might not suit you! You'd better set your own number!!!
+ or "multiprocessing.cpu_count()". Take your own risk! This default value might not suit you! You'd better set your own number!!!
save: save (bool): Whether to save the Chargemol output files. Default is False.
the existing Chargemol output files will be read from path. Default: True.
"""
@@ -131,7 +131,7 @@ def __init__(
self._atomic_densities_path = atomic_densities_path
self.save = save
-
+
self._chgcarpath = self._get_filepath(path, "CHGCAR")
self._potcarpath = self._get_filepath(path, "POTCAR")
self._aeccar0path = self._get_filepath(path, "AECCAR0")
@@ -213,10 +213,10 @@ def _execute_chargemol(self, **job_control_kwargs):
Default: None.
mpi(bool): Whether run the Chargemol in a parallel way. Default is False.
- ncores (int): The number of cores you want to use. Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS') or multiprocessing.cpu_count().
+ ncores (int): The number of cores you want to use. Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS') or multiprocessing.cpu_count().
jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
"""
-
+
if mpi:
if ncores:
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
@@ -227,7 +227,7 @@ def _execute_chargemol(self, **job_control_kwargs):
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
-
+
if self.save:
save_path = Path(Path.cwd(),"charge")
save_path.mkdir(parents=True, exist_ok=True)
@@ -279,7 +279,7 @@ def _execute_chargemol(self, **job_control_kwargs):
[links[i].symlink_to(source[i]) for i in range(len(links))]
# write job_script file:
self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
-
+
# Run Chargemol
with subprocess.Popen(
CHARGEMOLEXE,
@@ -292,7 +292,7 @@ def _execute_chargemol(self, **job_control_kwargs):
raise RuntimeError(
f"Chargemol exited with return code {int(rs.returncode)}. Please check your Chargemol installation."
)
-
+
self._from_data_dir()
def _from_data_dir(self, chargemol_output_path=None):
From e1756fd92d6e864939ffa872e520ac5f7729d533 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Mon, 6 Nov 2023 22:20:47 +0000
Subject: [PATCH 08/37] Revise some small parts in chargemol_caller.py
---
pymatgen/command_line/chargemol_caller.py | 47 +----------------------
1 file changed, 1 insertion(+), 46 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 51bf1796b6a..b4b4b95e17e 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -96,12 +96,6 @@ def __init__(
"""
Initializes the Chargemol Analysis.
- path: str | Path | None = None,
- atomic_densities_path: str | Path | None = None,
- run_chargemol: bool = True,
- ) -> None:
- """Initializes the Chargemol Analysis.
-
Args:
path (str): Path to the CHGCAR, POTCAR, AECCAR0, and AECCAR files.
@@ -129,20 +123,13 @@ def __init__(
if atomic_densities_path == "":
atomic_densities_path = os.getcwd()
self._atomic_densities_path = atomic_densities_path
-
self.save = save
-
+
self._chgcarpath = self._get_filepath(path, "CHGCAR")
self._potcarpath = self._get_filepath(path, "POTCAR")
self._aeccar0path = self._get_filepath(path, "AECCAR0")
self._aeccar2path = self._get_filepath(path, "AECCAR2")
- if run_chargemol and not (self._chgcarpath and self._potcarpath and self._aeccar0path and self._aeccar2path):
-
- self._chgcar_path = self._get_filepath(path, "CHGCAR")
- self._potcar_path = self._get_filepath(path, "POTCAR")
- self._aeccar0_path = self._get_filepath(path, "AECCAR0")
- self._aeccar2_path = self._get_filepath(path, "AECCAR2")
if run_chargemol and not (
self._chgcar_path and self._potcar_path and self._aeccar0_path and self._aeccar2_path
):
@@ -197,14 +184,6 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
- if len(paths) > 1:
- warnings.warn(f"Multiple files detected, using {os.path.basename(paths[0])}")
- fpath = paths[0]
- return fpath
-
- def _execute_chargemol(self, **job_control_kwargs):
- """Internal function to run Chargemol.
-
Args:
atomic_densities_path (str): Path to the atomic densities directory
@@ -247,30 +226,6 @@ def _execute_chargemol(self, **job_control_kwargs):
) as rs:
rs.communicate()
- job_control_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
- """
- with ScratchDir("."):
- try:
- os.symlink(self._chgcar_path, "./CHGCAR")
- os.symlink(self._potcar_path, "./POTCAR")
- os.symlink(self._aeccar0_path, "./AECCAR0")
- os.symlink(self._aeccar2_path, "./AECCAR2")
- except OSError as exc:
- print(f"Error creating symbolic link: {exc}")
-
- # write job_script file:
- self._write_jobscript_for_chargemol(**job_control_kwargs)
-
- # Run Chargemol
- with subprocess.Popen(CHARGEMOL_EXE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, close_fds=True) as rs:
- _stdout, stderr = rs.communicate()
- if rs.returncode != 0:
- raise RuntimeError(
- f"{CHARGEMOL_EXE} exit code: {rs.returncode}, error message: {stderr!s}. "
- "Please check your Chargemol installation."
- )
- self._from_data_dir(chargemol_output_path=save_path)
-
else:
with ScratchDir("."):
cwd = Path.cwd()
From e9c69bc0cb9c0974d5b7f900c0f65c43286c6756 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 6 Nov 2023 22:22:30 +0000
Subject: [PATCH 09/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 41 +++++++++++++----------
1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index b4b4b95e17e..c275d3f6832 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -42,17 +42,15 @@
from __future__ import annotations
+import multiprocessing
import os
import subprocess
-import multiprocessing
import warnings
from glob import glob
-from shutil import which
-
from pathlib import Path
+from shutil import which
from typing import TYPE_CHECKING
-
import numpy as np
from monty.tempfile import ScratchDir
@@ -81,14 +79,14 @@ class ChargemolAnalysis:
"""
CHARGEMOLEXE = (
- which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
-)
+ which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
+ )
def __init__(
self,
path=None,
atomic_densities_path=None,
- run_chargemol: bool =True,
+ run_chargemol: bool = True,
mpi: bool = False,
ncores: int = None,
save: bool = False,
@@ -124,7 +122,7 @@ def __init__(
atomic_densities_path = os.getcwd()
self._atomic_densities_path = atomic_densities_path
self.save = save
-
+
self._chgcarpath = self._get_filepath(path, "CHGCAR")
self._potcarpath = self._get_filepath(path, "POTCAR")
self._aeccar0path = self._get_filepath(path, "AECCAR0")
@@ -149,7 +147,7 @@ def __init__(
self.aeccar2 = Chgcar.from_file(self._aeccar2_path) if self._aeccar2_path else None
if run_chargemol:
- self._execute_chargemol(mpi=mpi,ncores=ncores)
+ self._execute_chargemol(mpi=mpi, ncores=ncores)
else:
self._from_data_dir(chargemol_output_path=path)
@@ -195,12 +193,11 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
ncores (int): The number of cores you want to use. Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS') or multiprocessing.cpu_count().
jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
"""
-
if mpi:
if ncores:
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
- ncores = os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
+ ncores = os.getenv("SLURM_CPUS_ON_NODE") or os.getenv("SLURM_NTASKS")
if ncores:
ncores = multiprocessing.cpu_count()
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
@@ -208,13 +205,18 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
if self.save:
- save_path = Path(Path.cwd(),"charge")
+ save_path = Path(Path.cwd(), "charge")
save_path.mkdir(parents=True, exist_ok=True)
- source = [Path(self._chgcarpath),Path(self._potcarpath),Path(self._aeccar0path),Path(self._aeccar2path)]
- links = [Path(save_path,"CHGCAR"),Path(save_path,"POTCAR"),Path(save_path,"AECCAR0"),Path(save_path,"AECCAR2")]
+ source = [Path(self._chgcarpath), Path(self._potcarpath), Path(self._aeccar0path), Path(self._aeccar2path)]
+ links = [
+ Path(save_path, "CHGCAR"),
+ Path(save_path, "POTCAR"),
+ Path(save_path, "AECCAR0"),
+ Path(save_path, "AECCAR2"),
+ ]
[links[i].symlink_to(source[i]) for i in range(len(links))]
# write job_script file:
- self._write_jobscript_for_chargemol(write_path=str(save_path)+"/job_control.txt",**jobcontrol_kwargs)
+ self._write_jobscript_for_chargemol(write_path=str(save_path) + "/job_control.txt", **jobcontrol_kwargs)
# Run Chargemol
with subprocess.Popen(
@@ -229,8 +231,13 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
else:
with ScratchDir("."):
cwd = Path.cwd()
- source = [Path(self._chgcarpath),Path(self._potcarpath),Path(self._aeccar0path),Path(self._aeccar2path)]
- links = [Path(cwd,"CHGCAR"),Path(cwd,"POTCAR"),Path(cwd,"AECCAR0"),Path(cwd,"AECCAR2")]
+ source = [
+ Path(self._chgcarpath),
+ Path(self._potcarpath),
+ Path(self._aeccar0path),
+ Path(self._aeccar2path),
+ ]
+ links = [Path(cwd, "CHGCAR"), Path(cwd, "POTCAR"), Path(cwd, "AECCAR0"), Path(cwd, "AECCAR2")]
[links[i].symlink_to(source[i]) for i in range(len(links))]
# write job_script file:
self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
From bdd8f89f83e050d3e275a9ebaceb1d97002ed09b Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Mon, 6 Nov 2023 22:58:27 +0000
Subject: [PATCH 10/37] Change the import method of Path, shorten the line
---
pymatgen/command_line/chargemol_caller.py | 30 +++++++++++++++--------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index c275d3f6832..3463c9cadbf 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -49,8 +49,11 @@
from glob import glob
from pathlib import Path
from shutil import which
+
+from pathlib import Path
from typing import TYPE_CHECKING
+
import numpy as np
from monty.tempfile import ScratchDir
@@ -86,9 +89,9 @@ def __init__(
self,
path=None,
atomic_densities_path=None,
- run_chargemol: bool = True,
+ run_chargemol: bool =True,
mpi: bool = False,
- ncores: int = None,
+ ncores: Optional[int] = None,
save: bool = False,
):
"""
@@ -106,10 +109,13 @@ def __init__(
the existing Chargemol output files will be read from path.
Default: True.
mpi (bool): Whether to run the Chargemol in a parallel way.
- ncores: Use how many cores to run the Chargemol! Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE') or os.environ.get('SLURM_CPUS_ON_NODE')",
- or "multiprocessing.cpu_count()". Take your own risk! This default value might not suit you! You'd better set your own number!!!
+ ncores: Use how many cores to run the Chargemol!
+ Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE'),
+ or os.environ.get('SLURM_CPUS_ON_NODE')", or "multiprocessing.cpu_count()".
+ Take your own risk! This default value might not suit you!
+ You'd better set your own number!!!
save: save (bool): Whether to save the Chargemol output files. Default is False.
- the existing Chargemol output files will be read from path. Default: True.
+ the existing Chargemol output files will be read from path. Default: True.
"""
path = path or os.getcwd()
if run_chargemol and not CHARGEMOL_EXE:
@@ -190,7 +196,9 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
Default: None.
mpi(bool): Whether run the Chargemol in a parallel way. Default is False.
- ncores (int): The number of cores you want to use. Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS') or multiprocessing.cpu_count().
+ ncores (int): The number of cores you want to use.
+ Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
+ or multiprocessing.cpu_count().
jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
"""
if mpi:
@@ -258,12 +266,13 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
self._from_data_dir()
def _from_data_dir(self, chargemol_output_path=None):
- """Internal command to parse Chargemol files from a directory.
+ """
+ Internal command to parse Chargemol files from a directory.
Args:
- chargemol_output_path (str): Path to the folder containing the
- Chargemol output files.
- Default: None (current working directory).
+ chargemol_output_path (str): Path to the folder containing
+ the Chargemol output files.
+ Default: None (current working directory).
"""
if chargemol_output_path is None:
chargemol_output_path = "."
@@ -411,6 +420,7 @@ def _write_jobscript_for_chargemol(
method (str): Method to use for the analysis. Options include "ddec6"
and "ddec3". Default: "ddec6"
compute_bond_orders (bool): Whether to compute bond orders. Default: True.
+ write_path (str): The path of output files of chargemol if you want to save them.
"""
self.net_charge = net_charge
self.periodicity = periodicity
From 3fabe7815f3b39de55b3e6a6e14a751c7022c6a1 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Mon, 6 Nov 2023 22:45:37 +0000
Subject: [PATCH 11/37] Change the import method of Path, shorten the line
---
pymatgen/command_line/chargemol_caller.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 3463c9cadbf..270da4161a5 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -47,11 +47,9 @@
import subprocess
import warnings
from glob import glob
-from pathlib import Path
from shutil import which
-from pathlib import Path
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Optional
import numpy as np
From 3f0cce06b39cf34b6e4388d9566530728e19fe50 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 6 Nov 2023 23:00:19 +0000
Subject: [PATCH 12/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 270da4161a5..a931c909269 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -48,10 +48,8 @@
import warnings
from glob import glob
from shutil import which
-
from typing import TYPE_CHECKING, Optional
-
import numpy as np
from monty.tempfile import ScratchDir
@@ -87,7 +85,7 @@ def __init__(
self,
path=None,
atomic_densities_path=None,
- run_chargemol: bool =True,
+ run_chargemol: bool = True,
mpi: bool = False,
ncores: Optional[int] = None,
save: bool = False,
@@ -107,10 +105,10 @@ def __init__(
the existing Chargemol output files will be read from path.
Default: True.
mpi (bool): Whether to run the Chargemol in a parallel way.
- ncores: Use how many cores to run the Chargemol!
- Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE'),
- or os.environ.get('SLURM_CPUS_ON_NODE')", or "multiprocessing.cpu_count()".
- Take your own risk! This default value might not suit you!
+ ncores: Use how many cores to run the Chargemol!
+ Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE'),
+ or os.environ.get('SLURM_CPUS_ON_NODE')", or "multiprocessing.cpu_count()".
+ Take your own risk! This default value might not suit you!
You'd better set your own number!!!
save: save (bool): Whether to save the Chargemol output files. Default is False.
the existing Chargemol output files will be read from path. Default: True.
@@ -194,8 +192,8 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
Default: None.
mpi(bool): Whether run the Chargemol in a parallel way. Default is False.
- ncores (int): The number of cores you want to use.
- Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
+ ncores (int): The number of cores you want to use.
+ Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
or multiprocessing.cpu_count().
jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
"""
@@ -268,7 +266,7 @@ def _from_data_dir(self, chargemol_output_path=None):
Internal command to parse Chargemol files from a directory.
Args:
- chargemol_output_path (str): Path to the folder containing
+ chargemol_output_path (str): Path to the folder containing
the Chargemol output files.
Default: None (current working directory).
"""
@@ -418,7 +416,7 @@ def _write_jobscript_for_chargemol(
method (str): Method to use for the analysis. Options include "ddec6"
and "ddec3". Default: "ddec6"
compute_bond_orders (bool): Whether to compute bond orders. Default: True.
- write_path (str): The path of output files of chargemol if you want to save them.
+ write_path (str): The path of output files of chargemol if you want to save them.
"""
self.net_charge = net_charge
self.periodicity = periodicity
From e482d87dee07c39a6f9e5b071fdce815b27826e7 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Mon, 6 Nov 2023 23:18:33 +0000
Subject: [PATCH 13/37] Delete trailing whitespace, shorten the code line
---
pymatgen/command_line/chargemol_caller.py | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index a931c909269..f0bfdd64622 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -48,7 +48,9 @@
import warnings
from glob import glob
from shutil import which
-from typing import TYPE_CHECKING, Optional
+
+from typing import TYPE_CHECKING
+
import numpy as np
from monty.tempfile import ScratchDir
@@ -57,8 +59,8 @@
from pymatgen.io.vasp.inputs import Potcar
from pymatgen.io.vasp.outputs import Chgcar
-if TYPE_CHECKING:
- from pathlib import Path
+
+
__author__ = "Martin Siron, Andrew S. Rosen"
__version__ = "0.1"
@@ -83,11 +85,11 @@ class ChargemolAnalysis:
def __init__(
self,
- path=None,
+ path: str | None =None,
atomic_densities_path=None,
run_chargemol: bool = True,
mpi: bool = False,
- ncores: Optional[int] = None,
+ ncores: int | None = None,
save: bool = False,
):
"""
@@ -180,7 +182,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores: int | None =None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -256,12 +258,13 @@ def _execute_chargemol(self, mpi=False, ncores=None, **jobcontrol_kwargs):
rs.communicate()
if rs.returncode != 0:
raise RuntimeError(
- f"Chargemol exited with return code {int(rs.returncode)}. Please check your Chargemol installation."
+ f"Chargemol exited with return code {int(rs.returncode)}.
+ Please check your Chargemol installation."
)
self._from_data_dir()
- def _from_data_dir(self, chargemol_output_path=None):
+ def _from_data_dir(self, chargemol_output_path: str | None =None):
"""
Internal command to parse Chargemol files from a directory.
@@ -336,7 +339,7 @@ def get_charge_transfer(self, atom_index, charge_type="ddec"):
charge_transfer = -self.cm5_charges[atom_index]
return charge_transfer
- def get_charge(self, atom_index, nelect=None, charge_type="ddec"):
+ def get_charge(self, atom_index, nelect: int | None =None, charge_type="ddec"):
"""Convenience method to get the charge on a particular atom using the same
sign convention as the BaderAnalysis. Note that this is *not* the partial
atomic charge. This value is nelect (e.g. ZVAL from the POTCAR) + the
From f0374197aa481da05c37c4f199bd6565fe79e9f3 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Mon, 6 Nov 2023 23:21:38 +0000
Subject: [PATCH 14/37] All Path pack
---
pymatgen/command_line/chargemol_caller.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index f0bfdd64622..0d28b78ad1d 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -50,6 +50,7 @@
from shutil import which
from typing import TYPE_CHECKING
+from pathlib import Path
import numpy as np
From 845ac0b43c30389ecd42159c5fef4c5590e09fdd Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Mon, 6 Nov 2023 23:38:14 +0000
Subject: [PATCH 15/37] Revise the f-string
---
pymatgen/command_line/chargemol_caller.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 0d28b78ad1d..d5463704206 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -259,8 +259,8 @@ def _execute_chargemol(self, mpi=False, ncores: int | None =None, **jobcontrol_k
rs.communicate()
if rs.returncode != 0:
raise RuntimeError(
- f"Chargemol exited with return code {int(rs.returncode)}.
- Please check your Chargemol installation."
+ f"Chargemol exited with return code {int(rs.returncode)}. "
+ "Please check your Chargemol installation."
)
self._from_data_dir()
From 3509c5b78908cfc5448b847ce00c7b9b1e3ffb29 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 6 Nov 2023 23:40:05 +0000
Subject: [PATCH 16/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index d5463704206..5697f5a3925 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -47,11 +47,8 @@
import subprocess
import warnings
from glob import glob
-from shutil import which
-
-from typing import TYPE_CHECKING
from pathlib import Path
-
+from shutil import which
import numpy as np
from monty.tempfile import ScratchDir
@@ -60,9 +57,6 @@
from pymatgen.io.vasp.inputs import Potcar
from pymatgen.io.vasp.outputs import Chgcar
-
-
-
__author__ = "Martin Siron, Andrew S. Rosen"
__version__ = "0.1"
__maintainer__ = "Shyue Ping Ong"
@@ -86,7 +80,7 @@ class ChargemolAnalysis:
def __init__(
self,
- path: str | None =None,
+ path: str | None = None,
atomic_densities_path=None,
run_chargemol: bool = True,
mpi: bool = False,
@@ -183,7 +177,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, mpi=False, ncores: int | None =None, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores: int | None = None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -265,7 +259,7 @@ def _execute_chargemol(self, mpi=False, ncores: int | None =None, **jobcontrol_k
self._from_data_dir()
- def _from_data_dir(self, chargemol_output_path: str | None =None):
+ def _from_data_dir(self, chargemol_output_path: str | None = None):
"""
Internal command to parse Chargemol files from a directory.
@@ -340,7 +334,7 @@ def get_charge_transfer(self, atom_index, charge_type="ddec"):
charge_transfer = -self.cm5_charges[atom_index]
return charge_transfer
- def get_charge(self, atom_index, nelect: int | None =None, charge_type="ddec"):
+ def get_charge(self, atom_index, nelect: int | None = None, charge_type="ddec"):
"""Convenience method to get the charge on a particular atom using the same
sign convention as the BaderAnalysis. Note that this is *not* the partial
atomic charge. This value is nelect (e.g. ZVAL from the POTCAR) + the
From 4b1bff21478c6b563206c92e60d06ba6dae698fd Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:26:25 +0000
Subject: [PATCH 17/37] Revise _chgcarpath to _chgcar_path, add
self._from_data_dir() to mode with outputs saved
---
pymatgen/command_line/chargemol_caller.py | 32 +++++++++++++----------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 5697f5a3925..808cd404a07 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -122,10 +122,10 @@ def __init__(
self._atomic_densities_path = atomic_densities_path
self.save = save
- self._chgcarpath = self._get_filepath(path, "CHGCAR")
- self._potcarpath = self._get_filepath(path, "POTCAR")
- self._aeccar0path = self._get_filepath(path, "AECCAR0")
- self._aeccar2path = self._get_filepath(path, "AECCAR2")
+ self._chgcar_path = self._get_filepath(path, "CHGCAR")
+ self._potcar_path = self._get_filepath(path, "POTCAR")
+ self._aeccar0_path = self._get_filepath(path, "AECCAR0")
+ self._aeccar2_path = self._get_filepath(path, "AECCAR2")
if run_chargemol and not (
self._chgcar_path and self._potcar_path and self._aeccar0_path and self._aeccar2_path
@@ -177,7 +177,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, mpi=False, ncores: int | None = None, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -189,7 +189,7 @@ def _execute_chargemol(self, mpi=False, ncores: int | None = None, **jobcontrol_
Default: None.
mpi(bool): Whether run the Chargemol in a parallel way. Default is False.
- ncores (int): The number of cores you want to use.
+ ncores (str): The number of cores you want to use.
Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
or multiprocessing.cpu_count().
jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
@@ -199,7 +199,7 @@ def _execute_chargemol(self, mpi=False, ncores: int | None = None, **jobcontrol_
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
ncores = os.getenv("SLURM_CPUS_ON_NODE") or os.getenv("SLURM_NTASKS")
- if ncores:
+ if not ncores:
ncores = multiprocessing.cpu_count()
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
@@ -208,14 +208,15 @@ def _execute_chargemol(self, mpi=False, ncores: int | None = None, **jobcontrol_
if self.save:
save_path = Path(Path.cwd(), "charge")
save_path.mkdir(parents=True, exist_ok=True)
- source = [Path(self._chgcarpath), Path(self._potcarpath), Path(self._aeccar0path), Path(self._aeccar2path)]
+ source = [Path(self._chgcar_path), Path(self._potcar_path), Path(self._aeccar0_path), Path(self._aeccar2_path)]
links = [
Path(save_path, "CHGCAR"),
Path(save_path, "POTCAR"),
Path(save_path, "AECCAR0"),
Path(save_path, "AECCAR2"),
]
- [links[i].symlink_to(source[i]) for i in range(len(links))]
+ for link, src in zip(links, source):
+ link.symlink_to(src)
# write job_script file:
self._write_jobscript_for_chargemol(write_path=str(save_path) + "/job_control.txt", **jobcontrol_kwargs)
@@ -228,18 +229,21 @@ def _execute_chargemol(self, mpi=False, ncores: int | None = None, **jobcontrol_
cwd=save_path,
) as rs:
rs.communicate()
+
+ self._from_data_dir(chargemol_output_path = str(save_path))
else:
with ScratchDir("."):
cwd = Path.cwd()
source = [
- Path(self._chgcarpath),
- Path(self._potcarpath),
- Path(self._aeccar0path),
- Path(self._aeccar2path),
+ Path(self._chgcar_path),
+ Path(self._potcar_path),
+ Path(self._aeccar0_path),
+ Path(self._aeccar2_path),
]
links = [Path(cwd, "CHGCAR"), Path(cwd, "POTCAR"), Path(cwd, "AECCAR0"), Path(cwd, "AECCAR2")]
- [links[i].symlink_to(source[i]) for i in range(len(links))]
+ for link, src in zip(links, source):
+ link.symlink_to(src)
# write job_script file:
self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
From 92872b0a85c4deb1f5625cc7ea42b54a7ed8b60f Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:30:26 +0000
Subject: [PATCH 18/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 808cd404a07..1fc88e063b5 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -177,7 +177,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -208,7 +208,12 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol
if self.save:
save_path = Path(Path.cwd(), "charge")
save_path.mkdir(parents=True, exist_ok=True)
- source = [Path(self._chgcar_path), Path(self._potcar_path), Path(self._aeccar0_path), Path(self._aeccar2_path)]
+ source = [
+ Path(self._chgcar_path),
+ Path(self._potcar_path),
+ Path(self._aeccar0_path),
+ Path(self._aeccar2_path),
+ ]
links = [
Path(save_path, "CHGCAR"),
Path(save_path, "POTCAR"),
@@ -229,8 +234,8 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol
cwd=save_path,
) as rs:
rs.communicate()
-
- self._from_data_dir(chargemol_output_path = str(save_path))
+
+ self._from_data_dir(chargemol_output_path=str(save_path))
else:
with ScratchDir("."):
From 1c243d3a51119942710b456b96b1d1f216b11ddd Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:38:13 +0000
Subject: [PATCH 19/37] Shorten the line, remove the whitespace
---
pymatgen/command_line/chargemol_caller.py | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 1fc88e063b5..22996e4e5ee 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -209,11 +209,17 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
save_path = Path(Path.cwd(), "charge")
save_path.mkdir(parents=True, exist_ok=True)
source = [
+
Path(self._chgcar_path),
+
Path(self._potcar_path),
+
Path(self._aeccar0_path),
+
Path(self._aeccar2_path),
+ ,
]
+
links = [
Path(save_path, "CHGCAR"),
Path(save_path, "POTCAR"),
@@ -223,7 +229,8 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
for link, src in zip(links, source):
link.symlink_to(src)
# write job_script file:
- self._write_jobscript_for_chargemol(write_path=str(save_path) + "/job_control.txt", **jobcontrol_kwargs)
+ write_path = str(save_path) + "/job_control.txt"
+ self._write_jobscript_for_chargemol(write_path=write_path, **jobcontrol_kwargs)
# Run Chargemol
with subprocess.Popen(
@@ -234,8 +241,8 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
cwd=save_path,
) as rs:
rs.communicate()
-
- self._from_data_dir(chargemol_output_path=str(save_path))
+
+ self._from_data_dir(chargemol_output_path = str(save_path))
else:
with ScratchDir("."):
From 88cba347ae34e575f0bd24a8cc7bfd7cf41b8000 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:34:39 +0000
Subject: [PATCH 20/37] Shorten the line, remove the whitespace
---
pymatgen/command_line/chargemol_caller.py | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 22996e4e5ee..5ded6096d11 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -177,7 +177,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -209,15 +209,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
save_path = Path(Path.cwd(), "charge")
save_path.mkdir(parents=True, exist_ok=True)
source = [
-
- Path(self._chgcar_path),
-
- Path(self._potcar_path),
-
- Path(self._aeccar0_path),
-
+ Path(self._chgcar_path),
+ Path(self._potcar_path),
+ Path(self._aeccar0_path),
Path(self._aeccar2_path),
- ,
]
links = [
@@ -241,9 +236,8 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
cwd=save_path,
) as rs:
rs.communicate()
-
self._from_data_dir(chargemol_output_path = str(save_path))
-
+
else:
with ScratchDir("."):
cwd = Path.cwd()
From 4ca657ff4d903dc791d58a77dc8349a8836007c3 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:41:01 +0000
Subject: [PATCH 21/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 5ded6096d11..5311a88bc25 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -177,7 +177,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -209,9 +209,9 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol
save_path = Path(Path.cwd(), "charge")
save_path.mkdir(parents=True, exist_ok=True)
source = [
- Path(self._chgcar_path),
- Path(self._potcar_path),
- Path(self._aeccar0_path),
+ Path(self._chgcar_path),
+ Path(self._potcar_path),
+ Path(self._aeccar0_path),
Path(self._aeccar2_path),
]
@@ -236,8 +236,8 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol
cwd=save_path,
) as rs:
rs.communicate()
- self._from_data_dir(chargemol_output_path = str(save_path))
-
+ self._from_data_dir(chargemol_output_path=str(save_path))
+
else:
with ScratchDir("."):
cwd = Path.cwd()
From f773a3ecc4e8ff2bf427e13c3b1dcc77de28eec8 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:43:48 +0000
Subject: [PATCH 22/37] delete trailing whitespace and black line contains
whitespace
---
pymatgen/command_line/chargemol_caller.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 5311a88bc25..e26c74859a9 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -236,8 +236,8 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
cwd=save_path,
) as rs:
rs.communicate()
- self._from_data_dir(chargemol_output_path=str(save_path))
-
+ self._from_data_dir(chargemol_output_path = str(save_path))
+
else:
with ScratchDir("."):
cwd = Path.cwd()
From cb10c905bfa211a62fed8a064787a71044970a01 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:41:21 +0000
Subject: [PATCH 23/37] delete trailing whitespace and black line contains
whitespace
---
pymatgen/command_line/chargemol_caller.py | 653 +++++++++++++++++++++-
1 file changed, 652 insertions(+), 1 deletion(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index e26c74859a9..a15b697aa35 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -182,6 +182,658 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
Internal function to run Chargemol.
+ Args:
+ atomic_densities_path (str): Path to the atomic densities directory
+ required by Chargemol. If None, Pymatgen assumes that this is
+ defined in a "DDEC6_ATOMIC_DENSITIES_DIR" environment variable.
+ Default: None.
+
+ mpi(bool): Whether run the Chargemol in a parallel way. Default is False.
+ ncores (str): The number of cores you want to use.
+ Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
+ or multiprocessing.cpu_count().
+ jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
+ """
+ if mpi:
+ if ncores:
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
+ else:
+ ncores = os.getenv("SLURM_CPUS_ON_NODE") or os.getenv("SLURM_NTASKS")
+ if not ncores:
+ ncores = multiprocessing.cpu_count()
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
+ else:
+ CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
+
+ if self.save:
+ save_path = Path(Path.cwd(), "charge")
+ save_path.mkdir(parents=True, exist_ok=True)
+ source = [
+ Path(self._chgcar_path),
+ Path(self._potcar_path),
+ Path(self._aeccar0_path),
+ Path(self._aeccar2_path),
+ ]
+
+ links = [
+ Path(save_path, "CHGCAR"),
+ Path(save_path, "POTCAR"),
+ Path(save_path, "AECCAR0"),
+ Path(save_path, "AECCAR2"),
+ ]
+ for link, src in zip(links, source):
+ link.symlink_to(src)
+ # write job_script file:
+ write_path = str(save_path) + "/job_control.txt"
+ self._write_jobscript_for_chargemol(write_path=write_path, **jobcontrol_kwargs)
+
+ # Run Chargemol
+ with subprocess.Popen(
+ CHARGEMOLEXE,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ close_fds=True,
+ cwd=save_path,
+ ) as rs:
+ rs.communicate()
+ self._from_data_dir(chargemol_output_path=str(save_path))
+
+ else:
+ with ScratchDir("."):
+ cwd = Path.cwd()
+ source = [
+ Path(self._chgcar_path),
+ Path(self._potcar_path),
+ Path(self._aeccar0_path),
+ Path(self._aeccar2_path),
+ ]
+ links = [Path(cwd, "CHGCAR"), Path(cwd, "POTCAR"), Path(cwd, "AECCAR0"), Path(cwd, "AECCAR2")]
+ for link, src in zip(links, source):
+ link.symlink_to(src)
+ # write job_script file:
+ self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
+
+ # Run Chargemol
+ with subprocess.Popen(
+ CHARGEMOLEXE,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ close_fds=True,
+ ) as rs:
+ rs.communicate()
+ if rs.returncode != 0:
+ raise RuntimeError(
+ f"Chargemol exited with return code {int(rs.returncode)}. "
+ "Please check your Chargemol installation."
+ )
+
+ self._from_data_dir()
+
+ def _from_data_dir(self, chargemol_output_path: str | None = None):
+ """
+ Internal command to parse Chargemol files from a directory.
+
+ Args:
+ chargemol_output_path (str): Path to the folder containing
+ the Chargemol output files.
+ Default: None (current working directory).
+ """
+ if chargemol_output_path is None:
+ chargemol_output_path = "."
+
+ charge_path = f"{chargemol_output_path}/DDEC6_even_tempered_net_atomic_charges.xyz"
+ self.ddec_charges = self._get_data_from_xyz(charge_path)
+ self.dipoles = self._get_dipole_info(charge_path)
+
+ bond_order_path = f"{chargemol_output_path}/DDEC6_even_tempered_bond_orders.xyz"
+ if os.path.exists(bond_order_path):
+ self.bond_order_sums = self._get_data_from_xyz(bond_order_path)
+ self.bond_order_dict = self._get_bond_order_info(bond_order_path)
+ else:
+ self.bond_order_sums = self.bond_order_dict = None
+
+ spin_moment_path = f"{chargemol_output_path}/DDEC6_even_tempered_atomic_spin_moments.xyz"
+ if os.path.exists(spin_moment_path):
+ self.ddec_spin_moments = self._get_data_from_xyz(spin_moment_path)
+ else:
+ self.ddec_spin_moments = None
+
+ rsquared_path = f"{chargemol_output_path}/DDEC_atomic_Rsquared_moments.xyz"
+ if os.path.exists(rsquared_path):
+ self.ddec_rsquared_moments = self._get_data_from_xyz(rsquared_path)
+ else:
+ self.ddec_rsquared_moments = None
+
+ rcubed_path = f"{chargemol_output_path}/DDEC_atomic_Rcubed_moments.xyz"
+ if os.path.exists(rcubed_path):
+ self.ddec_rcubed_moments = self._get_data_from_xyz(rcubed_path)
+ else:
+ self.ddec_rcubed_moments = None
+
+ rfourth_path = f"{chargemol_output_path}/DDEC_atomic_Rfourth_moments.xyz"
+ if os.path.exists(rfourth_path):
+ self.ddec_rfourth_moments = self._get_data_from_xyz(rfourth_path)
+ else:
+ self.ddec_rfourth_moments = None
+
+ ddec_analysis_path = f"{chargemol_output_path}/VASP_DDEC_analysis.output"
+ if os.path.exists(ddec_analysis_path):
+ self.cm5_charges = self._get_cm5_data_from_output(ddec_analysis_path)
+ else:
+ self.cm5_charges = None
+
+ def get_charge_transfer(self, atom_index, charge_type="ddec"):
+ """Returns the charge transferred for a particular atom. A positive value means
+ that the site has gained electron density (i.e. exhibits anionic character)
+ whereas a negative value means the site has lost electron density (i.e. exhibits
+ cationic character). This is the same thing as the negative of the partial atomic
+ charge.
+
+ Args:
+ atom_index (int): Index of atom to get charge transfer for.
+ charge_type (str): Type of charge to use ("ddec" or "cm5").
+
+ Returns:
+ float: charge transferred at atom_index
+ """
+ if charge_type.lower() not in ["ddec", "cm5"]:
+ raise ValueError(f"Invalid {charge_type=}")
+ if charge_type.lower() == "ddec":
+ charge_transfer = -self.ddec_charges[atom_index]
+ elif charge_type.lower() == "cm5":
+ charge_transfer = -self.cm5_charges[atom_index]
+ return charge_transfer
+
+ def get_charge(self, atom_index, nelect: int | None = None, charge_type="ddec"):
+ """Convenience method to get the charge on a particular atom using the same
+ sign convention as the BaderAnalysis. Note that this is *not* the partial
+ atomic charge. This value is nelect (e.g. ZVAL from the POTCAR) + the
+ charge transferred. If you want the partial atomic charge, use
+ get_partial_charge().
+
+ Args:
+ atom_index (int): Index of atom to get charge for.
+ nelect (int): number of electrons associated with an isolated atom at this index.
+ For most DFT codes this corresponds to the number of valence electrons
+ associated with the pseudopotential. If None, this value will be automatically
+ obtained from the POTCAR (if present).
+ Default: None.
+ charge_type (str): Type of charge to use ("ddec" or "cm5").
+
+ Returns:
+ float: charge on atom_index
+ """
+ if nelect:
+ charge = nelect + self.get_charge_transfer(atom_index, charge_type=charge_type)
+ elif self.potcar and self.natoms:
+ charge = None
+ potcar_indices = []
+ for i, v in enumerate(self.natoms):
+ potcar_indices += [i] * v
+ nelect = self.potcar[potcar_indices[atom_index]].nelectrons
+ charge = nelect + self.get_charge_transfer(atom_index, charge_type=charge_type)
+ else:
+ charge = None
+ return charge
+
+ def get_partial_charge(self, atom_index, charge_type="ddec"):
+ """Convenience method to get the partial atomic charge on a particular atom.
+ This is the value printed in the Chargemol analysis.
+
+ Args:
+ atom_index (int): Index of atom to get charge for.
+ charge_type (str): Type of charge to use ("ddec" or "cm5").
+ """
+ if charge_type.lower() not in ["ddec", "cm5"]:
+ raise ValueError(f"Invalid charge_type: {charge_type}")
+ if charge_type.lower() == "ddec":
+ partial_charge = self.ddec_charges[atom_index]
+ elif charge_type.lower() == "cm5":
+ partial_charge = self.cm5_charges[atom_index]
+ return partial_charge
+
+ def get_bond_order(self, index_from, index_to):
+ """Convenience method to get the bond order between two atoms.
+
+ Args:
+ index_from (int): Index of atom to get bond order from.
+ index_to (int): Index of atom to get bond order to.
+
+ Returns:
+ float: bond order between atoms
+ """
+ bonded_set = self.bond_order_dict[index_from]["bonded_to"]
+ bond_orders = [v["bond_order"] for v in bonded_set if v["index"] == index_to]
+ return 0.0 if bond_orders == [] else np.sum(bond_orders)
+
+ def _write_jobscript_for_chargemol(
+ self,
+ net_charge=0.0,
+ periodicity=(True, True, True),
+ method="ddec6",
+ compute_bond_orders=True,
+ write_path: str = "job_control.txt",
+ ):
+ """Writes job_script.txt for Chargemol execution.
+
+ Args:
+ net_charge (float): Net charge of the system.
+ Defaults to 0.0.
+ periodicity (tuple[bool]): Periodicity of the system.
+ Default: (True, True, True).
+ method (str): Method to use for the analysis. Options include "ddec6"
+ and "ddec3". Default: "ddec6"
+ compute_bond_orders (bool): Whether to compute bond orders. Default: True.
+ write_path (str): The path of output files of chargemol if you want to save them.
+ """
+ self.net_charge = net_charge
+ self.periodicity = periodicity
+ self.method = method
+
+ lines = ""
+
+ # Net Charge
+ if net_charge:
+ lines += f"\n{net_charge}\n\n"
+
+ # Periodicity
+ if periodicity:
+ per_a = ".true." if periodicity[0] else ".false."
+ per_b = ".true." if periodicity[1] else ".false."
+ per_c = ".true." if periodicity[2] else ".false."
+ lines += (
+ f"\n{per_a}\n{per_b}\n{per_c}\n"
+ "\n"
+ )
+
+ # atomic_densities dir
+ atomic_densities_path = self._atomic_densities_path or os.getenv("DDEC6_ATOMIC_DENSITIES_DIR")
+ if atomic_densities_path is None:
+ raise OSError(
+ "The DDEC6_ATOMIC_DENSITIES_DIR environment variable must be set or the atomic_densities_path must"
+ " be specified"
+ )
+ if not os.path.exists(atomic_densities_path):
+ raise FileNotFoundError(f"{atomic_densities_path=} does not exist")
+
+ # This is to fix a Chargemol filepath nuance
+ if os.name == "nt": # Windows
+ if atomic_densities_path[-1] != "\\":
+ atomic_densities_path += "\\"
+ elif atomic_densities_path[-1] != "/":
+ atomic_densities_path += "/"
+
+ lines += (
+ f"\n\n{atomic_densities_path}\n\n"
+ )
+
+ # Charge type
+ lines += f"\n\n{method.upper()}\n\n"
+
+ if compute_bond_orders:
+ bo = ".true." if compute_bond_orders else ".false."
+ lines += f"\n\n{bo}\n\n"
+
+ with open(write_path, "w") as fh:
+ fh.write(lines)
+
+ @staticmethod
+ def _get_dipole_info(filepath):
+ """Internal command to process dipoles.
+
+ Args:
+ filepath (str): The path to the DDEC6_even_tempered_net_atomic_charges.xyz file
+ """
+ idx = 0
+ start = False
+ dipoles = []
+ with open(filepath) as r:
+ for line in r:
+ if "The following XYZ" in line:
+ start = True
+ idx += 1
+ continue
+ if start and line.strip() == "":
+ break
+ if idx >= 2:
+ dipoles.append([float(d) for d in line.strip().split()[7:10]])
+ if start:
+ idx += 1
+
+ return dipoles
+
+ @staticmethod
+ def _get_bond_order_info(filename):
+ """Internal command to process pairwise bond order information.
+
+ Args:
+ filename (str): The path to the DDEC6_even_tempered_bond_orders.xyz file
+ """
+ # Get where relevant info for each atom starts
+ bond_order_info = {}
+
+ with open(filename) as r:
+ for line in r:
+ split = line.strip().split()
+ if "Printing BOs" in line:
+ start_idx = int(split[5]) - 1
+ start_el = Element(split[7])
+ bond_order_info[start_idx] = {"element": start_el, "bonded_to": []}
+ elif "Bonded to the" in line:
+ direction = tuple(int(i.split(")")[0].split(",")[0]) for i in split[4:7])
+ end_idx = int(split[12]) - 1
+ end_el = Element(split[14])
+ bo = float(split[20])
+ spin_bo = float(split[-1])
+ bonded_to = {
+ "index": end_idx,
+ "element": end_el,
+ "bond_order": bo,
+ "direction": direction,
+ "spin_polarization": spin_bo,
+ }
+ bond_order_info[start_idx]["bonded_to"].append(bonded_to)
+ elif "The sum of bond orders for this atom" in line:
+ bond_order_info[start_idx]["bond_order_sum"] = float(split[-1])
+
+ return bond_order_info
+
+ def get_property_decorated_structure(self):
+ """Takes CHGCAR's structure object and updates it with properties
+ from the Chargemol analysis.
+
+ Returns:
+ Pymatgen structure with site properties added
+ """
+ struct = self.structure.copy()
+ struct.add_site_property("partial_charge_ddec6", self.ddec_charges)
+ if self.dipoles:
+ struct.add_site_property("dipole_ddec6", self.dipoles)
+ if self.bond_order_sums:
+ struct.add_site_property("bond_order_sum_ddec6", self.bond_order_sums)
+ if self.ddec_spin_moments:
+ struct.add_site_property("spin_moment_ddec6", self.ddec_spin_moments)
+ if self.cm5_charges:
+ struct.add_site_property("partial_charge_cm5", self.cm5_charges)
+ return struct
+
+ @property
+ def summary(self):
+ """Returns a dictionary summary of the Chargemol analysis
+ {
+ "ddec": {
+ "partial_charges": list[float],
+ "spin_moments": list[float],
+ "dipoles": list[float],
+ "rsquared_moments": list[float],
+ "rcubed_moments": list[float],
+ "rfourth_moments": list[float],
+ "bond_order_dict": dict
+ },
+ "cm5": {
+ "partial_charges": list[float],
+ }
+ }.
+ """
+ summary = {}
+ ddec_summary = {"partial_charges": self.ddec_charges}
+ if self.bond_order_sums:
+ ddec_summary["bond_order_sums"] = self.bond_order_sums
+ if self.ddec_spin_moments:
+ ddec_summary["spin_moments"] = self.ddec_spin_moments
+ if self.dipoles:
+ ddec_summary["dipoles"] = self.dipoles
+ if self.ddec_rsquared_moments:
+ ddec_summary["rsquared_moments"] = self.ddec_rsquared_moments
+ if self.ddec_rcubed_moments:
+ ddec_summary["rcubed_moments"] = self.ddec_rcubed_moments
+ if self.ddec_rfourth_moments:
+ ddec_summary["rfourth_moments"] = self.ddec_rfourth_moments
+ if self.bond_order_dict:
+ ddec_summary["bond_order_dict"] = self.bond_order_dict
+
+ cm5_summary = {"partial_charges": self.cm5_charges} if self.cm5_charges else None
+
+ summary["ddec"] = ddec_summary
+ summary["cm5"] = cm5_summary
+
+ return summary
+
+ @staticmethod
+ def _get_data_from_xyz(xyz_path):
+ """Internal command to process Chargemol XYZ files.
+
+ Args:
+ xyz_path (str): Path to XYZ file
+
+ Returns:
+ list[float]: site-specific properties
+ """
+ props = []
+ if os.path.exists(xyz_path):
+ with open(xyz_path) as r:
+ for i, line in enumerate(r):
+ if i <= 1:
+ continue
+ if line.strip() == "":
+ break
+ props.append(float(line.split()[-1]))
+ else:
+ raise FileNotFoundError(f"{xyz_path} not found")
+
+ return props
+
+ @staticmethod
+ def _get_cm5_data_from_output(ddec_analysis_path):
+ """Internal command to process Chargemol CM5 data.
+
+ Args:
+ ddec_analysis_path (str): Path VASP_DDEC_analysis.output file
+
+ Returns:
+ list[float]: CM5 charges
+ """
+ props = []
+ if os.path.exists(ddec_analysis_path):
+ start = False
+ with open(ddec_analysis_path) as r:
+ for line in r:
+ if "computed CM5" in line:
+ start = True
+ continue
+ if "Hirshfeld and CM5" in line:
+ break
+ if start:
+ vals = line.split()
+ props.extend([float(c) for c in [val.strip() for val in vals]])
+ else:
+ raise FileNotFoundError(f"{ddec_analysis_path} not found")
+ return props
+"""This module implements an interface to Thomas Manz's Chargemol code
+https://sourceforge.net/projects/ddec for calculating DDEC3, DDEC6, and CM5 population analyses.
+
+This module depends on a compiled chargemol executable being available in the path.
+If you use this module, please cite the following based on which modules you use:
+
+Chargemol:
+(1) T. A. Manz and N. Gabaldon Limas, Chargemol program for performing DDEC analysis,
+Version 3.5, 2017, ddec.sourceforge.net.
+
+DDEC6 Charges:
+(1) T. A. Manz and N. Gabaldon Limas, “Introducing DDEC6 atomic population analysis:
+part 1. Charge partitioning theory and methodology,” RSC Adv., 6 (2016) 47771-47801.
+(2) N. Gabaldon Limas and T. A. Manz, “Introducing DDEC6 atomic population analysis:
+part 2. Computed results for a wide range of periodic and nonperiodic materials,”
+(3) N. Gabaldon Limas and T. A. Manz, “Introducing DDEC6 atomic population analysis:
+part 4. Efficient parallel computation of net atomic charges, atomic spin moments,
+bond orders, and more,” RSC Adv., 8 (2018) 2678-2707.
+
+CM5 Charges:
+(1) A.V. Marenich, S.V. Jerome, C.J. Cramer, D.G. Truhlar, "Charge Model 5: An Extension
+of Hirshfeld Population Analysis for the Accurate Description of Molecular Interactions
+in Gaseous and Condensed Phases", J. Chem. Theory. Comput., 8 (2012) 527-541.
+
+Spin Moments:
+(1) T. A. Manz and D. S. Sholl, “Methods for Computing Accurate Atomic Spin Moments for
+Collinear and Noncollinear Magnetism in Periodic and Nonperiodic Materials,”
+J. Chem. Theory Comput. 7 (2011) 4146-4164.
+
+Bond Orders:
+(1) “Introducing DDEC6 atomic population analysis: part 3. Comprehensive method to compute
+bond orders,” RSC Adv., 7 (2017) 45552-45581.
+
+DDEC3 Charges:
+(1) T. A. Manz and D. S. Sholl, “Improved Atoms-in-Molecule Charge Partitioning Functional
+for Simultaneously Reproducing the Electrostatic Potential and Chemical States in Periodic
+and Non-Periodic Materials,” J. Chem. Theory Comput. 8 (2012) 2844-2867.
+(2) T. A. Manz and D. S. Sholl, “Chemically Meaningful Atomic Charges that Reproduce the
+Electrostatic Potential in Periodic and Nonperiodic Materials,” J. Chem. Theory Comput. 6
+(2010) 2455-2468.
+"""
+
+from __future__ import annotations
+
+import multiprocessing
+import os
+import subprocess
+import warnings
+from glob import glob
+from pathlib import Path
+from shutil import which
+
+import numpy as np
+from monty.tempfile import ScratchDir
+
+from pymatgen.core import Element
+from pymatgen.io.vasp.inputs import Potcar
+from pymatgen.io.vasp.outputs import Chgcar
+
+__author__ = "Martin Siron, Andrew S. Rosen"
+__version__ = "0.1"
+__maintainer__ = "Shyue Ping Ong"
+__email__ = "shyuep@gmail.com"
+__date__ = "01/18/21"
+
+CHARGEMOL_EXE = (
+ which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
+)
+
+
+class ChargemolAnalysis:
+ """Chargemol analysis for DDEC3, DDEC6, and/or CM5 population analyses,
+ including the calculation of partial atomic charges, atomic spin moments,
+ bond orders, and related properties.
+ """
+
+ CHARGEMOLEXE = (
+ which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
+ )
+
+ def __init__(
+ self,
+ path: str | None = None,
+ atomic_densities_path=None,
+ run_chargemol: bool = True,
+ mpi: bool = False,
+ ncores: int | None = None,
+ save: bool = False,
+ ):
+ """
+ Initializes the Chargemol Analysis.
+
+
+ Args:
+ path (str): Path to the CHGCAR, POTCAR, AECCAR0, and AECCAR files.
+ The files can be gzipped or not. Default: None (current working directory).
+ atomic_densities_path (str | None): Path to the atomic densities directory
+ required by Chargemol. If None, Pymatgen assumes that this is
+ defined in a "DDEC6_ATOMIC_DENSITIES_DIR" environment variable.
+ Only used if run_chargemol is True. Default: None.
+ run_chargemol (bool): Whether to run the Chargemol analysis. If False,
+ the existing Chargemol output files will be read from path.
+ Default: True.
+ mpi (bool): Whether to run the Chargemol in a parallel way.
+ ncores: Use how many cores to run the Chargemol!
+ Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE'),
+ or os.environ.get('SLURM_CPUS_ON_NODE')", or "multiprocessing.cpu_count()".
+ Take your own risk! This default value might not suit you!
+ You'd better set your own number!!!
+ save: save (bool): Whether to save the Chargemol output files. Default is False.
+ the existing Chargemol output files will be read from path. Default: True.
+ """
+ path = path or os.getcwd()
+ if run_chargemol and not CHARGEMOL_EXE:
+ raise OSError(
+ "ChargemolAnalysis requires the Chargemol executable to be in PATH."
+ " Please download the library at https://sourceforge.net/projects/ddec/files"
+ "and follow the instructions."
+ )
+ if atomic_densities_path == "":
+ atomic_densities_path = os.getcwd()
+ self._atomic_densities_path = atomic_densities_path
+ self.save = save
+
+ self._chgcar_path = self._get_filepath(path, "CHGCAR")
+ self._potcar_path = self._get_filepath(path, "POTCAR")
+ self._aeccar0_path = self._get_filepath(path, "AECCAR0")
+ self._aeccar2_path = self._get_filepath(path, "AECCAR2")
+
+ if run_chargemol and not (
+ self._chgcar_path and self._potcar_path and self._aeccar0_path and self._aeccar2_path
+ ):
+ raise FileNotFoundError("CHGCAR, AECCAR0, AECCAR2, and POTCAR are all needed for Chargemol.")
+ if self._chgcar_path:
+ self.chgcar = Chgcar.from_file(self._chgcar_path)
+ self.structure = self.chgcar.structure
+ self.natoms = self.chgcar.poscar.natoms
+ else:
+ self.chgcar = self.structure = self.natoms = None
+ warnings.warn("No CHGCAR found. Some properties may be unavailable.", UserWarning)
+ if self._potcar_path:
+ self.potcar = Potcar.from_file(self._potcar_path)
+ else:
+ warnings.warn("No POTCAR found. Some properties may be unavailable.", UserWarning)
+ self.aeccar0 = Chgcar.from_file(self._aeccar0_path) if self._aeccar0_path else None
+ self.aeccar2 = Chgcar.from_file(self._aeccar2_path) if self._aeccar2_path else None
+
+ if run_chargemol:
+ self._execute_chargemol(mpi=mpi, ncores=ncores)
+ else:
+ self._from_data_dir(chargemol_output_path=path)
+
+ @staticmethod
+ def _get_filepath(path, filename, suffix=""):
+ """Returns the full path to the filename in the path. Works even if the file has
+ a .gz extension.
+
+ Args:
+ path (str): Path to the file.
+ filename (str): Filename.
+ suffix (str): Optional suffix at the end of the filename.
+
+ Returns:
+ str: Absolute path to the file.
+ """
+ name_pattern = f"{filename}{suffix}*" if filename != "POTCAR" else f"{filename}*"
+ paths = glob(os.path.join(path, name_pattern))
+ fpath = None
+ if len(paths) >= 1:
+ # using reverse=True because, if multiple files are present,
+ # they likely have suffixes 'static', 'relax', 'relax2', etc.
+ # and this would give 'static' over 'relax2' over 'relax'
+ # however, better to use 'suffix' kwarg to avoid this!
+ paths.sort(reverse=True)
+ # warning_msg = f"Multiple files detected, using {os.path.basename(paths[0])}" if len(paths) > 1 else None
+ # warnings.warn(warning_msg)
+ fpath = paths[0]
+ return fpath
+
+ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
+ """
+ Internal function to run Chargemol.
+
+
Args:
atomic_densities_path (str): Path to the atomic densities directory
required by Chargemol. If None, Pymatgen assumes that this is
@@ -237,7 +889,6 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
) as rs:
rs.communicate()
self._from_data_dir(chargemol_output_path = str(save_path))
-
else:
with ScratchDir("."):
cwd = Path.cwd()
From 841c728823e1c821048f830f93a58452bf5eb5d4 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:45:17 +0000
Subject: [PATCH 24/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index a15b697aa35..629d7195d99 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -650,6 +650,8 @@ def _get_cm5_data_from_output(ddec_analysis_path):
else:
raise FileNotFoundError(f"{ddec_analysis_path} not found")
return props
+
+
"""This module implements an interface to Thomas Manz's Chargemol code
https://sourceforge.net/projects/ddec for calculating DDEC3, DDEC6, and CM5 population analyses.
@@ -829,7 +831,7 @@ def _get_filepath(path, filename, suffix=""):
fpath = paths[0]
return fpath
- def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
+ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
"""
Internal function to run Chargemol.
@@ -888,7 +890,7 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol
cwd=save_path,
) as rs:
rs.communicate()
- self._from_data_dir(chargemol_output_path = str(save_path))
+ self._from_data_dir(chargemol_output_path=str(save_path))
else:
with ScratchDir("."):
cwd = Path.cwd()
From db4eeb2938fd607d8bfca031cf3c8c52e9cc2548 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 00:49:04 +0000
Subject: [PATCH 25/37] Delete the wrong lines
---
pymatgen/command_line/chargemol_caller.py | 653 ----------------------
1 file changed, 653 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 629d7195d99..5311a88bc25 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -650,656 +650,3 @@ def _get_cm5_data_from_output(ddec_analysis_path):
else:
raise FileNotFoundError(f"{ddec_analysis_path} not found")
return props
-
-
-"""This module implements an interface to Thomas Manz's Chargemol code
-https://sourceforge.net/projects/ddec for calculating DDEC3, DDEC6, and CM5 population analyses.
-
-This module depends on a compiled chargemol executable being available in the path.
-If you use this module, please cite the following based on which modules you use:
-
-Chargemol:
-(1) T. A. Manz and N. Gabaldon Limas, Chargemol program for performing DDEC analysis,
-Version 3.5, 2017, ddec.sourceforge.net.
-
-DDEC6 Charges:
-(1) T. A. Manz and N. Gabaldon Limas, “Introducing DDEC6 atomic population analysis:
-part 1. Charge partitioning theory and methodology,” RSC Adv., 6 (2016) 47771-47801.
-(2) N. Gabaldon Limas and T. A. Manz, “Introducing DDEC6 atomic population analysis:
-part 2. Computed results for a wide range of periodic and nonperiodic materials,”
-(3) N. Gabaldon Limas and T. A. Manz, “Introducing DDEC6 atomic population analysis:
-part 4. Efficient parallel computation of net atomic charges, atomic spin moments,
-bond orders, and more,” RSC Adv., 8 (2018) 2678-2707.
-
-CM5 Charges:
-(1) A.V. Marenich, S.V. Jerome, C.J. Cramer, D.G. Truhlar, "Charge Model 5: An Extension
-of Hirshfeld Population Analysis for the Accurate Description of Molecular Interactions
-in Gaseous and Condensed Phases", J. Chem. Theory. Comput., 8 (2012) 527-541.
-
-Spin Moments:
-(1) T. A. Manz and D. S. Sholl, “Methods for Computing Accurate Atomic Spin Moments for
-Collinear and Noncollinear Magnetism in Periodic and Nonperiodic Materials,”
-J. Chem. Theory Comput. 7 (2011) 4146-4164.
-
-Bond Orders:
-(1) “Introducing DDEC6 atomic population analysis: part 3. Comprehensive method to compute
-bond orders,” RSC Adv., 7 (2017) 45552-45581.
-
-DDEC3 Charges:
-(1) T. A. Manz and D. S. Sholl, “Improved Atoms-in-Molecule Charge Partitioning Functional
-for Simultaneously Reproducing the Electrostatic Potential and Chemical States in Periodic
-and Non-Periodic Materials,” J. Chem. Theory Comput. 8 (2012) 2844-2867.
-(2) T. A. Manz and D. S. Sholl, “Chemically Meaningful Atomic Charges that Reproduce the
-Electrostatic Potential in Periodic and Nonperiodic Materials,” J. Chem. Theory Comput. 6
-(2010) 2455-2468.
-"""
-
-from __future__ import annotations
-
-import multiprocessing
-import os
-import subprocess
-import warnings
-from glob import glob
-from pathlib import Path
-from shutil import which
-
-import numpy as np
-from monty.tempfile import ScratchDir
-
-from pymatgen.core import Element
-from pymatgen.io.vasp.inputs import Potcar
-from pymatgen.io.vasp.outputs import Chgcar
-
-__author__ = "Martin Siron, Andrew S. Rosen"
-__version__ = "0.1"
-__maintainer__ = "Shyue Ping Ong"
-__email__ = "shyuep@gmail.com"
-__date__ = "01/18/21"
-
-CHARGEMOL_EXE = (
- which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
-)
-
-
-class ChargemolAnalysis:
- """Chargemol analysis for DDEC3, DDEC6, and/or CM5 population analyses,
- including the calculation of partial atomic charges, atomic spin moments,
- bond orders, and related properties.
- """
-
- CHARGEMOLEXE = (
- which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
- )
-
- def __init__(
- self,
- path: str | None = None,
- atomic_densities_path=None,
- run_chargemol: bool = True,
- mpi: bool = False,
- ncores: int | None = None,
- save: bool = False,
- ):
- """
- Initializes the Chargemol Analysis.
-
-
- Args:
- path (str): Path to the CHGCAR, POTCAR, AECCAR0, and AECCAR files.
- The files can be gzipped or not. Default: None (current working directory).
- atomic_densities_path (str | None): Path to the atomic densities directory
- required by Chargemol. If None, Pymatgen assumes that this is
- defined in a "DDEC6_ATOMIC_DENSITIES_DIR" environment variable.
- Only used if run_chargemol is True. Default: None.
- run_chargemol (bool): Whether to run the Chargemol analysis. If False,
- the existing Chargemol output files will be read from path.
- Default: True.
- mpi (bool): Whether to run the Chargemol in a parallel way.
- ncores: Use how many cores to run the Chargemol!
- Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE'),
- or os.environ.get('SLURM_CPUS_ON_NODE')", or "multiprocessing.cpu_count()".
- Take your own risk! This default value might not suit you!
- You'd better set your own number!!!
- save: save (bool): Whether to save the Chargemol output files. Default is False.
- the existing Chargemol output files will be read from path. Default: True.
- """
- path = path or os.getcwd()
- if run_chargemol and not CHARGEMOL_EXE:
- raise OSError(
- "ChargemolAnalysis requires the Chargemol executable to be in PATH."
- " Please download the library at https://sourceforge.net/projects/ddec/files"
- "and follow the instructions."
- )
- if atomic_densities_path == "":
- atomic_densities_path = os.getcwd()
- self._atomic_densities_path = atomic_densities_path
- self.save = save
-
- self._chgcar_path = self._get_filepath(path, "CHGCAR")
- self._potcar_path = self._get_filepath(path, "POTCAR")
- self._aeccar0_path = self._get_filepath(path, "AECCAR0")
- self._aeccar2_path = self._get_filepath(path, "AECCAR2")
-
- if run_chargemol and not (
- self._chgcar_path and self._potcar_path and self._aeccar0_path and self._aeccar2_path
- ):
- raise FileNotFoundError("CHGCAR, AECCAR0, AECCAR2, and POTCAR are all needed for Chargemol.")
- if self._chgcar_path:
- self.chgcar = Chgcar.from_file(self._chgcar_path)
- self.structure = self.chgcar.structure
- self.natoms = self.chgcar.poscar.natoms
- else:
- self.chgcar = self.structure = self.natoms = None
- warnings.warn("No CHGCAR found. Some properties may be unavailable.", UserWarning)
- if self._potcar_path:
- self.potcar = Potcar.from_file(self._potcar_path)
- else:
- warnings.warn("No POTCAR found. Some properties may be unavailable.", UserWarning)
- self.aeccar0 = Chgcar.from_file(self._aeccar0_path) if self._aeccar0_path else None
- self.aeccar2 = Chgcar.from_file(self._aeccar2_path) if self._aeccar2_path else None
-
- if run_chargemol:
- self._execute_chargemol(mpi=mpi, ncores=ncores)
- else:
- self._from_data_dir(chargemol_output_path=path)
-
- @staticmethod
- def _get_filepath(path, filename, suffix=""):
- """Returns the full path to the filename in the path. Works even if the file has
- a .gz extension.
-
- Args:
- path (str): Path to the file.
- filename (str): Filename.
- suffix (str): Optional suffix at the end of the filename.
-
- Returns:
- str: Absolute path to the file.
- """
- name_pattern = f"{filename}{suffix}*" if filename != "POTCAR" else f"{filename}*"
- paths = glob(os.path.join(path, name_pattern))
- fpath = None
- if len(paths) >= 1:
- # using reverse=True because, if multiple files are present,
- # they likely have suffixes 'static', 'relax', 'relax2', etc.
- # and this would give 'static' over 'relax2' over 'relax'
- # however, better to use 'suffix' kwarg to avoid this!
- paths.sort(reverse=True)
- # warning_msg = f"Multiple files detected, using {os.path.basename(paths[0])}" if len(paths) > 1 else None
- # warnings.warn(warning_msg)
- fpath = paths[0]
- return fpath
-
- def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_kwargs):
- """
- Internal function to run Chargemol.
-
-
- Args:
- atomic_densities_path (str): Path to the atomic densities directory
- required by Chargemol. If None, Pymatgen assumes that this is
- defined in a "DDEC6_ATOMIC_DENSITIES_DIR" environment variable.
- Default: None.
-
- mpi(bool): Whether run the Chargemol in a parallel way. Default is False.
- ncores (str): The number of cores you want to use.
- Default is os.getenv('SLURM_CPUS_ON_NODE') or os.getenv('SLURM_NTASKS')
- or multiprocessing.cpu_count().
- jobcontrol_kwargs: Keyword arguments for _write_jobscript_for_chargemol.
- """
- if mpi:
- if ncores:
- CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
- else:
- ncores = os.getenv("SLURM_CPUS_ON_NODE") or os.getenv("SLURM_NTASKS")
- if not ncores:
- ncores = multiprocessing.cpu_count()
- CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
- else:
- CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
-
- if self.save:
- save_path = Path(Path.cwd(), "charge")
- save_path.mkdir(parents=True, exist_ok=True)
- source = [
- Path(self._chgcar_path),
- Path(self._potcar_path),
- Path(self._aeccar0_path),
- Path(self._aeccar2_path),
- ]
-
- links = [
- Path(save_path, "CHGCAR"),
- Path(save_path, "POTCAR"),
- Path(save_path, "AECCAR0"),
- Path(save_path, "AECCAR2"),
- ]
- for link, src in zip(links, source):
- link.symlink_to(src)
- # write job_script file:
- write_path = str(save_path) + "/job_control.txt"
- self._write_jobscript_for_chargemol(write_path=write_path, **jobcontrol_kwargs)
-
- # Run Chargemol
- with subprocess.Popen(
- CHARGEMOLEXE,
- stdout=subprocess.PIPE,
- stdin=subprocess.PIPE,
- close_fds=True,
- cwd=save_path,
- ) as rs:
- rs.communicate()
- self._from_data_dir(chargemol_output_path=str(save_path))
- else:
- with ScratchDir("."):
- cwd = Path.cwd()
- source = [
- Path(self._chgcar_path),
- Path(self._potcar_path),
- Path(self._aeccar0_path),
- Path(self._aeccar2_path),
- ]
- links = [Path(cwd, "CHGCAR"), Path(cwd, "POTCAR"), Path(cwd, "AECCAR0"), Path(cwd, "AECCAR2")]
- for link, src in zip(links, source):
- link.symlink_to(src)
- # write job_script file:
- self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
-
- # Run Chargemol
- with subprocess.Popen(
- CHARGEMOLEXE,
- stdout=subprocess.PIPE,
- stdin=subprocess.PIPE,
- close_fds=True,
- ) as rs:
- rs.communicate()
- if rs.returncode != 0:
- raise RuntimeError(
- f"Chargemol exited with return code {int(rs.returncode)}. "
- "Please check your Chargemol installation."
- )
-
- self._from_data_dir()
-
- def _from_data_dir(self, chargemol_output_path: str | None = None):
- """
- Internal command to parse Chargemol files from a directory.
-
- Args:
- chargemol_output_path (str): Path to the folder containing
- the Chargemol output files.
- Default: None (current working directory).
- """
- if chargemol_output_path is None:
- chargemol_output_path = "."
-
- charge_path = f"{chargemol_output_path}/DDEC6_even_tempered_net_atomic_charges.xyz"
- self.ddec_charges = self._get_data_from_xyz(charge_path)
- self.dipoles = self._get_dipole_info(charge_path)
-
- bond_order_path = f"{chargemol_output_path}/DDEC6_even_tempered_bond_orders.xyz"
- if os.path.exists(bond_order_path):
- self.bond_order_sums = self._get_data_from_xyz(bond_order_path)
- self.bond_order_dict = self._get_bond_order_info(bond_order_path)
- else:
- self.bond_order_sums = self.bond_order_dict = None
-
- spin_moment_path = f"{chargemol_output_path}/DDEC6_even_tempered_atomic_spin_moments.xyz"
- if os.path.exists(spin_moment_path):
- self.ddec_spin_moments = self._get_data_from_xyz(spin_moment_path)
- else:
- self.ddec_spin_moments = None
-
- rsquared_path = f"{chargemol_output_path}/DDEC_atomic_Rsquared_moments.xyz"
- if os.path.exists(rsquared_path):
- self.ddec_rsquared_moments = self._get_data_from_xyz(rsquared_path)
- else:
- self.ddec_rsquared_moments = None
-
- rcubed_path = f"{chargemol_output_path}/DDEC_atomic_Rcubed_moments.xyz"
- if os.path.exists(rcubed_path):
- self.ddec_rcubed_moments = self._get_data_from_xyz(rcubed_path)
- else:
- self.ddec_rcubed_moments = None
-
- rfourth_path = f"{chargemol_output_path}/DDEC_atomic_Rfourth_moments.xyz"
- if os.path.exists(rfourth_path):
- self.ddec_rfourth_moments = self._get_data_from_xyz(rfourth_path)
- else:
- self.ddec_rfourth_moments = None
-
- ddec_analysis_path = f"{chargemol_output_path}/VASP_DDEC_analysis.output"
- if os.path.exists(ddec_analysis_path):
- self.cm5_charges = self._get_cm5_data_from_output(ddec_analysis_path)
- else:
- self.cm5_charges = None
-
- def get_charge_transfer(self, atom_index, charge_type="ddec"):
- """Returns the charge transferred for a particular atom. A positive value means
- that the site has gained electron density (i.e. exhibits anionic character)
- whereas a negative value means the site has lost electron density (i.e. exhibits
- cationic character). This is the same thing as the negative of the partial atomic
- charge.
-
- Args:
- atom_index (int): Index of atom to get charge transfer for.
- charge_type (str): Type of charge to use ("ddec" or "cm5").
-
- Returns:
- float: charge transferred at atom_index
- """
- if charge_type.lower() not in ["ddec", "cm5"]:
- raise ValueError(f"Invalid {charge_type=}")
- if charge_type.lower() == "ddec":
- charge_transfer = -self.ddec_charges[atom_index]
- elif charge_type.lower() == "cm5":
- charge_transfer = -self.cm5_charges[atom_index]
- return charge_transfer
-
- def get_charge(self, atom_index, nelect: int | None = None, charge_type="ddec"):
- """Convenience method to get the charge on a particular atom using the same
- sign convention as the BaderAnalysis. Note that this is *not* the partial
- atomic charge. This value is nelect (e.g. ZVAL from the POTCAR) + the
- charge transferred. If you want the partial atomic charge, use
- get_partial_charge().
-
- Args:
- atom_index (int): Index of atom to get charge for.
- nelect (int): number of electrons associated with an isolated atom at this index.
- For most DFT codes this corresponds to the number of valence electrons
- associated with the pseudopotential. If None, this value will be automatically
- obtained from the POTCAR (if present).
- Default: None.
- charge_type (str): Type of charge to use ("ddec" or "cm5").
-
- Returns:
- float: charge on atom_index
- """
- if nelect:
- charge = nelect + self.get_charge_transfer(atom_index, charge_type=charge_type)
- elif self.potcar and self.natoms:
- charge = None
- potcar_indices = []
- for i, v in enumerate(self.natoms):
- potcar_indices += [i] * v
- nelect = self.potcar[potcar_indices[atom_index]].nelectrons
- charge = nelect + self.get_charge_transfer(atom_index, charge_type=charge_type)
- else:
- charge = None
- return charge
-
- def get_partial_charge(self, atom_index, charge_type="ddec"):
- """Convenience method to get the partial atomic charge on a particular atom.
- This is the value printed in the Chargemol analysis.
-
- Args:
- atom_index (int): Index of atom to get charge for.
- charge_type (str): Type of charge to use ("ddec" or "cm5").
- """
- if charge_type.lower() not in ["ddec", "cm5"]:
- raise ValueError(f"Invalid charge_type: {charge_type}")
- if charge_type.lower() == "ddec":
- partial_charge = self.ddec_charges[atom_index]
- elif charge_type.lower() == "cm5":
- partial_charge = self.cm5_charges[atom_index]
- return partial_charge
-
- def get_bond_order(self, index_from, index_to):
- """Convenience method to get the bond order between two atoms.
-
- Args:
- index_from (int): Index of atom to get bond order from.
- index_to (int): Index of atom to get bond order to.
-
- Returns:
- float: bond order between atoms
- """
- bonded_set = self.bond_order_dict[index_from]["bonded_to"]
- bond_orders = [v["bond_order"] for v in bonded_set if v["index"] == index_to]
- return 0.0 if bond_orders == [] else np.sum(bond_orders)
-
- def _write_jobscript_for_chargemol(
- self,
- net_charge=0.0,
- periodicity=(True, True, True),
- method="ddec6",
- compute_bond_orders=True,
- write_path: str = "job_control.txt",
- ):
- """Writes job_script.txt for Chargemol execution.
-
- Args:
- net_charge (float): Net charge of the system.
- Defaults to 0.0.
- periodicity (tuple[bool]): Periodicity of the system.
- Default: (True, True, True).
- method (str): Method to use for the analysis. Options include "ddec6"
- and "ddec3". Default: "ddec6"
- compute_bond_orders (bool): Whether to compute bond orders. Default: True.
- write_path (str): The path of output files of chargemol if you want to save them.
- """
- self.net_charge = net_charge
- self.periodicity = periodicity
- self.method = method
-
- lines = ""
-
- # Net Charge
- if net_charge:
- lines += f"\n{net_charge}\n\n"
-
- # Periodicity
- if periodicity:
- per_a = ".true." if periodicity[0] else ".false."
- per_b = ".true." if periodicity[1] else ".false."
- per_c = ".true." if periodicity[2] else ".false."
- lines += (
- f"\n{per_a}\n{per_b}\n{per_c}\n"
- "\n"
- )
-
- # atomic_densities dir
- atomic_densities_path = self._atomic_densities_path or os.getenv("DDEC6_ATOMIC_DENSITIES_DIR")
- if atomic_densities_path is None:
- raise OSError(
- "The DDEC6_ATOMIC_DENSITIES_DIR environment variable must be set or the atomic_densities_path must"
- " be specified"
- )
- if not os.path.exists(atomic_densities_path):
- raise FileNotFoundError(f"{atomic_densities_path=} does not exist")
-
- # This is to fix a Chargemol filepath nuance
- if os.name == "nt": # Windows
- if atomic_densities_path[-1] != "\\":
- atomic_densities_path += "\\"
- elif atomic_densities_path[-1] != "/":
- atomic_densities_path += "/"
-
- lines += (
- f"\n\n{atomic_densities_path}\n\n"
- )
-
- # Charge type
- lines += f"\n\n{method.upper()}\n\n"
-
- if compute_bond_orders:
- bo = ".true." if compute_bond_orders else ".false."
- lines += f"\n\n{bo}\n\n"
-
- with open(write_path, "w") as fh:
- fh.write(lines)
-
- @staticmethod
- def _get_dipole_info(filepath):
- """Internal command to process dipoles.
-
- Args:
- filepath (str): The path to the DDEC6_even_tempered_net_atomic_charges.xyz file
- """
- idx = 0
- start = False
- dipoles = []
- with open(filepath) as r:
- for line in r:
- if "The following XYZ" in line:
- start = True
- idx += 1
- continue
- if start and line.strip() == "":
- break
- if idx >= 2:
- dipoles.append([float(d) for d in line.strip().split()[7:10]])
- if start:
- idx += 1
-
- return dipoles
-
- @staticmethod
- def _get_bond_order_info(filename):
- """Internal command to process pairwise bond order information.
-
- Args:
- filename (str): The path to the DDEC6_even_tempered_bond_orders.xyz file
- """
- # Get where relevant info for each atom starts
- bond_order_info = {}
-
- with open(filename) as r:
- for line in r:
- split = line.strip().split()
- if "Printing BOs" in line:
- start_idx = int(split[5]) - 1
- start_el = Element(split[7])
- bond_order_info[start_idx] = {"element": start_el, "bonded_to": []}
- elif "Bonded to the" in line:
- direction = tuple(int(i.split(")")[0].split(",")[0]) for i in split[4:7])
- end_idx = int(split[12]) - 1
- end_el = Element(split[14])
- bo = float(split[20])
- spin_bo = float(split[-1])
- bonded_to = {
- "index": end_idx,
- "element": end_el,
- "bond_order": bo,
- "direction": direction,
- "spin_polarization": spin_bo,
- }
- bond_order_info[start_idx]["bonded_to"].append(bonded_to)
- elif "The sum of bond orders for this atom" in line:
- bond_order_info[start_idx]["bond_order_sum"] = float(split[-1])
-
- return bond_order_info
-
- def get_property_decorated_structure(self):
- """Takes CHGCAR's structure object and updates it with properties
- from the Chargemol analysis.
-
- Returns:
- Pymatgen structure with site properties added
- """
- struct = self.structure.copy()
- struct.add_site_property("partial_charge_ddec6", self.ddec_charges)
- if self.dipoles:
- struct.add_site_property("dipole_ddec6", self.dipoles)
- if self.bond_order_sums:
- struct.add_site_property("bond_order_sum_ddec6", self.bond_order_sums)
- if self.ddec_spin_moments:
- struct.add_site_property("spin_moment_ddec6", self.ddec_spin_moments)
- if self.cm5_charges:
- struct.add_site_property("partial_charge_cm5", self.cm5_charges)
- return struct
-
- @property
- def summary(self):
- """Returns a dictionary summary of the Chargemol analysis
- {
- "ddec": {
- "partial_charges": list[float],
- "spin_moments": list[float],
- "dipoles": list[float],
- "rsquared_moments": list[float],
- "rcubed_moments": list[float],
- "rfourth_moments": list[float],
- "bond_order_dict": dict
- },
- "cm5": {
- "partial_charges": list[float],
- }
- }.
- """
- summary = {}
- ddec_summary = {"partial_charges": self.ddec_charges}
- if self.bond_order_sums:
- ddec_summary["bond_order_sums"] = self.bond_order_sums
- if self.ddec_spin_moments:
- ddec_summary["spin_moments"] = self.ddec_spin_moments
- if self.dipoles:
- ddec_summary["dipoles"] = self.dipoles
- if self.ddec_rsquared_moments:
- ddec_summary["rsquared_moments"] = self.ddec_rsquared_moments
- if self.ddec_rcubed_moments:
- ddec_summary["rcubed_moments"] = self.ddec_rcubed_moments
- if self.ddec_rfourth_moments:
- ddec_summary["rfourth_moments"] = self.ddec_rfourth_moments
- if self.bond_order_dict:
- ddec_summary["bond_order_dict"] = self.bond_order_dict
-
- cm5_summary = {"partial_charges": self.cm5_charges} if self.cm5_charges else None
-
- summary["ddec"] = ddec_summary
- summary["cm5"] = cm5_summary
-
- return summary
-
- @staticmethod
- def _get_data_from_xyz(xyz_path):
- """Internal command to process Chargemol XYZ files.
-
- Args:
- xyz_path (str): Path to XYZ file
-
- Returns:
- list[float]: site-specific properties
- """
- props = []
- if os.path.exists(xyz_path):
- with open(xyz_path) as r:
- for i, line in enumerate(r):
- if i <= 1:
- continue
- if line.strip() == "":
- break
- props.append(float(line.split()[-1]))
- else:
- raise FileNotFoundError(f"{xyz_path} not found")
-
- return props
-
- @staticmethod
- def _get_cm5_data_from_output(ddec_analysis_path):
- """Internal command to process Chargemol CM5 data.
-
- Args:
- ddec_analysis_path (str): Path VASP_DDEC_analysis.output file
-
- Returns:
- list[float]: CM5 charges
- """
- props = []
- if os.path.exists(ddec_analysis_path):
- start = False
- with open(ddec_analysis_path) as r:
- for line in r:
- if "computed CM5" in line:
- start = True
- continue
- if "Hirshfeld and CM5" in line:
- break
- if start:
- vals = line.split()
- props.extend([float(c) for c in [val.strip() for val in vals]])
- else:
- raise FileNotFoundError(f"{ddec_analysis_path} not found")
- return props
From 27905f1a68e3e521979f5894491339383b4a98d3 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 01:21:04 +0000
Subject: [PATCH 26/37] Make sure chargemol exe is not None
---
pymatgen/command_line/chargemol_caller.py | 75 +++++++++++++----------
1 file changed, 44 insertions(+), 31 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 5311a88bc25..445fbaf32b6 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -84,7 +84,7 @@ def __init__(
atomic_densities_path=None,
run_chargemol: bool = True,
mpi: bool = False,
- ncores: int | None = None,
+ ncores: str | None = None,
save: bool = False,
):
"""
@@ -102,11 +102,11 @@ def __init__(
the existing Chargemol output files will be read from path.
Default: True.
mpi (bool): Whether to run the Chargemol in a parallel way.
- ncores: Use how many cores to run the Chargemol!
- Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE'),
- or os.environ.get('SLURM_CPUS_ON_NODE')", or "multiprocessing.cpu_count()".
- Take your own risk! This default value might not suit you!
- You'd better set your own number!!!
+ ncores (str): Use how many cores to run the Chargemol!
+ Default is "os.environ.get('SLURM_JOB_CPUS_PER_NODE'),
+ or os.environ.get('SLURM_CPUS_ON_NODE')", or "multiprocessing.cpu_count()".
+ Take your own risk! This default value might not suit you!
+ You'd better set your own number!!!
save: save (bool): Whether to save the Chargemol output files. Default is False.
the existing Chargemol output files will be read from path. Default: True.
"""
@@ -200,7 +200,7 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
else:
ncores = os.getenv("SLURM_CPUS_ON_NODE") or os.getenv("SLURM_NTASKS")
if not ncores:
- ncores = multiprocessing.cpu_count()
+ ncores = str(multiprocessing.cpu_count())
CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
else:
CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
@@ -228,16 +228,23 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
self._write_jobscript_for_chargemol(write_path=write_path, **jobcontrol_kwargs)
# Run Chargemol
- with subprocess.Popen(
- CHARGEMOLEXE,
- stdout=subprocess.PIPE,
- stdin=subprocess.PIPE,
- close_fds=True,
- cwd=save_path,
- ) as rs:
- rs.communicate()
- self._from_data_dir(chargemol_output_path=str(save_path))
-
+ if CHARGEMOLEXE:
+ if isinstance(CHARGEMOLEXE, list):
+ CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
+ if not CHARGEMOLEXE:
+ raise RuntimeError("Make sure compiled chargemol executable being available in the path")
+
+ with subprocess.Popen(
+ CHARGEMOLEXE,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ close_fds=True,
+ cwd=save_path,
+ ) as rs:
+ rs.communicate()
+ self._from_data_dir(chargemol_output_path=str(save_path))
+ else:
+ raise RuntimeError("Make sure compiled chargemol executable being available in the path")
else:
with ScratchDir("."):
cwd = Path.cwd()
@@ -254,20 +261,26 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
self._write_jobscript_for_chargemol(**jobcontrol_kwargs)
# Run Chargemol
- with subprocess.Popen(
- CHARGEMOLEXE,
- stdout=subprocess.PIPE,
- stdin=subprocess.PIPE,
- close_fds=True,
- ) as rs:
- rs.communicate()
- if rs.returncode != 0:
- raise RuntimeError(
- f"Chargemol exited with return code {int(rs.returncode)}. "
- "Please check your Chargemol installation."
- )
-
- self._from_data_dir()
+ if CHARGEMOLEXE:
+ if isinstance(CHARGEMOLEXE, list):
+ CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
+ if not CHARGEMOLEXE:
+ raise RuntimeError("Make sure compiled chargemol executable being available in the path")
+ with subprocess.Popen(
+ CHARGEMOLEXE,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ close_fds=True,
+ ) as rs:
+ rs.communicate()
+ if rs.returncode != 0:
+ raise RuntimeError(
+ f"Chargemol exited with return code {int(rs.returncode)}. "
+ "Please check your Chargemol installation."
+ )
+ self._from_data_dir()
+ else:
+ raise RuntimeError("Make sure compiled chargemol executable being available in the path")
def _from_data_dir(self, chargemol_output_path: str | None = None):
"""
From 15114163c53eb91511a5637663a471a5354c93bd Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 7 Nov 2023 01:22:33 +0000
Subject: [PATCH 27/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 445fbaf32b6..a901e306ee7 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -233,7 +233,7 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
-
+
with subprocess.Popen(
CHARGEMOLEXE,
stdout=subprocess.PIPE,
From c633a532334e5c2028b66c391719adf3efac8101 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 01:23:24 +0000
Subject: [PATCH 28/37] Remove the black whitespace
---
pymatgen/command_line/chargemol_caller.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index a901e306ee7..c39fe248c92 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -233,7 +233,6 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
-
with subprocess.Popen(
CHARGEMOLEXE,
stdout=subprocess.PIPE,
From 9fbcf087ca16578c63e8fe88422369b469871b14 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 03:35:28 +0000
Subject: [PATCH 29/37] Use cast from typing to assert to the type checker that
CHARGEMOLEXE is indeed a list of strings after the None filtering.
---
pymatgen/command_line/chargemol_caller.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index c39fe248c92..989cd983f7a 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -49,6 +49,7 @@
from glob import glob
from pathlib import Path
from shutil import which
+from typing import List, Optional, Union, cast
import numpy as np
from monty.tempfile import ScratchDir
@@ -74,7 +75,7 @@ class ChargemolAnalysis:
bond orders, and related properties.
"""
- CHARGEMOLEXE = (
+ CHARGEMOLEXE: Optional[Union[str, List[Optional[str]]]] = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -231,10 +232,12 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
+ CHARGEMOLEXE = cast(List[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
+ popen_args = cast(Union[str, List[str]], CHARGEMOLEXE)
with subprocess.Popen(
- CHARGEMOLEXE,
+ popen_args,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
close_fds=True,
@@ -263,8 +266,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
+ CHARGEMOLEXE = cast(List[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
+ popen_args = cast(Union[str, List[str]], CHARGEMOLEXE)
with subprocess.Popen(
CHARGEMOLEXE,
stdout=subprocess.PIPE,
From c2f40e5e1c61f6f89f666d6ceb341445799da9a1 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 03:37:45 +0000
Subject: [PATCH 30/37] change to popen_args
---
pymatgen/command_line/chargemol_caller.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 989cd983f7a..c95a3b76810 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -271,7 +271,7 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
popen_args = cast(Union[str, List[str]], CHARGEMOLEXE)
with subprocess.Popen(
- CHARGEMOLEXE,
+ popen_args,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
close_fds=True,
From f6289d572daa8f947748e52e3a3a132f06c6a371 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 7 Nov 2023 03:39:23 +0000
Subject: [PATCH 31/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index c95a3b76810..3b6f2bf126c 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -49,7 +49,7 @@
from glob import glob
from pathlib import Path
from shutil import which
-from typing import List, Optional, Union, cast
+from typing import Optional, Union, cast
import numpy as np
from monty.tempfile import ScratchDir
@@ -75,7 +75,7 @@ class ChargemolAnalysis:
bond orders, and related properties.
"""
- CHARGEMOLEXE: Optional[Union[str, List[Optional[str]]]] = (
+ CHARGEMOLEXE: Optional[Union[str, list[Optional[str]]]] = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -232,10 +232,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast(List[str], CHARGEMOLEXE)
+ CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast(Union[str, List[str]], CHARGEMOLEXE)
+ popen_args = cast(Union[str, list[str]], CHARGEMOLEXE)
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
@@ -266,10 +266,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast(List[str], CHARGEMOLEXE)
+ CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast(Union[str, List[str]], CHARGEMOLEXE)
+ popen_args = cast(Union[str, list[str]], CHARGEMOLEXE)
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
From 486e0d1b1f7b46e0a8efee9646537d14f98d93aa Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 03:42:54 +0000
Subject: [PATCH 32/37] 1.Replace typing.List with the built-in list. 2.Replace
typing.Optional with the | None. 3.Replace typing.Union with the | operator
for union types.
---
pymatgen/command_line/chargemol_caller.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 3b6f2bf126c..b24f7cdf805 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -49,7 +49,7 @@
from glob import glob
from pathlib import Path
from shutil import which
-from typing import Optional, Union, cast
+from typing import cast
import numpy as np
from monty.tempfile import ScratchDir
@@ -64,7 +64,7 @@
__email__ = "shyuep@gmail.com"
__date__ = "01/18/21"
-CHARGEMOL_EXE = (
+CHARGEMOL_EXE: "str | list[str | None] | None" = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -75,7 +75,7 @@ class ChargemolAnalysis:
bond orders, and related properties.
"""
- CHARGEMOLEXE: Optional[Union[str, list[Optional[str]]]] = (
+ CHARGEMOLEXE: "str | list[str | None] | None" = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -232,10 +232,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
+ CHARGEMOLEXE = cast("list[str]", CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast(Union[str, list[str]], CHARGEMOLEXE)
+ popen_args = cast("str | list[str]", CHARGEMOLEXE)
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
@@ -266,10 +266,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
+ CHARGEMOLEXE = cast("list[str]", CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast(Union[str, list[str]], CHARGEMOLEXE)
+ popen_args = cast("str | list[str]", CHARGEMOLEXE)
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
From 404d0484c8fb151476f54624f9b605113fd8747a Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 7 Nov 2023 03:44:58 +0000
Subject: [PATCH 33/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index b24f7cdf805..8f09e9f6b95 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -64,7 +64,7 @@
__email__ = "shyuep@gmail.com"
__date__ = "01/18/21"
-CHARGEMOL_EXE: "str | list[str | None] | None" = (
+CHARGEMOL_EXE: str | list[str | None] | None = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -75,7 +75,7 @@ class ChargemolAnalysis:
bond orders, and related properties.
"""
- CHARGEMOLEXE: "str | list[str | None] | None" = (
+ CHARGEMOLEXE: str | list[str | None] | None = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
From cebe56b6b6a0f29f7a561d09f6bd2c56379f95d2 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 03:47:50 +0000
Subject: [PATCH 34/37] Remove quotes from type annotation
---
pymatgen/command_line/chargemol_caller.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 8f09e9f6b95..d9bab2864f8 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -232,10 +232,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast("list[str]", CHARGEMOLEXE)
+ CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast("str | list[str]", CHARGEMOLEXE)
+ popen_args = cast(str | list[str], CHARGEMOLEXE)
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
@@ -266,10 +266,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast("list[str]", CHARGEMOLEXE)
+ CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast("str | list[str]", CHARGEMOLEXE)
+ popen_args = cast(str | list[str], CHARGEMOLEXE)
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
From 7bb8cbc22ca045ec70d93209530a32c1711b94b0 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 03:55:16 +0000
Subject: [PATCH 35/37] remove cast
---
pymatgen/command_line/chargemol_caller.py | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index d9bab2864f8..67a891d78fa 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -49,7 +49,6 @@
from glob import glob
from pathlib import Path
from shutil import which
-from typing import cast
import numpy as np
from monty.tempfile import ScratchDir
@@ -64,7 +63,7 @@
__email__ = "shyuep@gmail.com"
__date__ = "01/18/21"
-CHARGEMOL_EXE: str | list[str | None] | None = (
+CHARGEMOL_EXE: str | list[str] = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -75,7 +74,7 @@ class ChargemolAnalysis:
bond orders, and related properties.
"""
- CHARGEMOLEXE: str | list[str | None] | None = (
+ CHARGEMOLEXE: str | list[str] = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -232,10 +231,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
+ # CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast(str | list[str], CHARGEMOLEXE)
+ popen_args = CHARGEMOLEXE
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
@@ -266,10 +265,10 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
+ # CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = cast(str | list[str], CHARGEMOLEXE)
+ popen_args = CHARGEMOLEXE
with subprocess.Popen(
popen_args,
stdout=subprocess.PIPE,
From 65451754d5c32011d0baee77cf9f3bf61820a5f4 Mon Sep 17 00:00:00 2001
From: Six_ligand <49940294+RedStar-Iron@users.noreply.github.com>
Date: Tue, 7 Nov 2023 05:58:15 +0000
Subject: [PATCH 36/37] Ignore the type of "CHARGEMOLEXE"
---
pymatgen/command_line/chargemol_caller.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index 67a891d78fa..df798f4c89c 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -63,7 +63,7 @@
__email__ = "shyuep@gmail.com"
__date__ = "01/18/21"
-CHARGEMOL_EXE: str | list[str] = (
+CHARGEMOL_EXE = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -74,7 +74,7 @@ class ChargemolAnalysis:
bond orders, and related properties.
"""
- CHARGEMOLEXE: str | list[str] = (
+ CHARGEMOLEXE = (
which("Chargemol_09_26_2017_linux_parallel") or which("Chargemol_09_26_2017_linux_serial") or which("chargemol")
)
@@ -196,14 +196,14 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
"""
if mpi:
if ncores:
- CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE] # type: ignore
else:
ncores = os.getenv("SLURM_CPUS_ON_NODE") or os.getenv("SLURM_NTASKS")
if not ncores:
ncores = str(multiprocessing.cpu_count())
- CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE]
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE] # type: ignore
else:
- CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE
+ CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE # type: ignore
if self.save:
save_path = Path(Path.cwd(), "charge")
@@ -231,12 +231,12 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- # CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
+ popen_args: list[str] = CHARGEMOLEXE # type: ignore
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = CHARGEMOLEXE
+ popen_args = CHARGEMOLEXE # type: ignore
with subprocess.Popen(
- popen_args,
+ popen_args, # type: ignore
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
close_fds=True,
@@ -268,9 +268,9 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
# CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = CHARGEMOLEXE
+ popen_args = CHARGEMOLEXE # type: ignore
with subprocess.Popen(
- popen_args,
+ popen_args, # type: ignore
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
close_fds=True,
From e2ebd579f4082fc8d617644ba6ac703b6978b887 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 7 Nov 2023 05:59:21 +0000
Subject: [PATCH 37/37] pre-commit auto-fixes
---
pymatgen/command_line/chargemol_caller.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py
index df798f4c89c..6d7c585d374 100644
--- a/pymatgen/command_line/chargemol_caller.py
+++ b/pymatgen/command_line/chargemol_caller.py
@@ -196,14 +196,14 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
"""
if mpi:
if ncores:
- CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE] # type: ignore
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE] # type: ignore
else:
ncores = os.getenv("SLURM_CPUS_ON_NODE") or os.getenv("SLURM_NTASKS")
if not ncores:
ncores = str(multiprocessing.cpu_count())
- CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE] # type: ignore
+ CHARGEMOLEXE = ["mpirun", "-n", str(ncores), ChargemolAnalysis.CHARGEMOLEXE] # type: ignore
else:
- CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE # type: ignore
+ CHARGEMOLEXE = ChargemolAnalysis.CHARGEMOLEXE # type: ignore
if self.save:
save_path = Path(Path.cwd(), "charge")
@@ -231,12 +231,12 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
if CHARGEMOLEXE:
if isinstance(CHARGEMOLEXE, list):
CHARGEMOLEXE = [x for x in CHARGEMOLEXE if x is not None]
- popen_args: list[str] = CHARGEMOLEXE # type: ignore
+ popen_args: list[str] = CHARGEMOLEXE # type: ignore
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
popen_args = CHARGEMOLEXE # type: ignore
with subprocess.Popen(
- popen_args, # type: ignore
+ popen_args, # type: ignore
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
close_fds=True,
@@ -268,9 +268,9 @@ def _execute_chargemol(self, mpi=False, ncores: str | None = None, **jobcontrol_
# CHARGEMOLEXE = cast(list[str], CHARGEMOLEXE)
if not CHARGEMOLEXE:
raise RuntimeError("Make sure compiled chargemol executable being available in the path")
- popen_args = CHARGEMOLEXE # type: ignore
+ popen_args = CHARGEMOLEXE # type: ignore
with subprocess.Popen(
- popen_args, # type: ignore
+ popen_args, # type: ignore
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
close_fds=True,