.
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | py2sambvca
2 | Simple thin client to interface python scripts with SambVca catalytic pocket Fortran calculator.
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ## Installation
20 | `py2sambvca` is available on PyPI and can be installed like so:
21 | ```python
22 | pip install py2sambvca
23 | ```
24 |
25 | `py2sambvca` has __zero__ external dependencies.
26 |
27 | ### Downloading and Compiling `SambVca`
28 | `py2sambvca` can read and write input and output files for `SambVca` without the actual program in place, but in order to run input files you must have an executable `sambvca21.exe` (or similar) somewhere on your machine.
29 |
30 | You can download the source code [on the `SambVca` webserver](https://www.aocdweb.com/OMtools/sambvca2.1/download/download.html) and compile it using [`gfortran`](https://gcc.gnu.org/wiki/GFortranBinaries).
31 |
32 | By default, `py2sambvca` expects the executable to be present in the `cwd` and named `sambvca21.exe` on Windows or `sambvca21.x` on Unix-based systems. optionally, the filepath to your executable can be specified as shown below.
33 |
34 | ## Usage
35 | After installation, `py2sambvca` can be added to a Python script via `import` and instantiated:
36 | ```python
37 | from py2sambvca import p2s
38 |
39 | nhc_p2s = p2s(
40 | "test/data/nhc.xyz",
41 | [22],
42 | [5],
43 | [1],
44 | path_to_sambvcax="sambvca21.exe",
45 | )
46 | ```
47 |
48 | The required input parameters are shown below:
49 | - `xyz_filepath` (str): Location of .xyz molecular coordinates file for writing input data
50 | - `sphere_center_atom_ids` (list): ID of atoms defining the sphere center
51 | - `z_ax_atom_ids` (list): ID of atoms for z-axis
52 | - `xz_plane_atoms_ids` (list): ID of atoms for xz-plane
53 |
54 | The following parameters are optional and will be filled with default values if not specified:
55 | - `atoms_to_delete_ids` (list): ID of atoms to be deleted (default None)
56 | - `sphere_radius` (float): Sphere radius in Angstrom (default 3.5)
57 | - `displacement` (float): Displacement of oriented molecule from sphere center in Angstrom (default 0.0)
58 | - `mesh_size` (float): Mesh size for numerical integration (default 0.10)
59 | - `remove_H` (int): 0/1 Do not remove/remove H atoms from Vbur calculation (default 1)
60 | - `orient_z` (int): 0/1 Molecule oriented along negative/positive Z-axis (default 1)
61 | - `write_surf_files` (int): 0/1 Do not write/write files for top and bottom surfaces (default 1)
62 | - `path_to_sambvcax` (str): Path to the SambVca executable. Only needed to use py2sambvca.calc()(default "sambvca.exe")
63 | - `working_dir` (path): Path to the working directory where the output and input files are generated (default os.getcwd())
64 | - `verbose` (int): 0 for no output, 1 for some output, 2 for the most output (default 1)
65 | - `radii_table` (dict or str): a dictionary of atomic symbols and their radii (angstroms), or "default" for the radii used in the original implementation
66 |
67 |
68 | From here, running can be done stepwise or with a single function:
69 | ```python
70 | nhc_p2s.run()
71 | # equivalent to
72 | nhc_p2s.write_input()
73 | nhc_p2s.calc()
74 | nhc_p2s.parse_output()
75 | nhc_p2s.clean_files()
76 | ```
77 |
78 | All values for the total complex, quadrants, and octants are available through getters:
79 |
80 | | Total Values | Quadrant Values | Octant Values |
81 | | --- | --- | --- |
82 | | `get_free_volume()` | `get_quadrant_free_volume()` | `get_octant_free_volume()` |
83 | | `get_buried_volume()` | `get_quadrant_buried_volume()` | `get_octant_buried_volume()` |
84 | | `get_exact_volume()` | _not available_ | _not available_ |
85 | | `get_total_volume()` | `get_quadrant_total_volume()` | `get_octant_total_volume()` |
86 | | `get_percent_buried_volume()` | `get_quadrant_percent_buried_volume()` | `get_octant_percent_buried_volume()` |
87 | | `get_percent_free_volume()` | `get_quadrant_percent_free_volume()` | `get_octant_percent_free_volume()` |
88 | | `get_percent_total_volume()` | _not available_ | _not available_ |
89 |
90 | Results can also be accessed through a general getter method: `get()`, `get_quadrant_result()`, and `get_octant_result()`.
91 |
92 | All results can also be directly accessed through dictionaries, returned from a call to `run()` or `parse_output()` and available through `p2s.total_results`, `p2s.quadrant_results`, and `p2s.octant_results`.
93 |
94 | In case there is something else you are looking for, you can use a general purpose `get_regex()` function to return the line containing a pattern.
95 |
96 | ### Examples
97 | Here are a couple repositories using `py2sambvca` as a Python package or extending its source code, check them out:
98 | - ~~[Metal-organic framework stability analysis by Hiu Ki](https://github.com/hiukiwong/mof-stability-ml)~~
99 | - [MOF Stability ML by Ruihan Wang](https://github.com/ruihwang/mof-stability-ml)
100 |
101 | ### See Also
102 | - Kjell Jorner's [morfeus](https://github.com/kjelljorner/morfeus) package re-implements the original buried volume algorithm directly in Python
103 |
104 | ## License
105 | `py2sambvca` is available under the GNU GPLv3 in accordance with the base Fortran code which is available under the same license and can be retreieved here: https://www.molnac.unisa.it/OMtools/sambvca2.1/download/download.html
106 |
107 | The original fortran program (`sambvca21.f`) is also included in the `test` directory for testing purposes. It is still under the same terms of the GNU license:
108 | - This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation.
109 | - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
110 | - The results obtained from using the source code shall be used for scientific purposes only, excluding industrial or commercial purposes. To use the SambVca suite for industrial or commercial purposes, contact lcavallo|@|unisa.it.
111 | - Proper acknowledgement shall be made to the author of the source code in publications resulting from the use of it in its original form or modified.
112 | - The results from using the source code are provided "AS IS" without warranty of any kind.
113 |
114 | ## Citation
115 | Please cite the `SambVca` underlying Fortran tool according to the guidelines on the buried volume webserver: [https://www.molnac.unisa.it/OMtools/sambvca2.1/help/help.html](https://www.molnac.unisa.it/OMtools/sambvca2.1/help/help.html)
116 |
117 | `py2sambvca` has been uploaded to Figshare and may be cited as: Burns, J. figshare. 2020, DOI:[10.6084/m9.figshare.12846707](https://figshare.com/articles/software/py2sambvca/12846707)
118 |
--------------------------------------------------------------------------------
/py2sambvca/__init__.py:
--------------------------------------------------------------------------------
1 | from .py2sambvca import py2sambvca as p2s
2 |
3 | __all__ = ["p2s"]
4 |
5 | __version__ = "1.4.2"
6 |
--------------------------------------------------------------------------------
/py2sambvca/py2sambvca.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import re
3 | import glob
4 | import os
5 | from typing import List, Union
6 | from warnings import warn
7 |
8 | from py2sambvca.radii_tables import table_lookup, format_radii_table
9 |
10 |
11 | class py2sambvca:
12 | """
13 | Wrapper class for py2sambvca functions.
14 |
15 | Call this class to instantiate a py2sambvca object, which has methods to write input, call SambVca,
16 | and retrieve output.
17 |
18 | Parameters:
19 | xyz_filepath (str): Location of .xyz molecular coordinates file for writing input data
20 | sphere_center_atom_ids (list): ID of atoms defining the sphere center
21 | z_ax_atom_ids (list): ID of atoms for z-axis
22 | xz_plane_atoms_ids (list): ID of atoms for xz-plane
23 | atoms_to_delete_ids (list): ID of atoms to be deleted (default None)
24 | sphere_radius (float): Sphere radius in Angstrom (default 3.5)
25 | displacement (float): Displacement of oriented molecule from sphere center in Angstrom (default 0.0)
26 | mesh_size (float): Mesh size for numerical integration (default 0.10)
27 | remove_H (int): 0/1 Do not remove/remove H atoms from Vbur calculation (default 1)
28 | orient_z (int): 0/1 Molecule oriented along negative/positive Z-axis (default 1)
29 | write_surf_files (int): 0/1 Do not write/write files for top and bottom surfaces (default 1)
30 | path_to_sambvcax (str): Path to the SambVca executable. Only needed to use py2sambvca.calc()( default "/path/to/executable/sambvca.x")
31 | working_dir (path): Path to the working directory where the output and input files are generated (default os.getcwd())
32 | verbose (int): 0 for no output, 1 for some output, 2 for the most output
33 | radii_table (dict or str): atomic symbols (ie. H, LI) and radii (Angstrom), "default" (bondii radii*1.17), or "vdw" for van Der Waals
34 | """
35 |
36 | def __init__(
37 | self,
38 | xyz_filepath: str,
39 | sphere_center_atom_ids: List[int],
40 | z_ax_atom_ids: List[int],
41 | xz_plane_atoms_ids: List[int],
42 | atoms_to_delete_ids: List[int] = None,
43 | sphere_radius: float = 3.5,
44 | displacement: float = 0.0,
45 | mesh_size: float = 0.10,
46 | remove_H: int = 1,
47 | orient_z: int = 1,
48 | write_surf_files: int = 1,
49 | path_to_sambvcax: str = "sambvca.exe",
50 | working_dir: str = os.getcwd(),
51 | verbose: int = 1,
52 | radii_table: Union[str, dict] = "default",
53 | ):
54 | """
55 | Wrapper class for py2sambvca functions.
56 |
57 | Call this class to instantiate a py2sambvca object, which has methods to write input, call SambVca,
58 | and retrieve output.
59 |
60 | Parameters:
61 | xyz_filepath (str): Location of .xyz molecular coordinates file for writing input data
62 | sphere_center_atom_ids (list): ID of atoms defining the sphere center
63 | z_ax_atom_ids (list): ID of atoms for z-axis
64 | xz_plane_atoms_ids (list): ID of atoms for xz-plane
65 | atoms_to_delete_ids (list): ID of atoms to be deleted (default None)
66 | sphere_radius (float): Sphere radius in Angstrom (default 3.5)
67 | displacement (float): Displacement of oriented molecule from sphere center in Angstrom (default 0.0)
68 | mesh_size (float): Mesh size for numerical integration (default 0.10)
69 | remove_H (int): 0/1 Do not remove/remove H atoms from Vbur calculation (default 1)
70 | orient_z (int): 0/1 Molecule oriented along negative/positive Z-axis (default 1)
71 | write_surf_files (int): 0/1 Do not write/write files for top and bottom surfaces (default 1)
72 | path_to_sambvcax (str): Path to the SambVca executable. Only needed to use py2sambvca.calc()( default "/path/to/executable/sambvca.x")
73 | working_dir (path): Path to the working directory where the output and input files are generated (default os.getcwd())
74 | verbose (int): 0 for no output, 1 for some output, 2 for the most output
75 | radii_table (dict or str): atomic symbols (ie. H, LI) and radii (Angstrom), "default" (bondii radii*1.17), or "vdw" for van Der Waals
76 | """
77 | # if atoms are requested to be deleted, assign them and the number of them
78 | if atoms_to_delete_ids is not None:
79 | self.n_atoms_to_delete = len(atoms_to_delete_ids)
80 | self.atoms_to_delete_ids = atoms_to_delete_ids
81 | else: # otherwise, set to none to avoid bad writes in the future
82 | self.n_atoms_to_delete = None
83 | self.atoms_to_delete_ids = None
84 |
85 | # various other parameters
86 | self.sphere_center_atom_ids = sphere_center_atom_ids
87 | self.n_sphere_center_atoms = len(sphere_center_atom_ids)
88 | self.z_ax_atom_ids = z_ax_atom_ids
89 | self.n_z_atoms = len(z_ax_atom_ids)
90 | self.xz_plane_atoms_ids = xz_plane_atoms_ids
91 | self.n_xz_plane_atoms = len(xz_plane_atoms_ids)
92 | self.sphere_radius = sphere_radius
93 | self.displacement = displacement
94 | self.mesh_size = mesh_size
95 | self.remove_H = remove_H
96 | self.orient_z = orient_z
97 | self.write_surf_files = write_surf_files
98 |
99 | # open the xyz file, read the data
100 | if xyz_filepath.endswith(".xyz"):
101 | with open(xyz_filepath, "r") as file:
102 | self.xyz_data = file.readlines()
103 | else:
104 | raise RuntimeError(f"Invalid xyz_filepath ({xyz_filepath})")
105 |
106 | # assign the path to the calculator
107 | self.path_to_sambvcax = path_to_sambvcax
108 |
109 | # assign working directory path
110 | self.working_dir = working_dir
111 | if not os.path.exists(self.working_dir):
112 | os.makedirs(self.working_dir)
113 |
114 | # integer verbosity
115 | self.verbose = verbose
116 |
117 | # make results accesible from object directly
118 | self.total_results = None
119 | self.quadrant_results = None
120 | self.octant_results = None
121 |
122 | # data table
123 | self.__radii_table = format_radii_table(
124 | table_lookup[radii_table] if type(radii_table) is str else radii_table
125 | )
126 |
127 | def write_input(self):
128 | """
129 | Write input for the Sambvca buried-volume Fortran calculator based on the data entered
130 | when object was initialized.
131 |
132 | """
133 | # make file in the same cwd, which is where sambvca will look
134 | with open(os.path.join(self.working_dir, "py2sambvca_input.inp"), "w") as file:
135 | # write atoms to be deleted, if there are any
136 | if self.atoms_to_delete_ids is not None:
137 | file.writelines(
138 | [
139 | str(self.n_atoms_to_delete) + "\n",
140 | str(self.atoms_to_delete_ids)
141 | .replace(",", "")
142 | .replace("[", "")
143 | .replace("]", "")
144 | + "\n",
145 | ]
146 | )
147 | else:
148 | file.write("0\n")
149 | # write user settings
150 | file.writelines(
151 | [
152 | str(self.n_sphere_center_atoms) + "\n",
153 | str(self.sphere_center_atom_ids)
154 | .replace(",", "")
155 | .replace("[", "")
156 | .replace("]", "")
157 | + "\n",
158 | str(self.n_z_atoms) + "\n",
159 | str(self.z_ax_atom_ids)
160 | .replace(",", "")
161 | .replace("[", "")
162 | .replace("]", "")
163 | + "\n",
164 | str(self.n_xz_plane_atoms) + "\n",
165 | str(self.xz_plane_atoms_ids)
166 | .replace(",", "")
167 | .replace("[", "")
168 | .replace("]", "")
169 | + "\n",
170 | str(self.sphere_radius) + "\n",
171 | str(self.displacement) + "\n",
172 | str(self.mesh_size) + "\n",
173 | str(self.remove_H) + "\n",
174 | str(self.orient_z) + "\n",
175 | str(self.write_surf_files) + "\n",
176 | "103\n",
177 | ]
178 | )
179 | # write radii
180 | file.writelines(self.__radii_table)
181 | # write the atom coordinates
182 | file.writelines(self.xyz_data)
183 |
184 | def calc(self):
185 | """
186 | Call SambVca based on the executable path given on initialization of py2sambvca.
187 |
188 | Be sure to write_input() before calling this function.
189 |
190 | """
191 | if not os.path.exists(self.path_to_sambvcax):
192 | raise RuntimeError(
193 | "sambvca executable not found at provided path ({:s})".format(
194 | self.path_to_sambvcax
195 | )
196 | )
197 | try:
198 | result = subprocess.run(
199 | [
200 | self.path_to_sambvcax,
201 | os.path.join(self.working_dir, "py2sambvca_input"),
202 | ],
203 | stderr=subprocess.DEVNULL,
204 | )
205 | result.check_returncode()
206 | return True
207 | except subprocess.CalledProcessError as e:
208 | if self.verbose:
209 | print(e)
210 | return False
211 |
212 | def get_buried_vol(self):
213 | """
214 | Retrieves the buried volume from a SambVca output file in the current working directory
215 | or False if it cannot find it.
216 | """
217 | warn(
218 | "get_buried_vol is deprecated and will be removed in py2sambvca 2.0"
219 | "Use get_buried_volume instead",
220 | DeprecationWarning,
221 | stacklevel=2,
222 | )
223 | m = self.get_regex(r"^[ ]{4}The %V Bur of the molecule is:[ ]{4,5}(\d*\.\d*)$")
224 | return float(m[1])
225 |
226 | def clean_files(self):
227 | """
228 | Remove all input and output files associated with py2sambvca.
229 |
230 | """
231 | for f in glob.glob(os.path.join(self.working_dir, "py2sambvca_input*")):
232 | os.remove(f)
233 |
234 | def parse_output(self):
235 | """Parse output file for total, quadrant, and octant results.
236 |
237 | Returns:
238 | total_results (dict): Results for total
239 | quadrant_results (dict): Quadrant-decomposed results
240 | octant_results (dict): Octant-decomposed results
241 | """
242 | try:
243 | return self._parse_output()
244 | except TypeError as te:
245 | raise RuntimeError(
246 | "Unable to retrieve output from sambvca21 output file ({:s}). "
247 | "Did sambvca21 termiante normally?".format(
248 | os.path.join(self.working_dir, "py2sambvca_input.out")
249 | )
250 | ) from te
251 |
252 | def _parse_output(self):
253 | """Helper funciton for parse output to better wrap with try/except."""
254 | # total results
255 | m1 = self.get_regex(r"^\s*(\d*\.\d*)\s*(\d*\.\d*)\s*(\d*\.\d*)\s*(\d*\.\d*)$")
256 |
257 | m2 = self.get_regex(r"^\s*(\d*\.\d*)\s*(\d*\.\d*)\s*(\d*\.\d*)$")
258 |
259 | total_results = {
260 | "free_volume": float(m1[1]),
261 | "buried_volume": float(m1[2]),
262 | "total_volume": float(m1[3]),
263 | "exact_volume": float(m1[4]),
264 | "percent_buried_volume": float(m2[2]),
265 | "percent_free_volume": float(m2[1]),
266 | "percent_total_volume": float(m2[3]),
267 | }
268 |
269 | # quadrant and octant results
270 | quadrant_regions = ["SW", "NW", "NE", "SE"]
271 | quadrant_results = {
272 | "free_volume": {},
273 | "buried_volume": {},
274 | "total_volume": {},
275 | "percent_free_volume": {},
276 | "percent_buried_volume": {},
277 | }
278 | octant_regions = [
279 | r"SW\-z",
280 | r"NW\-z",
281 | r"NE\-z",
282 | r"SE\-z",
283 | r"SW\+z",
284 | r"NW\+z",
285 | r"NE\+z",
286 | r"SE\+z",
287 | ]
288 | octant_results = quadrant_results.copy()
289 |
290 | for region, result_dict in zip(
291 | [quadrant_regions, octant_regions],
292 | [quadrant_results, octant_results],
293 | ):
294 | for r in region:
295 | m = self.get_regex(
296 | r"^ "
297 | + r
298 | + r"\s*(\d*\.\d*)\s*(\d*\.\d*)\s*(\d*\.\d*)\s*(\d*\.\d*)\s*(\d*\.\d*)$"
299 | )
300 | result_dict["free_volume"][r.replace("\\", "")] = float(m[1])
301 | result_dict["buried_volume"][r.replace("\\", "")] = float(m[2])
302 | result_dict["total_volume"][r.replace("\\", "")] = float(m[3])
303 | result_dict["percent_free_volume"][r.replace("\\", "")] = float(m[4])
304 | result_dict["percent_buried_volume"][r.replace("\\", "")] = float(m[5])
305 |
306 | self.total_results = total_results
307 | self.quadrant_results = quadrant_results
308 | self.octant_results = octant_results
309 | return total_results, quadrant_results, octant_results
310 |
311 | def get_quadrant_result(self, key):
312 | """Get a result for the quadrants.
313 |
314 | Args:
315 | key (str): type of result
316 |
317 | Returns:
318 | dict: values requested per quadrant
319 | """
320 | return self.get(key, quadrant=True)
321 |
322 | def get_quadrant_free_volume(self):
323 | """Get the quadrant free volume.
324 |
325 | Returns:
326 | dict: free volume per quadrant
327 | """
328 | return self.get_quadrant_result("free_volume")
329 |
330 | def get_quadrant_buried_volume(self):
331 | """Get the quadrant buried volume.
332 |
333 | Returns:
334 | dict: buried volume per quadrant
335 | """
336 | return self.get_quadrant_result("buried_volume")
337 |
338 | def get_quadrant_total_volume(self):
339 | """Get the quadrant total volume.
340 |
341 | Returns:
342 | dict: total volume per quadrant
343 | """
344 | return self.get_quadrant_result("total_volume")
345 |
346 | def get_quadrant_percent_buried_volume(self):
347 | """Get the quadrant percent buried volume
348 |
349 | Returns:
350 | dict: total percent buried volume per quadrant
351 | """
352 | return self.get_quadrant_result("percent_buried_volume")
353 |
354 | def get_quadrant_percent_free_volume(self):
355 | """Get the total percent buried volume
356 |
357 | Returns:
358 | dict: total percent buried volume per quadrant
359 | """
360 | return self.get_quadrant_result("percent_buried_volume")
361 |
362 | def get_octant_result(self, key):
363 | """Get a result for the octant.
364 |
365 | Args:
366 | key (str): type of result
367 |
368 | Returns:
369 | dict: values requested per octant
370 | """
371 | return self.get(key, octant=True)
372 |
373 | def get_octant_free_volume(self):
374 | """Get the octant free volume.
375 |
376 | Returns:
377 | dict: free volume per octant
378 | """
379 | return self.get_octant_result("free_volume")
380 |
381 | def get_octant_buried_volume(self):
382 | """Get the octant buried volume.
383 |
384 | Returns:
385 | dict: buried volume per octant
386 | """
387 | return self.get_octant_result("buried_volume")
388 |
389 | def get_octant_total_volume(self):
390 | """Get the octant total volume.
391 |
392 | Returns:
393 | dict: total volume per octant
394 | """
395 | return self.get_octant_result("total_volume")
396 |
397 | def get_octant_percent_buried_volume(self):
398 | """Get the octant percent buried volume
399 |
400 | Returns:
401 | dict: total percent buried volume per octant
402 | """
403 | return self.get_octant_result("percent_buried_volume")
404 |
405 | def get_octant_percent_free_volume(self):
406 | """Get the octant percent buried volume
407 |
408 | Returns:
409 | dict: total percent buried volumes per octant
410 | """
411 | return self.get_octant_result("percent_buried_volume")
412 |
413 | def get_free_volume(self):
414 | """Get the free volume.
415 |
416 | Returns:
417 | float: free volume
418 | """
419 | return self.get("free_volume")
420 |
421 | def get_buried_volume(self):
422 | """Get the buried volume.
423 |
424 | Returns:
425 | float: buried volume
426 | """
427 | return self.get("buried_volume")
428 |
429 | def get_exact_volume(self):
430 | """Get the exact volume.
431 |
432 | Returns:
433 | float: exact volume
434 | """
435 | return self.get("exact_volume")
436 |
437 | def get_total_volume(self):
438 | """Get the total volume.
439 |
440 | Returns:
441 | float: total volume
442 | """
443 | return self.get("total_volume")
444 |
445 | def get_percent_buried_volume(self):
446 | """Get the total percent buried volume
447 |
448 | Returns:
449 | float: total percent buried volume
450 | """
451 | return self.get("percent_buried_volume")
452 |
453 | def get_percent_free_volume(self):
454 | """Get the total percent buried volume
455 |
456 | Returns:
457 | float: total percent buried volume
458 | """
459 | return self.get("percent_free_volume")
460 |
461 | def get_percent_total_volume(self):
462 | """Get the percent total volume
463 |
464 | Returns:
465 | float: percent total volume
466 | """
467 | return self.get("percent_total_volume")
468 |
469 | def get_regex(self, regex):
470 | """Open the output file and search for a line matching a regex pattern.
471 |
472 | Args:
473 | regex (str): regex to search
474 | """
475 | try:
476 | with open(
477 | os.path.join(self.working_dir, "py2sambvca_input.out"), "r"
478 | ) as file:
479 | file_data = file.readlines()
480 | except FileNotFoundError:
481 | raise RuntimeError(
482 | "Results not yet retrieved ({:s} not found). "
483 | "Call p2s.run() or p2s.calc() before using this function.".format(
484 | os.path.join(self.working_dir, "py2sambvca_input.out")
485 | )
486 | )
487 | pattern = re.compile(regex)
488 | for line in file_data:
489 | m = pattern.search(line)
490 | if m:
491 | return m
492 |
493 | def get(self, key, quadrant=False, octant=False):
494 | """
495 | Accept a key in the output of parse results, return it.
496 | """
497 | if self.total_results is None:
498 | raise RuntimeError(
499 | "Results not yet retrieved ({} not found). "
500 | "Call p2s.run() or p2s.parse_output() before using this function.".format(
501 | os.path.join(self.working_dir, "py2sambvca_input.out")
502 | )
503 | )
504 | try:
505 | if octant and quadrant:
506 | raise RuntimeError("Specify either quadrant or octant, not both")
507 | elif quadrant:
508 | return self.quadrant_results[key]
509 | elif octant:
510 | return self.octant_results[key]
511 | else:
512 | return self.total_results[key]
513 | except KeyError as e:
514 | if self.verbose:
515 | print(e)
516 | raise RuntimeError(f'Invalid parameter name "{key}"')
517 |
518 | def run(self):
519 | self.write_input()
520 | self.calc()
521 | self.parse_output()
522 | self.clean_files()
523 | return self.total_results, self.quadrant_results, self.octant_results
524 |
--------------------------------------------------------------------------------
/py2sambvca/radii_tables/__init__.py:
--------------------------------------------------------------------------------
1 | from py2sambvca.radii_tables.default import default_radii_table
2 | from py2sambvca.radii_tables.vanDerWaals import vdw_radii_table
3 | from py2sambvca.radii_tables.format_table import format_radii_table
4 |
5 | table_lookup = {
6 | "default": default_radii_table,
7 | "vdw": vdw_radii_table,
8 | }
9 |
10 | __all__ = ["format_radii_table", "table_lookup"]
11 |
--------------------------------------------------------------------------------
/py2sambvca/radii_tables/default.py:
--------------------------------------------------------------------------------
1 | # These are the radii used in the original Cavallo paper, which are just the vDW
2 | # radii multiplied by 1.17
3 | default_radii_table = {
4 | "H": 1.28,
5 | "HE": 1.64,
6 | "LI": 2.13,
7 | "BE": 1.79,
8 | "B": 2.25,
9 | "C": 1.99,
10 | "N": 1.81,
11 | "O": 1.78,
12 | "F": 1.72,
13 | "NE": 1.80,
14 | "NA": 2.66,
15 | "MG": 2.02,
16 | "AL": 2.15,
17 | "SI": 2.46,
18 | "P": 2.11,
19 | "S": 2.11,
20 | "CL": 2.05,
21 | "AR": 2.20,
22 | "K": 3.22,
23 | "CA": 2.70,
24 | "SC": 2.52,
25 | "TI": 2.47,
26 | "V": 2.42,
27 | "CR": 2.41,
28 | "MN": 2.40,
29 | "FE": 2.39,
30 | "CO": 2.34,
31 | "NI": 1.91,
32 | "CU": 1.64,
33 | "ZN": 1.63,
34 | "GA": 2.19,
35 | "GE": 2.47,
36 | "AS": 2.16,
37 | "SE": 2.22,
38 | "BR": 2.16,
39 | "KR": 2.36,
40 | "RB": 3.55,
41 | "SR": 2.91,
42 | "Y": 2.71,
43 | "ZR": 2.61,
44 | "NB": 2.55,
45 | "MO": 2.54,
46 | "TC": 2.53,
47 | "RU": 2.49,
48 | "RH": 2.46,
49 | "PD": 1.91,
50 | "AG": 2.01,
51 | "CD": 1.85,
52 | "IN": 2.26,
53 | "SN": 2.54,
54 | "SB": 2.41,
55 | "TE": 2.41,
56 | "I": 2.32,
57 | "XE": 2.53,
58 | "CS": 4.01,
59 | "BA": 3.14,
60 | "LA": 2.84,
61 | "CE": 2.83,
62 | "PR": 2.81,
63 | "ND": 2.80,
64 | "PM": 2.78,
65 | "SM": 2.76,
66 | "EU": 2.75,
67 | "GD": 2.74,
68 | "TB": 2.73,
69 | "DY": 2.70,
70 | "HO": 2.69,
71 | "ER": 2.68,
72 | "TM": 2.66,
73 | "YB": 2.64,
74 | "LU": 2.62,
75 | "HF": 2.61,
76 | "TA": 2.60,
77 | "W": 2.55,
78 | "RE": 2.53,
79 | "OS": 2.53,
80 | "IR": 2.49,
81 | "PT": 2.01,
82 | "AU": 1.94,
83 | "HG": 1.81,
84 | "TL": 2.29,
85 | "PB": 2.36,
86 | "BI": 2.42,
87 | "PO": 2.30,
88 | "AT": 2.36,
89 | "RN": 2.57,
90 | "FR": 4.07,
91 | "RA": 3.31,
92 | "AC": 2.89,
93 | "TH": 2.87,
94 | "PA": 2.84,
95 | "U": 2.18,
96 | "NP": 2.80,
97 | "PU": 2.84,
98 | "AM": 2.85,
99 | "CM": 2.87,
100 | "BK": 2.85,
101 | "CF": 2.87,
102 | "ES": 2.87,
103 | "FM": 2.87,
104 | "M": 2.88,
105 | "NO": 2.88,
106 | "LR": 2.88,
107 | }
108 |
--------------------------------------------------------------------------------
/py2sambvca/radii_tables/format_table.py:
--------------------------------------------------------------------------------
1 | def format_radii_table(radii_table: dict):
2 | """Formats a dictionary of atomic symbol: radii to a list of strings for sambvca
3 |
4 | Args:
5 | radii_table (dict): Mapping of atomic symbols to radii, i.e. {'H':1.0,'LI':1.1}
6 | """
7 | output_list = []
8 | for element, radius in radii_table.items():
9 | # sambvca expects each row in the radii table to look like this:
10 | # "C 1.11" with _exactly_ six spaces for elements with two letter
11 | # abbreviations and _exactly_ seven for elements with one letter
12 | # abbreviations, and then three digits for the radius
13 | output_list.append(
14 | element
15 | + (" " if len(element) == 1 else "")
16 | + " "
17 | + str(round(radius, 2))
18 | + "\n"
19 | )
20 | return output_list
21 |
--------------------------------------------------------------------------------
/py2sambvca/radii_tables/vanDerWaals.py:
--------------------------------------------------------------------------------
1 | # this radius table just returns to the plain old van Der Waals radii,
2 | # which are the radii reported in the original Cavallo paper divided by
3 | # 1.17
4 | from py2sambvca.radii_tables.default import default_radii_table
5 |
6 | vdw_radii_table = {
7 | element: radius / 1.17 for element, radius in default_radii_table.items()
8 | }
9 |
--------------------------------------------------------------------------------
/py2sambvca_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacksonBurns/py2sambvca/4b82315b0ef7da4e4f76be1802c38611ca3d9326/py2sambvca_logo.png
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import codecs
3 | import pathlib
4 | from setuptools import setup
5 |
6 | # The directory containing this file
7 | cwd = pathlib.Path(__file__).parent
8 |
9 | # The text of the README file
10 | README = (cwd / "README.md").read_text()
11 |
12 |
13 | def read(rel_path):
14 | here = os.path.abspath(os.path.dirname(__file__))
15 | with codecs.open(os.path.join(here, rel_path), 'r') as fp:
16 | return fp.read()
17 |
18 |
19 | def get_version(rel_path):
20 | for line in read(rel_path).splitlines():
21 | if line.startswith('__version__'):
22 | delim = '"' if '"' in line else "'"
23 | return line.split(delim)[1]
24 | raise RuntimeError("Unable to find version string.")
25 |
26 |
27 | # This call to setup() does all the work
28 | setup(
29 | name="py2sambvca",
30 | python_requires='>=3.6',
31 | version=get_version("py2sambvca/__init__.py"),
32 | description="Simple thin client to interface python scripts with SambVca catalytic pocket Fortran calculator.",
33 | long_description=README,
34 | long_description_content_type="text/markdown",
35 | url="https://github.com/JacksonBurns/py2sambvca",
36 | author="Jackson Burns",
37 | license="GNU GPLv3",
38 | classifiers=[
39 | "Programming Language :: Python :: 3.6",
40 | "Programming Language :: Python :: 3.7",
41 | "Programming Language :: Python :: 3.8",
42 | "Programming Language :: Python :: 3.9",
43 | "Programming Language :: Python :: 3.10",
44 | "Programming Language :: Python :: 3.11",
45 | "Programming Language :: Python :: 3.12",
46 | ],
47 | packages=["py2sambvca", "py2sambvca.radii_tables"],
48 | include_package_data=True
49 | )
50 |
--------------------------------------------------------------------------------
/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacksonBurns/py2sambvca/4b82315b0ef7da4e4f76be1802c38611ca3d9326/test/__init__.py
--------------------------------------------------------------------------------
/test/data/README.txt:
--------------------------------------------------------------------------------
1 | This directory contains two files used in the unit tests:
2 | - static_output.txt: this is an output file generated by sambvca21 as provided by @andre-cloud which is known to cause
3 | issues when retrieving the percent free volume (see https://github.com/JacksonBurns/py2sambvca/issues/31)
4 | - nhc.xyz: taken from the sambvca examples website, this set of coordinates is used in the tests that dynamically run
5 | py2sambvca
--------------------------------------------------------------------------------
/test/data/nhc.xyz:
--------------------------------------------------------------------------------
1 | 72
2 | Ru01 SCF Done: -3672.22033654 A.U.
3 | N 1.069943 0.405072 1.003703
4 | C 0.762569 0.756202 2.383661
5 | C -0.758833 0.916282 2.357600
6 | N -1.119693 0.336882 1.070505
7 | C -0.039567 0.108733 0.292687
8 | C -2.472926 0.210117 0.667779
9 | C 2.405211 0.232212 0.559797
10 | C -3.163057 -0.962918 0.967400
11 | C -4.497820 -1.074402 0.615422
12 | C -5.176490 -0.033588 -0.040830
13 | C -4.444854 1.128452 -0.338855
14 | C -3.108239 1.256533 0.000673
15 | C 3.032654 -1.003392 0.687291
16 | C 3.088846 1.322492 0.022646
17 | C 4.407673 1.169665 -0.367869
18 | C 5.075590 -0.061382 -0.258142
19 | C 4.355996 -1.133919 0.290811
20 | C -2.445394 -2.110684 1.608068
21 | C -2.341692 2.483980 -0.383312
22 | C 2.380185 2.627077 -0.174440
23 | C 2.275186 -2.188155 1.201635
24 | Se -0.069316 -0.497867 -1.421613
25 | N -6.502659 -0.148200 -0.381881
26 | N 6.381824 -0.214085 -0.701345
27 | C 7.231253 0.959693 -0.770937
28 | C 7.663175 1.493038 0.588002
29 | C 7.026616 -1.493773 -0.462804
30 | C 8.357894 -1.667532 -1.166906
31 | C -7.162917 0.845838 -1.197225
32 | C -7.756012 1.996840 -0.396897
33 | C -7.287393 -1.298011 0.004992
34 | C -7.190423 -2.464112 -0.969189
35 | H 1.280213 1.672745 2.674118
36 | H 1.082624 -0.042972 3.061509
37 | H -1.065892 1.967109 2.402357
38 | H -1.254312 0.385326 3.173077
39 | H -5.002314 -2.006781 0.831208
40 | H -4.919912 1.958740 -0.844310
41 | H 4.908840 2.030750 -0.788488
42 | H 4.819619 -2.102164 0.415151
43 | H -1.616861 -2.436252 0.972908
44 | H -3.117807 -2.955567 1.761714
45 | H -2.022615 -1.834838 2.578445
46 | H -2.993393 3.220452 -0.854947
47 | H -1.866707 2.957068 0.480511
48 | H -1.548525 2.217512 -1.088461
49 | H 1.528738 2.492200 -0.847508
50 | H 3.047991 3.373532 -0.606116
51 | H 1.990773 3.025347 0.766892
52 | H 2.922729 -3.062442 1.277960
53 | H 1.841920 -2.001858 2.188050
54 | H 1.451217 -2.422790 0.520802
55 | H 8.106267 0.708239 -1.369398
56 | H 6.716735 1.735886 -1.338377
57 | H 6.794055 1.758563 1.193657
58 | H 8.240366 0.744047 1.136598
59 | H 8.287755 2.382843 0.474651
60 | H 6.353761 -2.270633 -0.833248
61 | H 7.156061 -1.681356 0.616032
62 | H 8.683065 -2.703611 -1.051511
63 | H 9.144489 -1.033680 -0.752854
64 | H 8.269804 -1.460307 -2.235995
65 | H -7.954985 0.340268 -1.757676
66 | H -6.463820 1.221837 -1.949332
67 | H -8.498394 1.629874 0.316268
68 | H -6.982094 2.520132 0.167862
69 | H -8.246898 2.716549 -1.057534
70 | H -8.328987 -0.973185 0.088822
71 | H -6.994514 -1.612610 1.010219
72 | H -7.803966 -3.303676 -0.631509
73 | H -6.159176 -2.807792 -1.067955
74 | H -7.538413 -2.168403 -1.961987
75 |
--------------------------------------------------------------------------------
/test/data/static_output.out:
--------------------------------------------------------------------------------
1 |
2 | ---------------------------------------------------
3 | | |
4 | | S A M B V C A 2.1 |
5 | | |
6 | | Release 29-06-2019 |
7 | | |
8 | | Molecular Buried Volume Calculator |
9 | | |
10 | | http://www.molnac.unisa.it/OM-tools/SambVca |
11 | | |
12 | | L. Cavallo & Z. Cao Email: lcavallo@unisa.it |
13 | | |
14 | ---------------------------------------------------
15 |
16 | Checking: No. of atoms to be removed : 0
17 | Checking: No. of atoms defining the sphere center : 1
18 | Checking: Atoms defining the sphere center :
19 | 65
20 | Checking : No. of atoms defining the Z-axis : 2
21 | 65 64
22 | Checking : No. of atoms defining the XZ-plane : 3
23 | 64 65 82
24 | Checking: Molecule will be aligned along the Z+ direction
25 | Checking: Vbur sphere radius : 7.500
26 | Checking: Displacement from origin : 0.000
27 | Checking: Bin width : 0.100
28 | Checking: Removing H atoms
29 | Checking: Outputting steric map
30 | Checking: No. of atom types in your database : 103
31 | Checking: Atom labels and radius in the database :
32 | H 1.280
33 | HE 1.640
34 | LI 2.130
35 | BE 1.790
36 | B 2.250
37 | C 1.990
38 | N 1.810
39 | O 1.780
40 | F 1.720
41 | NE 1.800
42 | NA 2.660
43 | MG 2.020
44 | AL 2.150
45 | SI 2.460
46 | P 2.110
47 | S 2.110
48 | CL 2.050
49 | AR 2.200
50 | K 3.220
51 | CA 2.700
52 | SC 2.520
53 | TI 2.470
54 | V 2.420
55 | CR 2.410
56 | MN 2.400
57 | FE 2.390
58 | CO 2.340
59 | NI 1.910
60 | CU 1.640
61 | ZN 1.630
62 | GA 2.190
63 | GE 2.470
64 | AS 2.160
65 | SE 2.220
66 | BR 2.160
67 | KR 2.360
68 | RB 3.550
69 | SR 2.910
70 | Y 2.710
71 | ZR 2.610
72 | NB 2.550
73 | MO 2.540
74 | TC 2.530
75 | RU 2.490
76 | RH 2.460
77 | PD 1.910
78 | AG 2.010
79 | CD 1.850
80 | IN 2.260
81 | SN 2.540
82 | SB 2.410
83 | TE 2.410
84 | I 2.320
85 | XE 2.530
86 | CS 4.010
87 | BA 3.140
88 | LA 2.840
89 | CE 2.830
90 | PR 2.810
91 | ND 2.800
92 | PM 2.780
93 | SM 2.760
94 | EU 2.750
95 | GD 2.740
96 | TB 2.730
97 | DY 2.700
98 | HO 2.690
99 | ER 2.680
100 | TM 2.660
101 | YB 2.640
102 | LU 2.620
103 | HF 2.610
104 | TA 2.600
105 | W 2.550
106 | RE 2.530
107 | OS 2.530
108 | IR 2.490
109 | PT 2.010
110 | AU 1.940
111 | HG 1.810
112 | TL 2.290
113 | PB 2.360
114 | BI 2.420
115 | PO 2.300
116 | AT 2.360
117 | RN 2.570
118 | FR 4.070
119 | RA 3.310
120 | AC 2.890
121 | TH 2.870
122 | PA 2.840
123 | U 2.180
124 | NP 2.800
125 | PU 2.840
126 | AM 2.850
127 | CM 2.870
128 | BK 2.850
129 | CF 2.870
130 | ES 2.870
131 | FM 2.870
132 | M 2.880
133 | NO 2.880
134 | LR 2.880
135 |
136 | Checking: No. of atoms of input frame : 83
137 |
138 | Checking: Coordinates of input frame :
139 | GEOMETRY
140 |
141 | Results: Coordinates of rotated frame :
142 | GEOMETRY
143 |
144 | Results : Volumes in Angs^3
145 |
146 | N of voxels examined : 1767063
147 | Volume of voxel : 0.0010000
148 |
149 |
150 | V Free V Buried V Total V Exact
151 | 1169.4 597.3 1766.7 1767.1
152 |
153 | Buried volume = 597.29 Angs^3
154 |
155 | %V Free %V Buried % V Tot/V Ex
156 | 66.2 33.8 100.0
157 |
158 | The %V Bur of the molecule is: 33.8
159 |
160 | Quadrants analysis
161 | Quadrant V f V b V t %V f %V b
162 | SW 210.1 231.5 441.7 47.6 52.4
163 | NW 373.5 68.2 441.7 84.6 15.4
164 | NE 276.6 165.1 441.7 62.6 37.4
165 | SE 309.2 132.5 441.7 70.0 30.0
166 |
167 | Octants analysis
168 | Octant V f V b V t %V f %V b
169 | SW-z 137.4 83.5 220.8 62.2 37.8
170 | NW-z 210.0 10.8 220.8 95.1 4.9
171 | NE-z 206.3 14.5 220.8 93.4 6.6
172 | SE-z 175.5 45.3 220.8 79.5 20.5
173 | SW+z 72.7 148.1 220.8 32.9 67.1
174 | NW+z 163.5 57.4 220.8 74.0 26.0
175 | NE+z 70.3 150.6 220.8 31.8 68.2
176 | SE+z 133.7 87.2 220.8 60.5 39.5
177 |
178 | No. of frames analyzed : 1
179 |
180 | Normal termination
181 |
--------------------------------------------------------------------------------
/test/sambvca21.f:
--------------------------------------------------------------------------------
1 | c=========================================================================c
2 | program SAMBVCA
3 | c=========================================================================c
4 | c c
5 | c SambVca: Calculation of the Buried Volume of Organometallic Ligands c
6 | c c
7 | c RELEASE 2.1 20/07/2019 c
8 | c c
9 | c Copyright (C) 2008 - 2011 Luigi Cavallo, University of Salerno, Italy c
10 | c Copyright (C) 2012 - 2019 Luigi Cavallo, KAUST, Saudi Arabia c
11 | c c
12 | c Publications using this tool should cite : c
13 | c Poater, A.; Cosenza, B.; Correa, A.; Giudice, S.; Ragone, F.; Scarano, c
14 | c V.; Cavallo, L. SambVca: A Web Application for the Calculation of the c
15 | c Buried Volume of c N-Heterocyclic Carbene Ligands c
16 | c Eur. J. Inorg. Chem. 2009, 1759 c
17 | c c
18 | c For any questions/suggestions regarding this code, please contact c
19 | c Prof. Luigi Cavallo : luigi.cavallo@kaust.edu.sa; lcavallo@unisa.it c
20 | c c
21 | c This program is free software; you can redistribute it and/or modify c
22 | c it under the terms of the GNU General Public License as published by c
23 | c the Free Software Foundation; either version 1, or (at your option) c
24 | c any later version. c
25 | c c
26 | c This program is distributed WITHOUT ANY WARRANTY; without even c
27 | c the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR c
28 | c PURPOSE. See the c GNU General Public License for more details. c
29 | c c
30 | c For the GNU General Public License, see http://www.gnu.org/licenses/ c
31 | c c
32 | c Work using this software should cite: c
33 | c Falivene L. et a.. Nat. Chem. 2019, DOI:10.1038/s41557-019-0319-5 c
34 | c c
35 | c=========================================================================c
36 |
37 | IMPLICIT NONE
38 |
39 | c=========================================================================
40 | c Constant Parameters
41 | c=========================================================================
42 | Integer, parameter :: NatomMax = 100000 ! max number of atoms
43 | Integer, parameter :: NpsMax = 10000 ! max number of points for distribution
44 | Real*8, parameter :: PI = 3.1415926535 ! Pi
45 |
46 | c=========================================================================
47 | c Variables to define the system
48 | c=========================================================================
49 | Integer :: natoms ! # of atoms
50 | Integer :: natoms_new ! # of atoms after removing the unnecessary atoms
51 | Integer :: nTypes ! # of atom types
52 |
53 | Real*8, Allocatable :: AtomTypeRadius(:) ! radius of atom types
54 | Real*8, Allocatable :: Coord(:) ! coordinates from input
55 | Real*8, Allocatable :: Coord_new(:) ! coordinates after removing unnecessary atoms
56 | Real*8, Allocatable :: AtomRadius2(:) ! square of atom radii
57 | Real*8, Allocatable :: AtomRadius2_new(:) ! square of atom radii after removing unnecessary atoms
58 |
59 | Character*50 :: Title ! Frame title, line after natoms line from input
60 | Character*4, Allocatable :: AtomNames(:) ! atom names from input
61 | Character*4, Allocatable :: AtomNames_new(:) ! record temperary atom names when removing atoms
62 | Character*4, Allocatable :: AtomTypeName(:) ! atom names in the data base
63 |
64 | c=========================================================================
65 | c Variables to reorient the system
66 | c=========================================================================
67 | Real*8 :: GC(3) ! geometry center
68 | Real*8 :: x_axis(3) ! unit vector for x axis
69 | Real*8 :: y_axis(3) ! unit vector for y axis
70 | Real*8 :: z_axis(3) ! unit vector for z axis
71 | Real*8 :: z_GC(3) ! geometry center of atoms defining z-axis
72 | Real*8 :: x_temp_GC(3) ! geometry center of atoms
73 | Real*8 :: bench_vec(3) ! align to bench_vec
74 | Real*8 :: Initial(3) ! initial z
75 |
76 | c=========================================================================
77 | c Variables to control the calculation
78 | c=========================================================================
79 | Integer :: RemoveH ! if RemoveH = 0 do not remove H atoms. If = 1, remove H atoms
80 | Integer :: DoMap ! if DoMap: =0 do not print surfaces. If = 1 print surfaces
81 | Integer :: AlignZ ! if AlignZ =0, align the molecule along -z; if =1 along +z
82 | Integer :: IfOverlap ! if IfOverlap = 0 voxel is free. If = 1 voxel is buried
83 | Integer :: IndFrame ! index of the molecular frame currently calculated
84 | Integer :: NumPoints ! # of grid points to be scanned: radius / binsize * 2 + 1
85 | Integer :: NumInside ! # of points inside the sphere
86 | Integer :: nAtomDel ! # of atoms to be deleted
87 | Integer :: nAtomGC ! # of atoms to define a geometry center
88 | Integer :: nAtom_z ! # of atoms to define z axis
89 | Integer :: nAtom_xz ! # of atoms to define a second vector
90 |
91 | Integer, allocatable :: IndDel(:) ! index of the atoms to be deleted
92 | Integer, allocatable :: IndGC(:) ! index of the atoms to define a geometry center
93 | Integer, Allocatable :: IndAxis(:) ! index of the atoms to define an axis
94 | Integer, Allocatable :: Indxz(:) ! index of these atoms
95 |
96 | Integer :: npoints ! # of grid points for Vbur distributions
97 | Integer :: VburDist(NpsMax) ! distribution of the Vbur among different frames
98 | Integer :: Vbur4Dist(4, NpsMax) ! distribution of the Vbur4 among different frames
99 | Integer :: Vbur8Dist(8, NpsMax) ! distribution of the Vbur8 among different frames
100 | Integer :: IfWeb ! Hardcoded: If IfWeb = 1 print section headers for the web server
101 |
102 | Real*8 :: Wgth ! weigth of point, 1.0 inside sphere, 0.5 at surface
103 | Real*8 :: Radius ! radius of sphere
104 | Real*8 :: Radius2 ! radius of sphere squared
105 | Real*8 :: Disp ! displacement from sphere center
106 | Real*8 :: BinSize ! mesh point length: resolution
107 | Real*8 :: BinSize2 ! mesh point length squared
108 | Real*8 :: BinSize3 ! mesh point length cubic = volume of voxel
109 | Real*8 :: Cutoff ! cutoff to remove atoms far from the sphere
110 | Real*8 :: Vburf ! free volume
111 | Real*8 :: Vburo ! overlapped volume
112 | Real*8 :: VburTot ! total volume
113 | Real*8 :: zmin ! bottom z surface
114 | Real*8 :: zmax ! top z surface
115 | Real*8 :: Volume ! volume of the sphere
116 | Real*8 :: space ! grid point size
117 | Real*8 :: notch ! starting point of the plot
118 | Real*8 :: smin ! starting point of one grid
119 | Real*8 :: smax ! ending point of one grid
120 |
121 | Real*8 :: Vbur4f(4) ! project free volume into 4 sphere quadrants
122 | Real*8 :: Vbur8f(8) ! project free volume into 8 sphere octants
123 | Real*8 :: Vbur4o(4) ! project occupied volume into 4 sphere quadrants
124 | Real*8 :: Vbur8o(8) ! project occupied volume into 8 sphere octants
125 |
126 | c=========================================================================
127 | c Input and output files
128 | c=========================================================================
129 | Character*4 :: suffix_inp ! suffix for file : .inp
130 | Character*4 :: suffix_out ! suffix for file : .out
131 | Character*4 :: suffix_dat ! suffix for file : .dat
132 | Character*4 :: suffix_xyz ! suffix for file : .xyz
133 | Character*100 :: BaseName ! the BaseName identifying the job files
134 | Character*100 :: VburINP ! input file for Vbur
135 | Character*100 :: VburOUT ! output file
136 | Character*100 :: VburDAT ! output file for Vbur values
137 | Character*100 :: VburDistOUT ! output file for Vbur_distribution
138 | Character*100 :: RotatedXYZ ! output file for rotated molecule
139 | Character*100 :: Quadrant4 ! project the Vbur into 4 sphere quadrants
140 | Character*100 :: Quadrant4Dist ! distribution within 4 sphere quadrants
141 | Character*100 :: Quadrant8 ! project the Vbur into 8 sphere octants
142 | Character*100 :: Quadrant8Dist ! distribution within 8 sphere octants
143 | Character*100 :: CheckCoord ! check if the coordinates are successfully manipulated
144 | Character*100 :: TopSurface ! top surface
145 | Character*100 :: BotSurface ! bottmon surface
146 |
147 | c=========================================================================
148 | c simple index and temporary variables
149 | c=========================================================================
150 | Integer :: i, j, k, l, m, n
151 |
152 | Real*8 :: x, y, z, dx, dy, dz
153 | Real*8 :: dist, dist2
154 | Real*8 :: temp
155 | Character*4 :: ATOMNAME
156 | Character*128 :: WD
157 |
158 | c=========================================================================
159 | c setting input and output files
160 | c=========================================================================
161 |
162 | call getarg(1, BaseName)
163 |
164 | if(iargc().eq.0) then
165 | write(6,*) " "
166 | write(6,*) "Usage: "
167 | write(6,*) "Assuming a myfile.inp input file "
168 | write(6,*) "./sambvca21.x myfile "
169 | stop
170 | endif
171 |
172 | suffix_inp = ".inp"
173 | suffix_dat = ".dat"
174 | suffix_xyz = ".xyz"
175 | suffix_out = ".out"
176 |
177 | c====================================================================================
178 | c Line below is a hard coded parameter defining directories and output style.
179 | c IfWeb=1 triggers options for the webserver. IfWeb=0 to be used in line mode
180 | c====================================================================================
181 | c
182 | IfWeb=0
183 |
184 | if(IfWeb.eq.0) then
185 | WD=TRIM(BaseName)
186 | else
187 | WD='./temp/'//TRIM(BaseName)//"/"//TRIM(BaseName)
188 | endif
189 |
190 | c====================================================================================
191 | c End defining Web or command line output directory
192 | c====================================================================================
193 |
194 | VburINP = TRIM(WD)//TRIM(suffix_inp)
195 | VburOUT = TRIM(WD)//TRIM(suffix_out)
196 | VburDAT = TRIM(WD)//"-VBURvsFRAME"//TRIM(suffix_dat)
197 | VburDistOUT = TRIM(WD)//"-VBUR-distrib"//TRIM(suffix_dat)
198 | RotatedXYZ = TRIM(WD)//"-rotated"//TRIM(suffix_xyz)
199 | Quadrant4 = TRIM(WD)//"-QUADvsFRAME"//TRIM(suffix_dat)
200 | Quadrant4Dist = TRIM(WD)//"-QUAD-distrib"//TRIM(suffix_dat)
201 | Quadrant8 = TRIM(WD)//"-OCTvsFRAME"//TRIM(suffix_dat)
202 | Quadrant8Dist = TRIM(WD)//"-OCT-distrib"//TRIM(suffix_dat)
203 | TopSurface = TRIM(WD)//"-TopSurface"//TRIM(suffix_dat)
204 | BotSurface = TRIM(WD)//"-BotSurface"//TRIM(suffix_dat)
205 |
206 | open(101, file=VburINP, status='old')
207 | open(200, file=VburOUT, status='unknown')
208 | open(201, file=VburDAT, status='unknown')
209 | open(202, file=VburDistOUT, status='unknown')
210 | open(203, file=Quadrant4, status='unknown')
211 | open(204, file=Quadrant4Dist, status='unknown')
212 | open(205, file=Quadrant8, status='unknown')
213 | open(206, file=Quadrant8Dist, status='unknown')
214 | open(207, file=RotatedXYZ, status='unknown')
215 | open(208, file=TopSurface, action="write", status="unknown")
216 | open(209, file=BotSurface, action="write", status="unknown")
217 |
218 | CALL PrintFlag()
219 | c=========================================================================
220 | c Reading the input file
221 | c=========================================================================
222 |
223 | c atoms to be deleted
224 | read(101,*)nAtomDel
225 | if(nAtomDel.gt.0)then
226 | Allocate (IndDel(nAtomDel))
227 | read(101,*)IndDel(1:nAtomDel)
228 | endif
229 |
230 | c atoms defining the geometry center
231 | read(101,*)nAtomGC
232 | Allocate (IndGC(nAtomGC))
233 | read(101,*)IndGC(1:nAtomGC)
234 |
235 | c atoms defining the z-axis
236 | read(101,*)nAtom_z
237 | Allocate (IndAxis(nAtom_z))
238 | read(101,*)IndAxis(1:nAtom_z)
239 |
240 | c atoms defining the xz plane
241 | read(101,*)nAtom_xz
242 | Allocate (Indxz(nAtom_xz))
243 | read(101,*)Indxz(1:nAtom_xz)
244 |
245 | c radius of the sphere
246 | read(101,*)Radius
247 |
248 | c displacement of geometry center from the center of sphere
249 | read(101,*)Disp
250 |
251 | c mesh point length
252 | read(101,*)BinSize
253 |
254 | c if remove Hydrogen atoms
255 | read(101,*)RemoveH
256 |
257 | c align molecule to +z or -z
258 | read(101,*)AlignZ
259 |
260 | c if make map
261 | read(101,*)DoMap
262 |
263 | c read atom types and radius
264 | read(101,*)nTypes
265 |
266 | Allocate (AtomTypeRadius(nTypes))
267 | Allocate (AtomTypeName(nTypes))
268 |
269 | do i = 1, nTypes
270 | read(101,*)AtomTypeName(i),AtomTypeRadius(i)
271 | enddo
272 |
273 | CALL CheckControlParameters(nAtomDel, IndDel,
274 | x nAtomGC, IndGC, nAtom_z, IndAxis, nAtom_xz, Indxz,
275 | x Radius, Disp, BinSize, RemoveH, DoMap,
276 | x AlignZ, nTypes, AtomTypeName, AtomTypeRadius)
277 |
278 | c==========================================================================
279 | c Start the big loop over the number of coordinate frames in the input file.
280 | c==========================================================================
281 | IndFrame = 0
282 | do while(.true.)
283 |
284 | c read coordinates of atoms composing the molecule to be analyzed
285 | read(101,*,end=901)natoms
286 | IndFrame = IndFrame+1
287 | read(101,*,end=901)Title
288 | Allocate (Coord(natoms*3))
289 | Allocate (AtomNames(natoms))
290 | Allocate (AtomRadius2(natoms))
291 | do i = 1, natoms
292 | j = (i-1)*3
293 | read(101,*,end=901)AtomNames(i),
294 | x Coord(j+1),Coord(j+2),Coord(j+3)
295 | enddo
296 |
297 | c write input coordinates in output:
298 | write(200,*)
299 | write(200,'(a39,16x,i5)')
300 | x "Checking: No. of atoms of input frame :", natoms
301 |
302 | write(200,*)
303 | write(200,'(a38)')"Checking: Coordinates of input frame :"
304 | do i = 1, natoms
305 | j = (i-1)*3
306 | write(200,'(a4,1x,3f10.5)')AtomNames(i),
307 | x Coord(j+1),Coord(j+2),Coord(j+3)
308 | enddo
309 |
310 | c=========================================================================
311 | c defining the cartesian axis
312 | c=========================================================================
313 | Call ConstructXYZ(GC, natoms, Coord, IndGC, nAtomGC, z_GC,
314 | x nAtom_z, IndAxis, nAtom_xz, Indxz,
315 | x x_axis, y_axis, z_axis, x_temp_GC)
316 |
317 | c align z_axis to 0 0 1
318 | bench_vec(1) = 0.d0
319 | bench_vec(2) = 0.d0
320 | if(AlignZ.eq.0)bench_vec(3) = -1.d0
321 | if(AlignZ.eq.1)bench_vec(3) = +1.d0
322 | initial(1) = z_axis(1)
323 | initial(2) = z_axis(2)
324 | initial(3) = z_axis(3)
325 |
326 | call ali(Coord, bench_vec, initial,
327 | x x_axis, y_axis, z_axis, GC, natoms)
328 |
329 | c renew your axis
330 | Call ConstructXYZ(GC, natoms, Coord, IndGC, nAtomGC, z_GC,
331 | x nAtom_z, IndAxis, nAtom_xz, Indxz,
332 | x x_axis, y_axis, z_axis, x_temp_GC)
333 |
334 | c align x_axis to 1 0 0 with fixed z
335 | bench_vec(1) = 1.d0
336 | bench_vec(2) = 0.d0
337 | bench_vec(3) = 0.d0
338 | initial(1) = x_axis(1)
339 | initial(2) = x_axis(2)
340 | initial(3) = 0.d0
341 | call ali(Coord, bench_vec, initial,
342 | x x_axis, y_axis, z_axis, GC, natoms)
343 |
344 | c renew your GC
345 | Call ConstructXYZ(GC, natoms, Coord, IndGC, nAtomGC, z_GC,
346 | x nAtom_z, IndAxis, nAtom_xz, Indxz,
347 | x x_axis, y_axis, z_axis, x_temp_GC)
348 |
349 | Call MoveParticle(Coord, GC, natoms)
350 |
351 | c move the particle if Disp > 0.0
352 | if(Disp.ne.0.0)then
353 | GC(1) = 0.d0
354 | GC(2) = 0.d0
355 | if(AlignZ.eq.0)GC(3) = +Disp
356 | if(AlignZ.eq.1)GC(3) = -Disp
357 | Call MoveParticle(Coord, GC, natoms)
358 | endif
359 |
360 | c write XYZ rotated frame in output and -rotated.xyz file
361 | write(200,*)
362 | write(200,'(a39)')"Results: Coordinates of rotated frame :"
363 | call WriteXYZ(200,natoms,Title,Atomnames,Coord)
364 | call WriteXYZ(207,natoms,Title,Atomnames,Coord)
365 |
366 | c remove deleted atoms "AS" and H atoms, if requested, by working array
367 | if (nAtomDel.gt.0)then
368 | do i = 1, nAtomDel
369 | j = IndDel(i)
370 | AtomNames(j) = "AS"
371 | enddo
372 | endif
373 |
374 | Allocate(Coord_new(natoms*3))
375 | Allocate(AtomNames_new(natoms))
376 |
377 | ATOMNAME="AS"
378 | call RemoveAtom(natoms,natoms_new,AtomNames,AtomNames_new,
379 | x Coord,Coord_new,ATOMNAME)
380 |
381 | if(RemoveH.eq.1)then
382 | ATOMNAME="H"
383 | call RemoveAtom(natoms,natoms_new,AtomNames,AtomNames_new,
384 | x Coord,Coord_new,ATOMNAME)
385 | endif
386 |
387 | Deallocate(Coord_new)
388 | Deallocate(AtomNames_new)
389 |
390 | c assign atom radius from the data base
391 | do i = 1, natoms
392 | k = 0
393 | call ToUpperCase(AtomNames(i),ATOMNAME)
394 | do j = 1, nTypes
395 | if (ATOMNAME .eq. AtomTypeName(j))then
396 | k = 1
397 | AtomRadius2(i) = AtomTypeRadius(j) * AtomTypeRadius(j)
398 | goto 800
399 | endif
400 | enddo
401 | 800 continue
402 |
403 | if (k.eq.0)then
404 | write(200,*)"Can't find atom type for atom ", AtomNames(i)
405 | stop
406 | endif
407 | enddo
408 |
409 | c remove atoms far away from the sphere
410 | Cutoff = AtomTypeRadius(1)
411 | do i = 2, nTypes
412 | if(Cutoff.lt.AtomTypeRadius(i))Cutoff=AtomTypeRadius(i)
413 | enddo
414 | Cutoff = (Cutoff + Radius + 1.0)**2
415 |
416 | Allocate (Coord_new(natoms*3))
417 | Allocate (AtomRadius2_new(natoms))
418 | Allocate (AtomNames_new(natoms))
419 | natoms_new = 0
420 | do i = 1, natoms
421 | j = (i-1)*3
422 | dist = Coord(j+1)**2 + Coord(j+2)**2 + Coord(j+3)**2
423 | if(dist.le.Cutoff)then
424 | natoms_new = natoms_new + 1
425 | k = (natoms_new - 1)*3
426 | Coord_new(k+1) = Coord(j+1)
427 | Coord_new(k+2) = Coord(j+2)
428 | Coord_new(k+3) = Coord(j+3)
429 | AtomNames_new(natoms_new) = AtomNames(i)
430 | AtomRadius2_new(natoms_new) = AtomRadius2(i)
431 | endif
432 | enddo
433 | natoms = natoms_new
434 | do i = 1, natoms
435 | j = (i-1)*3
436 | Coord(j+1) = Coord_new(j+1)
437 | Coord(j+2) = Coord_new(j+2)
438 | Coord(j+3) = Coord_new(j+3)
439 | AtomNames(i) = AtomNames_new(i)
440 | AtomRadius2(i) = AtomRadius2_new(i)
441 | enddo
442 |
443 | Deallocate(Coord_new)
444 | Deallocate(AtomNames_new)
445 | Deallocate(AtomRadius2_new)
446 |
447 | c initialize some parameters for the Vbur calculation
448 | c increase marginally square radius of sphere (Radius2) to capture
449 | c grid points that are on the sphere surface.
450 | c
451 | Radius2 = Radius * Radius +0.0001*BinSize*BinSize
452 | BinSize2 = BinSize * BinSize
453 | BinSize3 = BinSize2 * BinSize
454 | NumInside = 0
455 | Vburf = 0.0
456 | Vburo = 0.0
457 | VburTot = 0.0
458 |
459 | c space is the stepsize for Vbur distribuition over different frames
460 | c for future implementation, currently hardcoded to 1.0
461 | space = 1.d0
462 | npoints = int(100.d0/space)
463 | notch = space / 2.d0
464 |
465 | do i = 1, npoints
466 | VburDist(i) = 0
467 | enddo
468 |
469 | do i = 1, 4
470 | Vbur4f(i) = 0.0
471 | Vbur4o(i) = 0.0
472 | do j = 1, npoints
473 | Vbur4Dist(i,j) = 0
474 | enddo
475 | enddo
476 |
477 | do i = 1, 8
478 | Vbur8f(i) = 0.0
479 | Vbur8o(i) = 0.0
480 | do j = 1, npoints
481 | Vbur8Dist(i,j) = 0
482 | enddo
483 | enddo
484 |
485 | c=========================================================
486 | c Buried volume calculation starts below by setting number
487 | c of grid points to be sampled on each axis
488 | c check if grid point is inside the sampled sphere
489 | c if grid point is on the surface set volume weight = 0.5
490 | c=========================================================
491 | NumPoints = int(2.0 * Radius / BinSize + 1.0)
492 |
493 | do i= 1, NumPoints
494 | x = -Radius + Real(i-1)*BinSize
495 | do j = 1, NumPoints
496 | y = -Radius + Real(j-1)*BinSize
497 | zmin = Radius * 2.0
498 | zmax =-Radius * 2.0
499 | do k = 1, NumPoints
500 | z = -Radius + Real(k-1)*BinSize
501 | IfOverlap = 0
502 | dist2 = x*x + y*y + z*z
503 | if(dist2.le.Radius2)then
504 | NumInside = NumInside + 1
505 | Wgth = 1.0
506 | if (abs(dist2-Radius2).lt.0.01*BinSize) Wgth = 0.5
507 | do l = 1, natoms
508 | m = (l-1)*3
509 | dx = x - Coord(m+1)
510 | dy = y - Coord(m+2)
511 | dz = z - Coord(m+3)
512 | dist2 = dx*dx + dy*dy + dz*dz
513 | if(dist2.lt.AtomRadius2(l)) IfOverlap = 1
514 | enddo
515 | c
516 | c no overlap of grid point with any atom
517 | if(IfOverlap.eq.0)then
518 | Vburf = Vburf + BinSize3*Wgth
519 | Call Proj4(x, y, z, Vbur4f, BinSize, BinSize3, Wgth)
520 | Call Proj8(x, y, z, Vbur8f, BinSize, BinSize3, Wgth)
521 | endif
522 | c
523 | c grid point overlapping with at least one atom
524 | if(IfOverlap.eq.1)then
525 | Vburo = Vburo + BinSize3*Wgth
526 | Call Proj4(x, y, z, Vbur4o, BinSize, BinSize3, Wgth)
527 | Call Proj8(x, y, z, Vbur8o, BinSize, BinSize3, Wgth)
528 | endif
529 |
530 | VburTot = VburTot + BinSize3*Wgth
531 | endif
532 |
533 | c tune bottom and top surface
534 | if (IfOverlap.eq.1)then
535 | if(z.gt.zmax) zmax = z
536 | if(z.lt.zmin) zmin = z
537 | endif
538 | enddo
539 | c make map
540 | if(DoMap.eq.1)then
541 | write(208, '(3f8.2)')x,y,zmax
542 | write(209, '(3f8.2)')x,y,zmin
543 | endif
544 |
545 | enddo
546 | enddo
547 |
548 | Volume = Radius * Radius2 * pi * 4.0 / 3.0
549 |
550 | temp = Vburo / VburTot * 100.0
551 | do j = 1, npoints
552 | smin = Real(j - 1) * space
553 | smax = Real(j) * space
554 | if((temp.gt.smin).and.(temp.lt.smax))
555 | x VburDist(j) = VburDist(j) + 1
556 | enddo
557 |
558 | Call OutputVbur(Volume, NumInside, BinSize3, Vburf,
559 | x Vburo, VburTot, IndFrame, VburDist, npoints,
560 | x space, notch, IfWeb)
561 |
562 | write(204,*) '# notch -- -+ ++ +- '
563 | Call OutputQua4(Vbur4f, Vbur4o, IndFrame, IfWeb)
564 | do j = 1, npoints
565 | smin = REAL(j - 1) * space
566 | smax = REAL(j) * space
567 | do k = 1, 4
568 | temp = 100.0 * Vbur4o(k) / (Vbur4f(k) + Vbur4o(k))
569 | if((temp.gt.smin).and.(temp.lt.smax))
570 | x Vbur4Dist(k, j) = Vbur4Dist(k, j) + 1.0
571 | enddo
572 | write(204,'(f5.1,4f10.1)') smin + notch,
573 | x (REAL(Vbur4Dist(k,j))/REAL(IndFrame), k = 1, 4)
574 | enddo
575 |
576 | write(206, *)'# notch --- -+- ++- +--
577 | x -++ +++ +-+'
578 | Call OutputQua8(Vbur8f, Vbur8o, IndFrame, IfWeb)
579 | do j = 1, npoints
580 | smin = REAL(j - 1) * space
581 | smax = REAL(j) * space
582 | do k = 1, 4
583 | temp = 100.0 * Vbur8o(k) / (Vbur8f(k) + Vbur8o(k))
584 | if((temp.gt.smin).and.(temp.lt.smax))
585 | x Vbur8Dist(k, j) = Vbur8Dist(k, j) + 1
586 | enddo
587 | write(206, '(f5.1,8f10.5)') smin + notch,
588 | x (REAL(Vbur8Dist(k,j))/REAL(IndFrame), k = 1, 8)
589 | enddo
590 |
591 | Deallocate (Coord)
592 | Deallocate (AtomNames)
593 | Deallocate (AtomRadius2)
594 |
595 | c Below, end loop over frames
596 | enddo
597 | 901 continue
598 |
599 | write(200,*)
600 | write(200,*)' No. of frames analyzed : ',IndFrame
601 | write(200,*)
602 | write(200,*)'Normal termination'
603 |
604 | if(nAtomDel.gt.0) Deallocate (IndDel)
605 | Deallocate (IndGC)
606 | Deallocate (IndAxis)
607 | Deallocate (Indxz)
608 | Deallocate (AtomTypeRadius)
609 | Deallocate (AtomTypeName)
610 |
611 | close(101)
612 | close(201)
613 | close(203)
614 | close(205)
615 | close(207)
616 |
617 | if(IndFrame.eq.1) then
618 | close(202,status="delete")
619 | close(204,status="delete")
620 | close(206,status="delete")
621 | else
622 | close(202)
623 | close(204)
624 | close(206)
625 | endif
626 |
627 | if(DoMap.eq.0) then
628 | close(208,status="delete")
629 | close(209,status="delete")
630 | else
631 | close(208)
632 | close(209)
633 | endif
634 |
635 | end
636 |
637 | c=======================================================================
638 | c This subroutine is to output a flag for the program c
639 | c=======================================================================
640 | subroutine PrintFlag()
641 | Implicit None
642 |
643 | write(200,*)
644 | write(200,*)'---------------------------------------------------'
645 | write(200,*)'| |'
646 | write(200,*)'| S A M B V C A 2.1 |'
647 | write(200,*)'| |'
648 | write(200,*)'| Release 29-06-2019 |'
649 | write(200,*)'| |'
650 | write(200,*)'| Molecular Buried Volume Calculator |'
651 | write(200,*)'| |'
652 | write(200,*)'| http://www.molnac.unisa.it/OM-tools/SambVca |'
653 | write(200,*)'| |'
654 | write(200,*)'| L. Cavallo & Z. Cao Email: lcavallo@unisa.it |'
655 | write(200,*)'| |'
656 | write(200,*)'---------------------------------------------------'
657 | write(200,*)
658 |
659 | end subroutine PrintFlag
660 |
661 | c=======================================================================
662 | c This subroutine is to check if the input file is correctly read c
663 | c=======================================================================
664 | subroutine CheckControlParameters(nAtomDel, IndDel,
665 | x nAtomGC, IndGC, nAtom_z, IndAxis, nAtom_xz, Indxz, Radius,
666 | x Disp, BinSize, RemoveH, DoMap,
667 | x AlignZ, nTypes, AtomTypeName, AtomTypeRadius)
668 |
669 | Implicit None
670 |
671 | Integer :: i
672 |
673 | Integer :: IfDel ! parameter to determine if there is atom to be deleted: > 0, yes
674 | Integer :: nAtomDel ! # of atoms to be deleted
675 | Integer :: RemoveH ! parameter to determine if remove H: =1, remove; =2, not
676 | Integer :: DoMap ! if make map: =1, make; =2, not
677 | Integer :: AlignZ ! =1, align the molecule along z; =2 align the molecule along -z
678 |
679 | Integer :: IndDel(nAtomDel) ! index of the atoms to be deleted
680 |
681 | Integer :: nAtomGC ! # of atoms to define a geometry center
682 | Integer :: IndGC(nAtomGC) ! index of the atoms to define a geometry center
683 |
684 | Integer :: nAtom_z ! # of atoms to define x axis
685 | Integer :: IndAxis(nAtom_z) ! index of the atoms to define an axis
686 | Integer :: nAtom_xz ! # of atoms to define a second vector
687 | Integer :: Indxz(nAtom_xz) ! index of these atoms
688 |
689 | Real*8 :: Radius ! radius of the sphere
690 | Real*8 :: Disp ! displacement from the sphere center
691 | Real*8 :: BinSize ! mesh point length: resolution
692 | Real*8 :: AtomTypeRadius(nTypes) ! Radius of atoms in database
693 |
694 | Integer :: nTypes ! # of atom types in the database
695 |
696 | Character*4 :: AtomTypeName(nTypes) ! Radius of atoms in database
697 |
698 | c End variables definition
699 |
700 | write(200,'(a39,16x,i5)')
701 | x"Checking: No. of atoms to be removed : ", nAtomDel
702 | if (nAtomDel.gt.0) then
703 | write(200,'(a32)')
704 | x "Checking: Atoms to be removed : "
705 | write(200,'(10i5)')IndDel(1:nAtomDel)
706 | endif
707 |
708 | write(200,'(a51,4x,i5)')
709 | x"Checking: No. of atoms defining the sphere center :", nAtomGC
710 | write(200,'(a44,11x,i5)')
711 | x"Checking: Atoms defining the sphere center :"
712 | write(200,'(10i5)')IndGC(1:nAtomGC)
713 |
714 | write(200,'(a45,10x,i5)')
715 | x"Checking : No. of atoms defining the Z-axis :", nAtom_z
716 | write(200,'(10i5)')IndAxis(1:nAtom_z)
717 |
718 | write(200,'(a47,8x,i5)')
719 | x"Checking : No. of atoms defining the XZ-plane :", nAtom_xz
720 | write(200,'(10i5)')Indxz(1:nAtom_xz)
721 |
722 | if (AlignZ.eq.1) write(200,*)
723 | x "Checking: Molecule will be aligned along the Z+ direction"
724 | if (AlignZ.eq.2) write(200,*)
725 | x "Checking: Molecule will be aligned along the Z- direction"
726 |
727 | write(200,'(a32,8x,f8.3)')
728 | x"Checking: Vbur sphere radius : ", Radius
729 |
730 | write(200,'(a38,2x,f8.3)')
731 | x"Checking: Displacement from origin : " , Disp
732 |
733 | write(200,'(a23,17x,f8.3)')
734 | x"Checking: Bin width : ", BinSize
735 |
736 | if (RemoveH.eq.1) write(200,*)
737 | x "Checking: Removing H atoms"
738 | if (RemoveH.ne.1) write(200,*)
739 | x "Checking: Keeping H atoms"
740 |
741 | if (DoMap.eq.1) write(200,*)
742 | x "Checking: Outputting steric map"
743 | if (DoMap.ne.1) write(200,*)
744 | x "Checking: No steric map in the output"
745 |
746 | write(200,*)
747 | x"Checking: No. of atom types in your database :", nTypes
748 | write(200,*)
749 | x"Checking: Atom labels and radius in the database :"
750 |
751 | do i = 1,nTypes
752 | write(200,'(a4,f6.3)')AtomTypeName(i), AtomTypeRadius(i)
753 | enddo
754 |
755 | end subroutine CheckControlParameters
756 |
757 | c=======================================================================
758 | c This subroutine is to construct the original point and the
759 | c orthogonol axis based on the input information
760 | c=======================================================================
761 | subroutine ConstructXYZ(GC, natoms, Coord, IndGC, nAtomGC, z_GC,
762 | x nAtom_z, IndAxis, nAtom_xz, Indxz,
763 | x x_axis, y_axis, z_axis, x_temp_GC)
764 | Implicit None
765 |
766 | Integer :: i, j, k
767 | Integer :: natoms, nAtomGC, nAtom_z, nAtom_xz
768 | Integer :: IndGC(nAtomGC)
769 | Integer :: IndAxis(nAtom_z)
770 | Integer :: Indxz(nAtom_xz)
771 |
772 | Real*8 :: dist
773 | Real*8 :: Coord(natoms*3)
774 | Real*8 :: x_axis(3), y_axis(3), z_axis(3)
775 | Real*8 :: x_temp_GC(3)
776 | Real*8 :: GC(3)
777 | Real*8 :: z_GC(3)
778 |
779 | c Make geometry center
780 | do k = 1, 3
781 | GC(k) = 0.0
782 | enddo
783 |
784 | do i = 1, nAtomGC
785 | j = IndGC(i)
786 | if ((j.le.0).or.(j.gt.natoms))then
787 | write(200,*)"wrong atoms to define geometry center"
788 | stop
789 | endif
790 |
791 | if ((j.gt.0).and.(j.le.natoms))then
792 | j = (j - 1) * 3
793 | do k = 1, 3
794 | GC(k) = GC(k) + Coord(j+k)
795 | enddo
796 | endif
797 | enddo
798 |
799 | do k = 1, 3
800 | GC(k) = GC(k) / REAL(nAtomGC)
801 | enddo
802 |
803 | c make z-axis
804 | do k = 1, 3
805 | z_GC(k) = 0.0
806 | enddo
807 |
808 | do i = 1, nAtom_z
809 | j = IndAxis(i)
810 | if ((j.le.0).or.(j.gt.natoms))then
811 | write(200,*)"wrong atoms to define z_axis"
812 | stop
813 | endif
814 | if ((j.gt.0).and.(j.le.natoms))then
815 | j = (j - 1) * 3
816 | do k = 1, 3
817 | z_GC(k) = z_GC(k) + Coord(j+k)
818 | enddo
819 | endif
820 | enddo
821 |
822 | do k = 1, 3
823 | z_GC(k) = z_GC(k) / REAL(nAtom_z)
824 | enddo
825 | call UnitVector(z_GC, GC, z_axis, dist)
826 |
827 | c second vector in x-z plane
828 | do k = 1, 3
829 | x_temp_GC(k) = 0.0
830 | enddo
831 |
832 | do i = 1, nAtom_xz
833 | j = Indxz(i)
834 | if ((j.le.0).or.(j.gt.natoms))then
835 | write(200,*)"wrong atoms to define x-z plane"
836 | stop
837 | endif
838 |
839 | if ((j.gt.0).and.(j.le.natoms))then
840 | j = (j - 1) * 3
841 | do k = 1, 3
842 | x_temp_GC(k) = x_temp_GC(k) + Coord(j+k)
843 | enddo
844 | endif
845 | enddo
846 |
847 | do k = 1, 3
848 | x_temp_GC(k) = x_temp_GC(k) / REAL(nAtom_xz)
849 | enddo
850 |
851 | Call UnitVector(x_temp_GC, GC, x_axis, dist)
852 |
853 | c make y axis: cross product of z and x'
854 | Call CrossPro(z_axis, x_axis, y_axis)
855 |
856 | c make x axis: cross product of y and z
857 | Call CrossPro(y_axis, z_axis, x_axis)
858 |
859 | end subroutine ConstructXYZ
860 |
861 |
862 | c=======================================================================
863 | c This subroutine is to get a vector from coordinates of two atoms
864 | c=======================================================================
865 | subroutine UnitVector(coord1,coord2,vec, dist)
866 | Implicit None
867 |
868 | Integer :: k
869 | Real*8 :: dist
870 | Real*8 :: coord1(3), Coord2(3), vec(3)
871 |
872 | do k = 1, 3
873 | vec(k) = Coord1(k) - Coord2(k)
874 | enddo
875 |
876 | dist = sqrt(vec(1)**2+vec(2)**2+vec(3)**2)
877 |
878 | do k = 1, 3
879 | vec(k) = vec(k) / dist
880 | enddo
881 |
882 | end subroutine UnitVector
883 |
884 | c=======================================================================
885 | c This subroutine is to do the cross product c
886 | c=======================================================================
887 | subroutine CrossPro(vec1, vec2, vec3)
888 | Implicit None
889 |
890 | Integer :: k
891 | Real*8 :: dist
892 | Real*8 :: vec1(3), vec2(3), vec3(3)
893 |
894 | vec3(1) = vec1(2) * vec2(3) - vec1(3) * vec2(2)
895 | vec3(2) = vec1(3) * vec2(1) - vec1(1) * vec2(3)
896 | vec3(3) = vec1(1) * vec2(2) - vec1(2) * vec2(1)
897 |
898 | dist = sqrt (vec3(1)**2+vec3(2)**2+vec3(3)**2)
899 |
900 | do k = 1, 3
901 | vec3(k) = vec3(k) / dist
902 | enddo
903 |
904 | end subroutine CrossPro
905 |
906 | c=======================================================================
907 | c This subroutine is to align molecule along certain direction c
908 | c=======================================================================
909 | subroutine ali(Coord, bench_vec, initial, x_axis, y_axis, z_axis,
910 | x GC, natoms)
911 | Implicit None
912 |
913 | Integer :: i, j, k
914 | Integer :: natoms
915 |
916 | Real*8 :: Coord(natoms*3)
917 | Real*8 :: initial(3)
918 | Real*8 :: bench_vec(3)
919 | Real*8 :: x_axis(3)
920 | Real*8 :: y_axis(3)
921 | Real*8 :: z_axis(3)
922 | Real*8 :: GC(3)
923 | Real*8 :: GC_temp(3)
924 |
925 | Real*8 :: vec1(3), vec2(3), vec3(3)
926 | Real*8 :: rot(3)
927 | Real*8 :: Rodrigue(3,3)
928 |
929 | Real*8 :: costheta, sintheta, dl, dist, dist1
930 |
931 | c check if the bench_vec and the initial vector are the same, if so, no need to do reorientation
932 | if((initial(1).eq.bench_vec(1)).and.(initial(2).eq.bench_vec(2))
933 | x .and.(initial(3).eq.bench_vec(3)))goto 1001
934 |
935 | c get rotation axis
936 | call CrossPro(initial, bench_vec, rot)
937 |
938 | c build Rodrigue's matrix
939 | costheta = initial(1) * bench_vec(1) + initial(2) * bench_vec(2)
940 | x + initial(3) * bench_vec(3)
941 | dl = 1.0 - costheta
942 | sintheta = sqrt(1.0 - costheta * costheta)
943 |
944 | Rodrigue(1,1) = costheta + rot(1) * rot(1) * dl
945 | Rodrigue(1,2) = rot(1) * rot(2) * dl - rot(3) * sintheta
946 | Rodrigue(1,3) = rot(2) * sintheta + rot(1) * rot(3) * dl
947 |
948 | Rodrigue(2,1) = rot(3) * sintheta + rot(1) * rot(2) * dl
949 | Rodrigue(2,2) = costheta + rot(2) * rot(2) * dl
950 | Rodrigue(2,3) =-rot(1) * sintheta + rot(2) * rot(3) * dl
951 |
952 | Rodrigue(3,1) =-rot(2) * sintheta + rot(1) * rot(3) * dl
953 | Rodrigue(3,2) = rot(1) * sintheta + rot(2) * rot(3) * dl
954 | Rodrigue(3,3) = costheta + rot(3) * rot(3) * dl
955 |
956 | c set the geometry center to rotate the molecule around
957 | do k = 1, 3
958 | GC_temp(k) = Coord(k)
959 | enddo
960 | do i = 2, natoms
961 | j = (i-1) * 3
962 | GC_temp(1) = Coord(j+1)
963 | GC_temp(2) = Coord(j+2)
964 | GC_temp(3) = Coord(j+3)
965 | enddo
966 | do k = 1, 3
967 | GC_temp(k) = GC_temp(k) / REAL(natoms)
968 | enddo
969 |
970 | c rotate the particle
971 | do i = 1, natoms
972 | j = (i-1) * 3
973 | do k = 1, 3
974 | vec1(k) = Coord(j+k) - GC_temp(k)
975 | enddo
976 | dist = sqrt(vec1(1)**2 + vec1(2)**2 + vec1(3)**2)
977 | do k = 1, 3
978 | vec1(k) = vec1(k) / dist
979 | enddo
980 |
981 | call matrixpro(Rodrigue, vec1, vec2, dist1)
982 |
983 | Coord(j+1) = GC_temp(1) + vec2(1) * dist
984 | Coord(j+2) = GC_temp(2) + vec2(2) * dist
985 | Coord(j+3) = GC_temp(3) + vec2(3) * dist
986 | enddo
987 |
988 | 1001 continue
989 | end subroutine ali
990 |
991 | c=======================================================================
992 | c This subroutine is to do matrix operation c
993 | c=======================================================================
994 | subroutine matrixPro(m, n, l, dist)
995 | Implicit None
996 |
997 | Real*8 :: m(3,3)
998 | Real*8 :: n(3), l(3)
999 |
1000 | Real*8 :: dist
1001 |
1002 | l(1) = m(1,1) * n(1) + m(1,2) * n(2) + m(1,3) * n(3)
1003 | l(2) = m(2,1) * n(1) + m(2,2) * n(2) + m(2,3) * n(3)
1004 | l(3) = m(3,1) * n(1) + m(3,2) * n(2) + m(3,3) * n(3)
1005 |
1006 | dist = sqrt (l(1)**2 + l(2)**2 + l(3)**2)
1007 |
1008 | l(1) = l(1) / dist
1009 | l(2) = l(2) / dist
1010 | l(3) = l(3) / dist
1011 |
1012 | end subroutine matrixPro
1013 |
1014 | c=======================================================================
1015 | c This subroutine is to move the geometry center to the origin c
1016 | c=======================================================================
1017 | subroutine MoveParticle(Coord,GC,natoms)
1018 | Implicit None
1019 |
1020 | Real*8 :: Coord(natoms*3)
1021 | Real*8 :: GC(3)
1022 |
1023 | Integer :: natoms
1024 | Integer :: i, j
1025 |
1026 | do i = 1, natoms
1027 | j = (i-1) * 3
1028 | Coord(j+1) = Coord(j+1) - GC(1)
1029 | Coord(j+2) = Coord(j+2) - GC(2)
1030 | Coord(j+3) = Coord(j+3) - GC(3)
1031 | enddo
1032 |
1033 | GC(1) = 0.0
1034 | GC(2) = 0.0
1035 | GC(3) = 0.0
1036 |
1037 | end subroutine MoveParticle
1038 |
1039 | c=======================================================================
1040 | c This subroutine is to project Vbur into 4 quadrants
1041 | c=======================================================================
1042 | subroutine Proj4(x,y,z,Vb4,BS,BS3,W)
1043 | Implicit None
1044 | Real*8 :: x, y, z, BS, BS3, Cut, W
1045 | Real*8 :: Vb4(4)
1046 |
1047 | Cut = 0.25*BS
1048 |
1049 | if(abs(x).lt.Cut .and. abs(y).lt.Cut) then
1050 | Vb4(1) = Vb4(1) + W*BS3/4.0
1051 | Vb4(2) = Vb4(2) + W*BS3/4.0
1052 | Vb4(3) = Vb4(3) + W*BS3/4.0
1053 | Vb4(4) = Vb4(4) + W*BS3/4.0
1054 | return
1055 | endif
1056 |
1057 | if(abs(x).lt.Cut .and. y.lt.0) then
1058 | Vb4(1) = Vb4(1) + W*BS3/2.0
1059 | Vb4(4) = Vb4(4) + W*BS3/2.0
1060 | return
1061 | endif
1062 |
1063 | if(abs(x).lt.Cut .and. y.gt.0) then
1064 | Vb4(2) = Vb4(2) + W*BS3/2.0
1065 | Vb4(3) = Vb4(3) + W*BS3/2.0
1066 | return
1067 | endif
1068 |
1069 | if(x.lt.0 .and. abs(y).lt.Cut) then
1070 | Vb4(1) = Vb4(1) + W*BS3/2.0
1071 | Vb4(2) = Vb4(2) + W*BS3/2.0
1072 | return
1073 | endif
1074 |
1075 | if(x.gt.0 .and. abs(y).lt.Cut) then
1076 | Vb4(3) = Vb4(3) + W*BS3/2.0
1077 | Vb4(4) = Vb4(4) + W*BS3/2.0
1078 | return
1079 | endif
1080 |
1081 | if((x.lt.0.0).and.(y.lt.0.0))Vb4(1) = Vb4(1) +W*BS3
1082 | if((x.lt.0.0).and.(y.gt.0.0))Vb4(2) = Vb4(2) +W*BS3
1083 | if((x.gt.0.0).and.(y.gt.0.0))Vb4(3) = Vb4(3) +W*BS3
1084 | if((x.gt.0.0).and.(y.lt.0.0))Vb4(4) = Vb4(4) +W*BS3
1085 |
1086 | end subroutine Proj4
1087 |
1088 | c=======================================================================
1089 | c This subroutine is to project Vbur into 8 sphere octants
1090 | c=======================================================================
1091 | subroutine Proj8(x,y,z,Vb8,BS,BS3,W)
1092 | Implicit None
1093 | Real*8 :: x, y, z, BS, BS3, Cut, W
1094 | Real*8 :: Vb8(8)
1095 |
1096 | Cut = 0.25*BS
1097 |
1098 | c check if point is at the origin
1099 | if((abs(x).lt.Cut).and.(abs(y).lt.Cut).and.(abs(z).lt.Cut)) then
1100 | Vb8(1) = Vb8(1) + W*BS3/8.0
1101 | Vb8(2) = Vb8(2) + W*BS3/8.0
1102 | Vb8(3) = Vb8(3) + W*BS3/8.0
1103 | Vb8(4) = Vb8(4) + W*BS3/8.0
1104 | Vb8(5) = Vb8(5) + W*BS3/8.0
1105 | Vb8(6) = Vb8(6) + W*BS3/8.0
1106 | Vb8(7) = Vb8(7) + W*BS3/8.0
1107 | Vb8(8) = Vb8(8) + W*BS3/8.0
1108 | return
1109 | endif
1110 |
1111 | c check if point is on -x axis
1112 | if((x.lt.-Cut).and.(abs(y).lt.Cut).and.(abs(z).lt.Cut)) then ! -x axis
1113 | Vb8(1) = Vb8(1) + W*BS3/4.0
1114 | Vb8(2) = Vb8(2) + W*BS3/4.0
1115 | Vb8(5) = Vb8(5) + W*BS3/4.0
1116 | Vb8(6) = Vb8(6) + W*BS3/4.0
1117 | return
1118 | endif
1119 |
1120 | c check if point is on +x axis
1121 | if((x.gt.+Cut).and.(abs(y).lt.Cut).and.(abs(z).lt.Cut)) then ! +x axis
1122 | Vb8(3) = Vb8(3) + W*BS3/4.0
1123 | Vb8(4) = Vb8(4) + W*BS3/4.0
1124 | Vb8(7) = Vb8(7) + W*BS3/4.0
1125 | Vb8(8) = Vb8(8) + W*BS3/4.0
1126 | return
1127 | endif
1128 |
1129 | c check if point is on -y axis
1130 | if((abs(x).lt.Cut).and.(y.lt.-Cut).and.(abs(z).lt.Cut)) then ! -y axis
1131 | Vb8(1) = Vb8(1) + W*BS3/4.0
1132 | Vb8(4) = Vb8(4) + W*BS3/4.0
1133 | Vb8(5) = Vb8(5) + W*BS3/4.0
1134 | Vb8(8) = Vb8(8) + W*BS3/4.0
1135 | return
1136 | endif
1137 |
1138 | c check if point is on +y axis
1139 | if((abs(x).lt.Cut).and.(y.gt.+Cut).and.(abs(z).lt.Cut)) then ! +y axis
1140 | Vb8(2) = Vb8(2) + W*BS3/4.0
1141 | Vb8(3) = Vb8(3) + W*BS3/4.0
1142 | Vb8(6) = Vb8(6) + W*BS3/4.0
1143 | Vb8(7) = Vb8(7) + W*BS3/4.0
1144 | return
1145 | endif
1146 |
1147 | c check if point is on -z axis
1148 | if((abs(x).lt.Cut).and.(abs(y).lt.Cut).and.(z.lt.-Cut)) then ! -z axis
1149 | Vb8(1) = Vb8(1) + W*BS3/4.0
1150 | Vb8(2) = Vb8(2) + W*BS3/4.0
1151 | Vb8(3) = Vb8(3) + W*BS3/4.0
1152 | Vb8(4) = Vb8(4) + W*BS3/4.0
1153 | return
1154 | endif
1155 |
1156 | c check if point is on +z axis
1157 | if((abs(x).lt.Cut).and.(abs(y).lt.Cut).and.(z.gt.+Cut)) then ! +z axis
1158 | Vb8(5) = Vb8(5) + W*BS3/4.0
1159 | Vb8(6) = Vb8(6) + W*BS3/4.0
1160 | Vb8(7) = Vb8(7) + W*BS3/4.0
1161 | Vb8(8) = Vb8(8) + W*BS3/4.0
1162 | return
1163 | endif
1164 |
1165 | c check if point is on -x-y plane
1166 | if((x.lt.-Cut).and.(y.lt.-Cut).and.(abs(z).lt.Cut)) then ! -x-y plane
1167 | Vb8(1) = Vb8(1) + W*BS3/2.0
1168 | Vb8(5) = Vb8(5) + W*BS3/2.0
1169 | return
1170 | endif
1171 |
1172 | c check if point is on -x+y plane
1173 | if((x.lt.-Cut).and.(y.gt.+Cut).and.(abs(z).lt.Cut)) then ! -x+y plane
1174 | Vb8(2) = Vb8(2) + W*BS3/2.0
1175 | Vb8(6) = Vb8(6) + W*BS3/2.0
1176 | return
1177 | endif
1178 |
1179 | c check if point is on +x-y plane
1180 | if((x.gt.+Cut).and.(y.lt.-Cut).and.(abs(z).lt.Cut)) then ! +x-y plane
1181 | Vb8(4) = Vb8(4) + W*BS3/2.0
1182 | Vb8(8) = Vb8(8) + W*BS3/2.0
1183 | return
1184 | endif
1185 |
1186 | c check if point is on +x+y plane
1187 | if((x.gt.+Cut).and.(y.gt.+Cut).and.(abs(z).lt.Cut)) then ! +x+y plane
1188 | Vb8(3) = Vb8(3) + W*BS3/2.0
1189 | Vb8(7) = Vb8(7) + W*BS3/2.0
1190 | return
1191 | endif
1192 |
1193 | c check if point is on -x-z plane
1194 | if((x.lt.-Cut).and.(abs(y).lt.Cut).and.(z.lt.-Cut)) then ! -x-z plane
1195 | Vb8(1) = Vb8(1) + W*BS3/2.0
1196 | Vb8(2) = Vb8(2) + W*BS3/2.0
1197 | return
1198 | endif
1199 |
1200 | c check if point is on -x+z plane
1201 | if((x.lt.-Cut).and.(abs(y).lt.Cut).and.(z.gt.+Cut)) then ! -x+z plane
1202 | Vb8(5) = Vb8(5) + W*BS3/2.0
1203 | Vb8(6) = Vb8(6) + W*BS3/2.0
1204 | return
1205 | endif
1206 |
1207 | c check if point is on +x-z plane
1208 | if((x.gt.+Cut).and.(abs(y).lt.Cut).and.(z.lt.-Cut)) then ! +x-z plane
1209 | Vb8(3) = Vb8(3) + W*BS3/2.0
1210 | Vb8(4) = Vb8(4) + W*BS3/2.0
1211 | return
1212 | endif
1213 |
1214 | c check if point is on +x+z plane
1215 | if((x.gt.+Cut).and.(abs(y).lt.Cut).and.(z.gt.+Cut)) then ! +x+z plane
1216 | Vb8(7) = Vb8(7) + W*BS3/2.0
1217 | Vb8(8) = Vb8(8) + W*BS3/2.0
1218 | return
1219 | endif
1220 |
1221 | c check if point is on -y-z plane
1222 | if((abs(x).lt.Cut).and.(y.lt.-Cut).and.(z.lt.-Cut)) then ! -y-z plane
1223 | Vb8(1) = Vb8(1) + W*BS3/2.0
1224 | Vb8(4) = Vb8(4) + W*BS3/2.0
1225 | return
1226 | endif
1227 |
1228 | c check if point is on -y+z plane
1229 | if((abs(x).lt.Cut).and.(y.lt.-Cut).and.(z.gt.+Cut)) then ! -y+z plane
1230 | Vb8(5) = Vb8(5) + W*BS3/2.0
1231 | Vb8(8) = Vb8(8) + W*BS3/2.0
1232 | return
1233 | endif
1234 |
1235 | c check if point is on +y-z plane
1236 | if((abs(x).lt.Cut).and.(y.gt.+Cut).and.(z.lt.-Cut)) then ! +y-z plane
1237 | Vb8(2) = Vb8(2) + W*BS3/2.0
1238 | Vb8(3) = Vb8(3) + W*BS3/2.0
1239 | return
1240 | endif
1241 |
1242 | c check if point is on +y+z plane
1243 | if((abs(x).lt.Cut).and.(y.gt.+Cut).and.(z.gt.+Cut)) then ! +y+z plane
1244 | Vb8(6) = Vb8(6) + W*BS3/2.0
1245 | Vb8(7) = Vb8(7) + W*BS3/2.0
1246 | return
1247 | endif
1248 |
1249 | if((x.lt.0.0).and.(y.le.0.0).and.(z.lt.0.0))Vb8(1) = Vb8(1) +W*BS3
1250 | if((x.lt.0.0).and.(y.le.0.0).and.(z.ge.0.0))Vb8(5) = Vb8(5) +W*BS3
1251 | if((x.lt.0.0).and.(y.gt.0.0).and.(z.lt.0.0))Vb8(2) = Vb8(2) +W*BS3
1252 | if((x.lt.0.0).and.(y.gt.0.0).and.(z.ge.0.0))Vb8(6) = Vb8(6) +W*BS3
1253 | if((x.ge.0.0).and.(y.ge.0.0).and.(z.lt.0.0))Vb8(3) = Vb8(3) +W*BS3
1254 | if((x.ge.0.0).and.(y.ge.0.0).and.(z.ge.0.0))Vb8(7) = Vb8(7) +W*BS3
1255 | if((x.ge.0.0).and.(y.lt.0.0).and.(z.lt.0.0))Vb8(4) = Vb8(4) +W*BS3
1256 | if((x.ge.0.0).and.(y.lt.0.0).and.(z.ge.0.0))Vb8(8) = Vb8(8) +W*BS3
1257 |
1258 | end subroutine Proj8
1259 |
1260 | c=======================================================================
1261 | c This subroutine is to output general infor. for Vbur calculation c
1262 | c=======================================================================
1263 | subroutine OutputVbur(Volume, NumInside, BinSize3, Vburf,
1264 | x Vburo, VburTot, IndFrame, VburDist, npoints,
1265 | x space, notch, IfWeb)
1266 | Implicit None
1267 |
1268 | Real*8 :: Volume
1269 | Real*8 :: Vburf
1270 | Real*8 :: Vburo
1271 | Real*8 :: VburTot
1272 | Real*8 :: BinSize3
1273 | Real*8 :: space
1274 | Real*8 :: notch
1275 |
1276 | Integer :: IfWeb
1277 | Integer :: IndFrame
1278 | Integer :: NumInside
1279 | Integer :: npoints
1280 | Integer :: i
1281 |
1282 | Integer :: VburDist(npoints)
1283 |
1284 | write(200,*)
1285 | if (IfWeb.eq.1) write(200,*) ''
1286 | write(200,'(a27)') 'Results : Volumes in Angs^3'
1287 | write(200,*)
1288 | write(200,'(a30,i15)')' N of voxels examined : ', NumInside
1289 | write(200,'(a30,f15.7)') ' Volume of voxel : ',BinSize3
1290 | write(200,*)
1291 | if (IfWeb.eq.1) write(200,*) ''
1292 |
1293 | write(200,*)
1294 | if (IfWeb.eq.1) write(200,*) ''
1295 | write(200,*) " V Free V Buried V Total V Exact"
1296 | write(200,'(4f10.1)') Vburf,Vburo, VburTot, Volume
1297 |
1298 | write(200,*)
1299 | write(200,'(a20,f10.2,a7)')'Buried volume = ',Vburo,' Angs^3'
1300 | if (IfWeb.eq.1) write(200,*) ''
1301 |
1302 | write(200,*)
1303 | if (IfWeb.eq.1) write(200,*) ''
1304 | write(200,*) " %V Free %V Buried % V Tot/V Ex"
1305 | write(200,'(4f10.1)')
1306 | x 100.0*Vburf/VburTot, 100.0*Vburo/VburTot, 100.0*VburTot/Volume
1307 | if (IfWeb.eq.1) write(200,*) ''
1308 |
1309 | write(200,*)
1310 | if (IfWeb.eq.1) write(200,*) ''
1311 | write(200,'(a35,f8.1)')" The %V Bur of the molecule is: ",
1312 | x 100.0*Vburo / VburTot
1313 | write(201,'(i6,f10.5)')IndFrame, 100.0*Vburo / VburTot
1314 | if (IfWeb.eq.1) write(200,*) ''
1315 | write(200,*)
1316 |
1317 | write(202,*) '# notch n/ntot'
1318 | do i = 1, npoints
1319 | write(202,'(f8.3,f10.6)') REAL(i-1)*space + notch,
1320 | x VburDist(i)/Real(IndFrame)
1321 | enddo
1322 | end subroutine OutPutVbur
1323 |
1324 | c=======================================================================
1325 | c This subroutine is to output the projected Vbur into 4 quadrants
1326 | c=======================================================================
1327 | subroutine OutputQua4(Vbur4f, Vbur4o, IndFrame, IfWeb)
1328 | Implicit None
1329 |
1330 | Integer :: IndFrame
1331 | Integer :: IfWeb
1332 | Integer :: i
1333 | Real*8 :: Vbur4f(4)
1334 | Real*8 :: Vbur4o(4)
1335 |
1336 | if (IfWeb.eq.1) write(200,*) ''
1337 | write(200,*) ' Quadrants analysis'
1338 | if (IfWeb.eq.1) write(200,*) ''
1339 |
1340 | if (IfWeb.eq.1) write(200,*) ''
1341 | write(200,*) 'Quadrant V f V b V t %V f %V b'
1342 |
1343 | write(200,'(a5,4x,5f8.1)')"SW ", Vbur4f(1), Vbur4o(1),
1344 | x (Vbur4f(1) + Vbur4o(1)),
1345 | x 100.0 * Vbur4f(1) / (Vbur4f(1) + Vbur4o(1)),
1346 | x 100.0 * Vbur4o(1) / (Vbur4f(1) + Vbur4o(1))
1347 | write(200,'(a5,4x,5f8.1)')"NW ", Vbur4f(2), Vbur4o(2),
1348 | x (Vbur4f(2) + Vbur4o(2)),
1349 | x 100.0 * Vbur4f(2) / (Vbur4f(2) + Vbur4o(2)),
1350 | x 100.0 * Vbur4o(2) / (Vbur4f(2) + Vbur4o(2))
1351 | write(200,'(a5,4x,5f8.1)')"NE ", Vbur4f(3), Vbur4o(3),
1352 | x (Vbur4f(3) + Vbur4o(3)),
1353 | x 100.0 * Vbur4f(3) / (Vbur4f(3) + Vbur4o(3)),
1354 | x 100.0 * Vbur4o(3) / (Vbur4f(3) + Vbur4o(3))
1355 | write(200,'(a5,4x,5f8.1)')"SE ", Vbur4f(4), Vbur4o(4),
1356 | x (Vbur4f(4) + Vbur4o(4)),
1357 | x 100.0 * Vbur4f(4) / (Vbur4f(4) + Vbur4o(4)),
1358 | x 100.0 * Vbur4o(4) / (Vbur4f(4) + Vbur4o(4))
1359 |
1360 | write(203,*) '# notch -- -+ ++ +- '
1361 | write(203,'(i5,4f10.5)')IndFrame,
1362 | x (100.0*(Vbur4o(i)/(Vbur4f(i) + Vbur4o(i))), i = 1, 4)
1363 | if (IfWeb.eq.1) write(200,*) ''
1364 |
1365 | end subroutine OutputQua4
1366 |
1367 | c=======================================================================
1368 | c This subroutine is to output the projected Vbur into 8 octants
1369 | c=======================================================================
1370 | subroutine OutputQua8(Vbur8f, Vbur8o, IndFrame, IfWeb)
1371 | Implicit None
1372 |
1373 | Integer :: IndFrame
1374 | Integer :: IfWeb
1375 | Integer :: i
1376 |
1377 | Real*8 :: Vbur8f(8)
1378 | Real*8 :: Vbur8o(8)
1379 |
1380 | write(200,*)
1381 | if(IfWeb.eq.1)write(200,*) ''
1382 | write(200,*) ' Octants analysis'
1383 | if(IfWeb.eq.1)write(200,*) ''
1384 |
1385 | if(IfWeb.eq.1)write(200,*) ''
1386 | write(200,*)'Octant V f V b V t %V f %V b'
1387 |
1388 | write(200,'(a5,4x,5f8.1)')" SW-z", Vbur8f(1), Vbur8o(1),
1389 | x (Vbur8f(1) + Vbur8o(1)),
1390 | x 100.0 * Vbur8f(1) / (Vbur8f(1) + Vbur8o(1)),
1391 | x 100.0 * Vbur8o(1) / (Vbur8f(1) + Vbur8o(1))
1392 | write(200,'(a5,4x,5f8.1)')" NW-z", Vbur8f(2), Vbur8o(2),
1393 | x (Vbur8f(2) + Vbur8o(2)),
1394 | x 100.0 * Vbur8f(2) / (Vbur8f(2) + Vbur8o(2)),
1395 | x 100.0 * Vbur8o(2) / (Vbur8f(2) + Vbur8o(2))
1396 | write(200,'(a5,4x,5f8.1)')" NE-z", Vbur8f(3), Vbur8o(3),
1397 | x (Vbur8f(3) + Vbur8o(3)),
1398 | x 100.0 * Vbur8f(3) / (Vbur8f(3) + Vbur8o(3)),
1399 | x 100.0 * Vbur8o(3) / (Vbur8f(3) + Vbur8o(3))
1400 | write(200,'(a5,4x,5f8.1)')" SE-z", Vbur8f(4), Vbur8o(4),
1401 | x (Vbur8f(4) + Vbur8o(4)),
1402 | x 100.0 * Vbur8f(4) / (Vbur8f(4) + Vbur8o(4)),
1403 | x 100.0 * Vbur8o(4) / (Vbur8f(4) + Vbur8o(4))
1404 | write(200,'(a5,4x,5f8.1)')" SW+z", Vbur8f(5), Vbur8o(5),
1405 | x (Vbur8f(5) + Vbur8o(5)),
1406 | x 100.0 * Vbur8f(5) / (Vbur8f(5) + Vbur8o(5)),
1407 | x 100.0 * Vbur8o(5) / (Vbur8f(5) + Vbur8o(5))
1408 | write(200,'(a5,4x,5f8.1)')" NW+z", Vbur8f(6), Vbur8o(6),
1409 | x (Vbur8f(6) + Vbur8o(6)),
1410 | x 100.0 * Vbur8f(6) / (Vbur8f(6) + Vbur8o(6)),
1411 | x 100.0 * Vbur8o(6) / (Vbur8f(6) + Vbur8o(6))
1412 | write(200,'(a5,4x,5f8.1)')" NE+z", Vbur8f(7), Vbur8o(7),
1413 | x (Vbur8f(7) + Vbur8o(7)),
1414 | x 100.0 * Vbur8f(7) / (Vbur8f(7) + Vbur8o(7)),
1415 | x 100.0 * Vbur8o(7) / (Vbur8f(7) + Vbur8o(7))
1416 | write(200,'(a5,4x,5f8.1)')" SE+z", Vbur8f(8), Vbur8o(8),
1417 | x (Vbur8f(8) + Vbur8o(8)),
1418 | x 100.0 * Vbur8f(8) / (Vbur8f(8) + Vbur8o(8)),
1419 | x 100.0 * Vbur8o(8) / (Vbur8f(8) + Vbur8o(8))
1420 |
1421 | write(205,*) '# notch --- -+- ++- +-- --+
1422 | x -++ +++ +-+'
1423 | write(205, '(i5,8f10.5)')IndFrame,
1424 | x (100.0*(Vbur8o(i)/(Vbur8f(i) + Vbur8o(i))), i = 1, 8)
1425 |
1426 | if(IfWeb.eq.1)write(200,*) ''
1427 |
1428 | end subroutine OutputQua8
1429 |
1430 | c=======================================================================
1431 | c This subroutine converts atom names from lower to upper case
1432 | c=======================================================================
1433 | subroutine ToUpperCase(strIn,strOut)
1434 | implicit none
1435 |
1436 | integer :: i,j
1437 |
1438 | character*4 :: StrIN, StrOut
1439 |
1440 | do i = 1, len(strIn)
1441 | j = iachar(strIn(i:i))
1442 | if (j>= iachar("a") .and. j<=iachar("z") ) then
1443 | strOut(i:i) = achar(iachar(strIn(i:i))-32)
1444 | else
1445 | strOut(i:i) = strIn(i:i)
1446 | end if
1447 | end do
1448 |
1449 | end subroutine ToUpperCase
1450 |
1451 | c=======================================================================
1452 | c This subroutine writes the molecule in file iouint
1453 | c=======================================================================
1454 | subroutine WriteXYZ(iounit,natoms,Title,AtomNames,Coord)
1455 | implicit none
1456 |
1457 | Integer :: i,j, iounit
1458 | Integer :: natoms
1459 |
1460 | Real*8 :: Coord(natoms*3)
1461 |
1462 | Character*50 :: Title
1463 | Character*4 :: AtomNames(natoms)
1464 |
1465 | write(iounit,*)natoms
1466 | write(iounit,'(a39)')Title
1467 | do i = 1, natoms
1468 | j = (i-1)*3
1469 | write(iounit,'(a4,1x,3f10.5)')AtomNames(i),
1470 | x Coord(j+1),Coord(j+2),Coord(j+3)
1471 | enddo
1472 |
1473 | end subroutine
1474 |
1475 | c=======================================================================
1476 | c This subroutine removes atoms type ATOMNAME from coordinates
1477 | c=======================================================================
1478 | subroutine RemoveAtom(natoms,natoms_new,AtomNames,AtomNames_new,
1479 | x Coord,Coord_new,ATOMNAME)
1480 | implicit none
1481 |
1482 | Integer :: i,j,k
1483 | Integer :: natoms, natoms_new
1484 |
1485 | Real*8 :: Coord(natoms*3)
1486 | Real*8 :: Coord_new(natoms*3)
1487 |
1488 | Character*4 :: ATOMNAME
1489 | Character*4 :: AtomNames(natoms)
1490 | Character*4 :: AtomNames_new(natoms)
1491 |
1492 | natoms_new = 0
1493 | do i=1, natoms
1494 | if(AtomNames(i).ne.ATOMNAME)then
1495 | j = (i-1)*3
1496 | natoms_new = natoms_new + 1
1497 | k = (natoms_new-1)*3
1498 | Coord_new(k+1) = Coord(j+1)
1499 | Coord_new(k+2) = Coord(j+2)
1500 | Coord_new(k+3) = Coord(j+3)
1501 | AtomNames_new(natoms_new) = AtomNames(i)
1502 | endif
1503 | enddo
1504 |
1505 | do i = 1, natoms_new
1506 | j = (i-1)*3
1507 | Coord(j+1) = Coord_new(j+1)
1508 | Coord(j+2) = Coord_new(j+2)
1509 | Coord(j+3) = Coord_new(j+3)
1510 | AtomNames(i) = AtomNames_new(i)
1511 | enddo
1512 |
1513 | natoms = natoms_new
1514 |
1515 | end subroutine RemoveAtom
1516 |
--------------------------------------------------------------------------------
/test/test_py2sambvca.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import unittest
4 | import shutil
5 |
6 | from py2sambvca import p2s
7 |
8 |
9 | class Testpy2sambvca(unittest.TestCase):
10 | """
11 | Test the various functionalities of py2sambvca.
12 | """
13 |
14 | @classmethod
15 | def setUpClass(self):
16 | """Set input attirbutes for py2sambvca."""
17 | cwd = os.getcwd()
18 | self.xyz_file = os.path.join(cwd, "test", "data", "nhc.xyz")
19 | self.sphere_ids = [22]
20 | self.z_ids = [5]
21 | self.xz_ids = [1]
22 | self.delete_ids = [22]
23 | self.radius = 3.5
24 | self.displacement = 0.00
25 | self.mesh_size = 0.10
26 | self.remove_H = 1
27 | self.orient_Z = 0
28 | self.write_surf_files = 0
29 | self.working_dir = os.path.join(os.getcwd(), "test_dir")
30 | if sys.platform == "win32":
31 | self.exe_path = os.path.join(cwd, "sambvca21.exe")
32 | else:
33 | self.exe_path = os.path.join(cwd, "sambvca21.x")
34 |
35 | def test_no_init_error(self):
36 | """
37 | Call various methods without first writing input, assure result is false.
38 | """
39 | test_p2s = p2s(
40 | self.xyz_file,
41 | self.sphere_ids,
42 | self.z_ids,
43 | self.xz_ids,
44 | path_to_sambvcax=self.exe_path,
45 | verbose=2,
46 | )
47 | test_p2s.clean_files()
48 |
49 | # calc returns false without input file
50 | self.assertFalse(test_p2s.calc())
51 |
52 | # getters raise errors without output files
53 | with self.assertRaises(
54 | RuntimeError,
55 | msg="get_regex should not work without first running",
56 | ):
57 | test_p2s.get_regex("test")
58 |
59 | with self.assertRaises(
60 | RuntimeError,
61 | msg="get should not work without first running and parsing reults",
62 | ):
63 | test_p2s.get("test")
64 |
65 | def test_bad_executable_error(self):
66 | """
67 | Attempt to call calc with a bad exe path
68 | """
69 | test_p2s = p2s(
70 | self.xyz_file,
71 | self.sphere_ids,
72 | self.z_ids,
73 | self.xz_ids,
74 | path_to_sambvcax="/path/does/not/exist/sambvca21.x",
75 | verbose=2,
76 | )
77 | with self.assertRaises(RuntimeError):
78 | test_p2s.run()
79 |
80 | def test_invalid_xyz_error(self):
81 | """
82 | Attempt to init class with invalid xyz file.
83 | """
84 | with self.assertRaises(RuntimeError):
85 | test_p2s = p2s(
86 | "invalid_file.pdb",
87 | self.sphere_ids,
88 | self.z_ids,
89 | self.xz_ids,
90 | path_to_sambvcax=self.exe_path,
91 | verbose=0,
92 | )
93 |
94 | def test_full_init(self):
95 | """
96 | Initialization and attribute check.
97 | """
98 | test_p2s = p2s(
99 | self.xyz_file,
100 | self.sphere_ids,
101 | self.z_ids,
102 | self.xz_ids,
103 | self.delete_ids,
104 | self.radius,
105 | self.displacement,
106 | self.mesh_size,
107 | self.remove_H,
108 | self.orient_Z,
109 | self.write_surf_files,
110 | self.exe_path,
111 | "default",
112 | )
113 | self.assertEqual(
114 | self.sphere_ids,
115 | test_p2s.sphere_center_atom_ids,
116 | "sphere_center_atom_ids not set correctly.",
117 | )
118 | self.assertEqual(
119 | self.z_ids,
120 | test_p2s.z_ax_atom_ids,
121 | "z_ax_atom_ids not set correctly.",
122 | )
123 | self.assertEqual(
124 | self.xz_ids,
125 | test_p2s.xz_plane_atoms_ids,
126 | "xz_plane_atoms_ids not set correctly.",
127 | )
128 | self.assertEqual(
129 | self.delete_ids,
130 | test_p2s.atoms_to_delete_ids,
131 | "atoms_to_delete_ids not set correctly.",
132 | )
133 | self.assertEqual(
134 | self.radius,
135 | test_p2s.sphere_radius,
136 | "sphere_radius not set correctly.",
137 | )
138 | self.assertEqual(
139 | self.displacement,
140 | test_p2s.displacement,
141 | "displacement not set correctly.",
142 | )
143 | self.assertEqual(
144 | self.mesh_size,
145 | test_p2s.mesh_size,
146 | "mesh_size not set correctly.",
147 | )
148 | self.assertEqual(
149 | self.remove_H,
150 | test_p2s.remove_H,
151 | "remove_H not set correctly.",
152 | )
153 | self.assertEqual(
154 | self.orient_Z,
155 | test_p2s.orient_z,
156 | "orient_z not set correctly.",
157 | )
158 | self.assertEqual(
159 | self.write_surf_files,
160 | test_p2s.write_surf_files,
161 | "write_surf_files not set correctly.",
162 | )
163 | self.assertEqual(
164 | self.exe_path,
165 | test_p2s.path_to_sambvcax,
166 | "path_to_sambvcax not set correctly.",
167 | )
168 | test_p2s.write_input()
169 | test_p2s.clean_files()
170 |
171 | def test_canot_retrieve_error(self):
172 | """
173 | py2sambvca should provide a helpful error when it cannot retrieve data
174 | """
175 | test_p2s = p2s(
176 | self.xyz_file,
177 | self.sphere_ids,
178 | self.z_ids,
179 | self.xz_ids,
180 | working_dir=self.working_dir,
181 | )
182 | # mimic an unsuccessful run
183 | blank_file = os.path.join(self.working_dir, "py2sambvca_input.out")
184 | open(blank_file, "w").close()
185 | # would nromally result in a TypeError from py2sambvca trying to use a nonexistent regex match
186 | with self.assertRaises(RuntimeError):
187 | test_p2s.parse_output()
188 | os.remove(blank_file)
189 |
190 | def test_partial_init(self):
191 | """
192 | Provide only the bare minimum inputs, check all attributes.
193 | """
194 | test_p2s = p2s(
195 | self.xyz_file,
196 | self.sphere_ids,
197 | self.z_ids,
198 | self.xz_ids,
199 | path_to_sambvcax=self.exe_path,
200 | )
201 | self.assertEqual(
202 | self.sphere_ids,
203 | test_p2s.sphere_center_atom_ids,
204 | "sphere_center_atom_ids not set correctly.",
205 | )
206 | self.assertEqual(
207 | self.z_ids,
208 | test_p2s.z_ax_atom_ids,
209 | "z_ax_atom_ids not set correctly.",
210 | )
211 | self.assertEqual(
212 | self.xz_ids,
213 | test_p2s.xz_plane_atoms_ids,
214 | "xz_plane_atoms_ids not set correctly.",
215 | )
216 | self.assertEqual(
217 | None,
218 | test_p2s.atoms_to_delete_ids,
219 | "atoms_to_delete_ids not set correctly.",
220 | )
221 | self.assertEqual(
222 | 3.5,
223 | test_p2s.sphere_radius,
224 | "sphere_radius not set correctly.",
225 | )
226 | self.assertEqual(
227 | 0.0,
228 | test_p2s.displacement,
229 | "displacement not set correctly.",
230 | )
231 | self.assertEqual(
232 | 0.10,
233 | test_p2s.mesh_size,
234 | "mesh_size not set correctly.",
235 | )
236 | self.assertEqual(
237 | 1,
238 | test_p2s.remove_H,
239 | "remove_H not set correctly.",
240 | )
241 | self.assertEqual(
242 | 1,
243 | test_p2s.orient_z,
244 | "orient_z not set correctly.",
245 | )
246 | self.assertEqual(
247 | 1,
248 | test_p2s.write_surf_files,
249 | "write_surf_files not set correctly.",
250 | )
251 | self.assertEqual(
252 | self.exe_path,
253 | test_p2s.path_to_sambvcax,
254 | "path_to_sambvcax not set correctly.",
255 | )
256 |
257 | def test_input_writer(self):
258 | """
259 | Test ability to write input file separately.
260 | """
261 | test_p2s = p2s(
262 | self.xyz_file,
263 | self.sphere_ids,
264 | self.z_ids,
265 | self.xz_ids,
266 | path_to_sambvcax=self.exe_path,
267 | verbose=0,
268 | )
269 | test_p2s.write_input()
270 | with open("py2sambvca_input.inp") as file:
271 | self.assertIsNotNone(file)
272 |
273 | def test_calc(self):
274 | """
275 | Test call to calculator.
276 | """
277 | test_p2s = p2s(
278 | self.xyz_file,
279 | self.sphere_ids,
280 | self.z_ids,
281 | self.xz_ids,
282 | path_to_sambvcax=self.exe_path,
283 | verbose=0,
284 | )
285 | test_p2s.write_input()
286 | self.assertTrue(test_p2s.calc())
287 |
288 | def test_buried_volume(self):
289 | """
290 | Test retrieval of buried volume.
291 | """
292 | test_p2s = p2s(
293 | self.xyz_file,
294 | self.sphere_ids,
295 | self.z_ids,
296 | self.xz_ids,
297 | path_to_sambvcax=self.exe_path,
298 | verbose=1,
299 | )
300 | test_p2s.write_input()
301 | test_p2s.calc()
302 | self.assertEqual(test_p2s.get_buried_vol(), 55.7)
303 |
304 | def test_clean_files(self):
305 | """
306 | File cleaner should remove all py2sambvca files.
307 | """
308 | test_p2s = p2s(
309 | self.xyz_file,
310 | self.sphere_ids,
311 | self.z_ids,
312 | self.xz_ids,
313 | path_to_sambvcax=self.exe_path,
314 | verbose=0,
315 | )
316 | test_p2s.write_input()
317 | test_p2s.clean_files()
318 | with self.assertRaises(FileNotFoundError):
319 | open("py2sambvca_input.inp")
320 |
321 | def test_run(self):
322 | """
323 | Full call to all functions using run.
324 | """
325 | test_p2s = p2s(
326 | self.xyz_file,
327 | self.sphere_ids,
328 | self.z_ids,
329 | self.xz_ids,
330 | path_to_sambvcax=self.exe_path,
331 | verbose=0,
332 | )
333 | test_p2s.run()
334 |
335 | def test_create_working_dir(self):
336 | """
337 | Initialize with a non-existsent directory and check for creation
338 | """
339 | test_p2s = p2s(
340 | self.xyz_file,
341 | self.sphere_ids,
342 | self.z_ids,
343 | self.xz_ids,
344 | path_to_sambvcax=self.exe_path,
345 | verbose=0,
346 | working_dir=self.working_dir,
347 | )
348 | self.assertTrue(os.path.exists(self.working_dir))
349 |
350 | def test_existing_working_dir(self):
351 | """
352 | Initialize with an existing directory and check for creation of input in it
353 | """
354 | test_p2s = p2s(
355 | self.xyz_file,
356 | self.sphere_ids,
357 | self.z_ids,
358 | self.xz_ids,
359 | path_to_sambvcax=self.exe_path,
360 | verbose=0,
361 | working_dir=self.working_dir,
362 | )
363 | test_p2s.write_input()
364 | self.assertTrue(
365 | os.path.exists(os.path.join(self.working_dir, "py2sambvca_input.inp"))
366 | )
367 |
368 | def test_run_different_dir(self):
369 | """
370 | Full call to all functions using run in a different directory
371 | """
372 | test_p2s = p2s(
373 | self.xyz_file,
374 | self.sphere_ids,
375 | self.z_ids,
376 | self.xz_ids,
377 | path_to_sambvcax=self.exe_path,
378 | verbose=0,
379 | working_dir=self.working_dir,
380 | )
381 | test_p2s.run()
382 |
383 | def test_getters(self):
384 | """
385 | Test getter methods.
386 | """
387 | test_p2s = p2s(
388 | self.xyz_file,
389 | self.sphere_ids,
390 | self.z_ids,
391 | self.xz_ids,
392 | path_to_sambvcax=self.exe_path,
393 | verbose=2,
394 | )
395 | test_p2s.run()
396 | self.assertEqual(
397 | test_p2s.get_quadrant_free_volume()["SW"],
398 | 21.0,
399 | )
400 | self.assertEqual(
401 | test_p2s.get_quadrant_buried_volume()["SW"],
402 | 23.9,
403 | )
404 | self.assertEqual(
405 | test_p2s.get_quadrant_total_volume()["NW"],
406 | 44.9,
407 | )
408 | self.assertEqual(
409 | test_p2s.get_quadrant_percent_buried_volume()["SE"],
410 | 58.1,
411 | )
412 | self.assertEqual(
413 | test_p2s.get_quadrant_percent_free_volume()["SW"],
414 | 53.2,
415 | )
416 | self.assertEqual(
417 | test_p2s.get_octant_free_volume()["SW-z"],
418 | 16.7,
419 | )
420 | self.assertEqual(
421 | test_p2s.get_octant_buried_volume()["SW+z"],
422 | 18.1,
423 | )
424 | self.assertEqual(
425 | test_p2s.get_octant_total_volume()["SE-z"],
426 | 22.4,
427 | )
428 | self.assertEqual(
429 | test_p2s.get_octant_percent_buried_volume()["SE+z"],
430 | 90.2,
431 | )
432 | self.assertEqual(
433 | test_p2s.get_octant_percent_free_volume()["NW+z"],
434 | 90.4,
435 | )
436 | self.assertEqual(
437 | test_p2s.get_free_volume(),
438 | 79.4,
439 | )
440 | self.assertEqual(
441 | test_p2s.get_buried_volume(),
442 | 100.0,
443 | )
444 | self.assertEqual(
445 | test_p2s.get_exact_volume(),
446 | 179.6,
447 | )
448 | self.assertEqual(
449 | test_p2s.get_total_volume(),
450 | 179.4,
451 | )
452 | self.assertEqual(
453 | test_p2s.get_percent_buried_volume(),
454 | 55.7,
455 | )
456 | self.assertEqual(
457 | test_p2s.get_percent_free_volume(),
458 | 44.3,
459 | )
460 | self.assertEqual(
461 | test_p2s.get_percent_total_volume(),
462 | 99.9,
463 | )
464 | # invalid key should raise error
465 | with self.assertRaises(RuntimeError):
466 | test_p2s.get("invalid_key_test")
467 | # invalid call to get should raise error
468 | with self.assertRaises(RuntimeError):
469 | test_p2s.get(
470 | "buried_volume",
471 | quadrant=True,
472 | octant=True,
473 | )
474 |
475 | @classmethod
476 | def tearDownClass(self):
477 | """Clean up the mess."""
478 | test_p2s = p2s(
479 | self.xyz_file,
480 | self.sphere_ids,
481 | self.z_ids,
482 | self.xz_ids,
483 | )
484 | test_p2s.clean_files()
485 | shutil.rmtree(self.working_dir)
486 |
487 |
488 | if __name__ == "__main__":
489 | unittest.main()
490 |
--------------------------------------------------------------------------------
/test/test_static_output.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | import shutil
4 |
5 | from py2sambvca import p2s
6 |
7 |
8 | class Teststaticoutput(unittest.TestCase):
9 | """
10 | Test the various functionalities of py2sambvca on a static output file.
11 | """
12 |
13 | @classmethod
14 | def setUpClass(self):
15 | """Set input attirbutes for py2sambvca."""
16 | # current working directory
17 | cwd = os.getcwd()
18 | # directory for temporary test files
19 | self.working_dir = os.path.join(os.getcwd(), "test_dir")
20 | os.makedirs(self.working_dir, exist_ok=True)
21 | # get the saved data file, copy to working dir
22 | static_filepath = os.path.join(cwd, "test", "data", "static_output.out")
23 | self.static_output = os.path.join(self.working_dir, "py2sambvca_input.out")
24 | shutil.copy(static_filepath, self.static_output)
25 | # others needed to initialize a dummy py2sambvca
26 | self.xyz_file = os.path.join(cwd, "test", "data", "nhc.xyz")
27 | self.sphere_ids = [22]
28 | self.z_ids = [5]
29 | self.xz_ids = [1]
30 |
31 | def test_get_free_volume(self):
32 | """
33 | Retrieve the percent free volume from the saved output file.
34 | """
35 | test_p2s = p2s(
36 | self.xyz_file,
37 | self.sphere_ids,
38 | self.z_ids,
39 | self.xz_ids,
40 | working_dir=self.working_dir,
41 | )
42 | test_p2s.parse_output()
43 | result = test_p2s.get_percent_free_volume()
44 | self.assertEqual(result, 66.2)
45 |
46 | @classmethod
47 | def tearDownClass(self):
48 | """Clean up the mess."""
49 | test_p2s = p2s(
50 | self.xyz_file,
51 | self.sphere_ids,
52 | self.z_ids,
53 | self.xz_ids,
54 | )
55 | test_p2s.clean_files()
56 | shutil.rmtree(self.working_dir)
57 |
58 |
59 | if __name__ == "__main__":
60 | unittest.main()
61 |
--------------------------------------------------------------------------------