From b869ee74fb8f3e80b5b371c00777509c32af2d22 Mon Sep 17 00:00:00 2001 From: Vitor Hideyoshi Date: Sat, 22 Apr 2023 11:27:20 -0300 Subject: [PATCH] Implements More Tests and Begins Dice Refactor Implementation --- Pipfile | 3 +- Pipfile.lock | 149 ++++++++++---- diceplayer/__main__.py | 7 +- diceplayer/shared/config/dice_dto.py | 10 +- diceplayer/shared/config/step_dto.py | 18 ++ diceplayer/shared/environment/molecule.py | 121 ++++------- diceplayer/shared/environment/system.py | 194 +++++++++--------- diceplayer/shared/external/__external.py | 2 +- diceplayer/shared/external/__init__.py | 1 + diceplayer/shared/external/dice.py | 45 +++- diceplayer/shared/external/gaussian.py | 3 +- tests/shared/config/__init__.py | 0 tests/shared/config/test_dice_dto.py | 89 ++++++++ tests/shared/environment/molecule.py | 61 ------ .../environment/{atom.py => test_atom.py} | 0 tests/shared/environment/test_molecule.py | 147 +++++++++++++ tests/shared/environment/test_system.py | 50 +++++ tests/shared/external/__init__.py | 0 tests/shared/external/test_dice.py | 116 +++++++++++ 19 files changed, 719 insertions(+), 297 deletions(-) create mode 100644 diceplayer/shared/config/step_dto.py create mode 100644 tests/shared/config/__init__.py create mode 100644 tests/shared/config/test_dice_dto.py delete mode 100644 tests/shared/environment/molecule.py rename tests/shared/environment/{atom.py => test_atom.py} (100%) create mode 100644 tests/shared/environment/test_molecule.py create mode 100644 tests/shared/environment/test_system.py create mode 100644 tests/shared/external/__init__.py create mode 100644 tests/shared/external/test_dice.py diff --git a/Pipfile b/Pipfile index ed75480..c8b8028 100644 --- a/Pipfile +++ b/Pipfile @@ -17,6 +17,7 @@ dacite = "*" [dev-packages] pyinstaller = "*" +coverage = "*" [requires] -python_version = "3.10" +python_version = ">3.8" diff --git a/Pipfile.lock b/Pipfile.lock index d61ee4f..68f88c5 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "f68e7183744102bdc470014dfc368a947097de4d482f891b1c9b31d17160fd64" + "sha256": "9af00878e9f259614f72d3c11de93c16ede78176a30d8337c741be06c048c8a8" }, "pipfile-spec": 6, "requires": { - "python_version": "3.10" + "python_version": ">3.8" }, "sources": [ { @@ -41,12 +41,14 @@ }, "intel-openmp": { "hashes": [ - "sha256:1ebce47df0b5ddad77992a70689fd3185df25f47cf901dd8384fd6d05a974f81", - "sha256:77131c2cda75ace2060f6e9cdcc9f9c672ec838c4602fc544ce0858cfe8f416e", - "sha256:adfb32b0dde6b3a95ced62608b05a45a7cfad928054136ddbfe0563e16cbf33c" + "sha256:0ca275fd223bda7b835f3b30221ee462e934468738dcbb26741c57e795f09818", + "sha256:0e3166732d0a2105988e538baaffff1182fd5a23883da255b3bf62c187475b46", + "sha256:39308882e7bcdbd17e2d2b3be96e34ed475f60205bc90cb86694d0bcfc955bbc", + "sha256:523094cd2593d31c71ce2d6b2942389fa9d231dce9f8825bfec9e2fdc545e037", + "sha256:7bd67f60ca3da7dbe39ab2a296b5322f8e8248d59d7573068deeeb9b0f901895" ], "index": "pypi", - "version": "==2023.0.0" + "version": "==2023.1.0" }, "nptyping": { "hashes": [ @@ -99,29 +101,29 @@ }, "pyinstaller": { "hashes": [ - "sha256:314fb883caf3cbf06adbea2b77671bb73c3481568e994af0467ea7e47eb64755", - "sha256:3b74f50a57b1413047042e47033480b7324b091f23dff790a4494af32b377d94", - "sha256:4f4d818588e2d8de4bf24ed018056c3de0c95898ad25719e12d68626161b4933", - "sha256:502a2166165a8e8c3d99c19272e923d2548bac2132424d78910ef9dd8bb11705", - "sha256:5c9632a20faecd6d79f0124afb31e6557414d19be271e572765b474f860f8d76", - "sha256:8d004699c5d71c704c14a5f81eec233faa4f87a3bf0ae68e222b87d63f5dd17e", - "sha256:a62ee598b137202ef2e99d8dbaee6bc7379a6565c3ddf0331decb41b98eff1a2", - "sha256:bacf236b5c2f8f674723a39daca399646dceb470881f842f52e393b9a67ff2f8", - "sha256:bf1f7b7e88b467d7aefcdb2bc9cbd2e856ca88c5ab232c0efe0848f146d3bd5f", - "sha256:ded780f0d3642d7bfc21d97b98d4ec4b41d2fe70c3f5c5d243868612f536e011", - "sha256:e68bcadf32edc1171ccb06117699a6a4f8e924b7c2c8812cfa00fd0186ade4ee", - "sha256:f9361eff44c7108c2312f39d85ed768c4ada7e0aa729046bbcef3ef3c1577d18" + "sha256:247b99c52dc3cf69eba905da30dbca0a8ea309e1058cab44658ac838d9b8f2f0", + "sha256:2d16641a495593d174504263b038a6d3d46b3b15a381ccb216cf6cce67723512", + "sha256:333b4ffda38d9c0a561c38429dd9848d37aa78f3b8ea8a6f2b2e69a60d523c02", + "sha256:6afc7aa4885ffd3e6121a8cf2138830099f874c18cb5869bed8c1a42db82d060", + "sha256:6ecc464bf56919bf2d6bff275f38d85ff08ae747b8ead3a0c26cf85573b3c723", + "sha256:7a1db833bb0302b66ae3ae337fbd5487699658ce869ca4d538b5359b8179e83a", + "sha256:85e39e36d03355423636907a26a9bfa06fdc93cb1086441b19d2d0ca448479fa", + "sha256:915a502802c751bafd92d568ac57468ec6cdf252b8308aa9a167bbc2c565ad2d", + "sha256:9e9a38f41f8280c8e29b294716992852281b41fbe64ba330ebab671efe27b26d", + "sha256:bb7de35cd209a0a0358aec761a273ae951d2161c03728f15d9a640d06a88e472", + "sha256:df97aaf1103a1c485aa3c9947792a86675e370f5ce9b436b4a84e34a4180c8d2", + "sha256:f677fbc151db1eb00ada94e86ed128e7b359cbd6bf3f6ea815afdde687692d46" ], "index": "pypi", - "version": "==5.8.0" + "version": "==5.10.1" }, "pyinstaller-hooks-contrib": { "hashes": [ - "sha256:29d052eb73e0ab8f137f11df8e73d464c1c6d4c3044d9dc8df2af44639d8bfbf", - "sha256:bd578781cd6a33ef713584bf3726f7cd60a3e656ec08a6cc7971e39990808cc0" + "sha256:7fb856a81fd06a717188a3175caa77e902035cc067b00b583c6409c62497b23f", + "sha256:e02c5f0ee3d4f5814588c2128caf5036c058ba764aaf24d957bb5311ad8690ad" ], "markers": "python_version >= '3.7'", - "version": "==2023.0" + "version": "==2023.2" }, "pyyaml": { "hashes": [ @@ -249,11 +251,11 @@ }, "setuptools": { "hashes": [ - "sha256:e5fd0a713141a4a105412233c63dc4e17ba0090c8e8334594ac790ec97792330", - "sha256:f106dee1b506dee5102cc3f3e9e68137bbad6d47b616be7991714b0c62204251" + "sha256:6f0839fbdb7e3cfef1fc38d7954f5c1c26bf4eebb155a55c9bf8faf997b9fb67", + "sha256:bb16732e8eb928922eabaa022f881ae2b7cdcfaf9993ef1f5e841a96d32b8e0c" ], "markers": "python_version >= '3.7'", - "version": "==67.4.0" + "version": "==67.7.1" }, "typing": { "hashes": [ @@ -280,39 +282,96 @@ ], "version": "==0.17.3" }, - "pyinstaller": { + "coverage": { "hashes": [ - "sha256:314fb883caf3cbf06adbea2b77671bb73c3481568e994af0467ea7e47eb64755", - "sha256:3b74f50a57b1413047042e47033480b7324b091f23dff790a4494af32b377d94", - "sha256:4f4d818588e2d8de4bf24ed018056c3de0c95898ad25719e12d68626161b4933", - "sha256:502a2166165a8e8c3d99c19272e923d2548bac2132424d78910ef9dd8bb11705", - "sha256:5c9632a20faecd6d79f0124afb31e6557414d19be271e572765b474f860f8d76", - "sha256:8d004699c5d71c704c14a5f81eec233faa4f87a3bf0ae68e222b87d63f5dd17e", - "sha256:a62ee598b137202ef2e99d8dbaee6bc7379a6565c3ddf0331decb41b98eff1a2", - "sha256:bacf236b5c2f8f674723a39daca399646dceb470881f842f52e393b9a67ff2f8", - "sha256:bf1f7b7e88b467d7aefcdb2bc9cbd2e856ca88c5ab232c0efe0848f146d3bd5f", - "sha256:ded780f0d3642d7bfc21d97b98d4ec4b41d2fe70c3f5c5d243868612f536e011", - "sha256:e68bcadf32edc1171ccb06117699a6a4f8e924b7c2c8812cfa00fd0186ade4ee", - "sha256:f9361eff44c7108c2312f39d85ed768c4ada7e0aa729046bbcef3ef3c1577d18" + "sha256:06ddd9c0249a0546997fdda5a30fbcb40f23926df0a874a60a8a185bc3a87d93", + "sha256:0743b0035d4b0e32bc1df5de70fba3059662ace5b9a2a86a9f894cfe66569013", + "sha256:0f3736a5d34e091b0a611964c6262fd68ca4363df56185902528f0b75dbb9c1f", + "sha256:1127b16220f7bfb3f1049ed4a62d26d81970a723544e8252db0efde853268e21", + "sha256:172db976ae6327ed4728e2507daf8a4de73c7cc89796483e0a9198fd2e47b462", + "sha256:182eb9ac3f2b4874a1f41b78b87db20b66da6b9cdc32737fbbf4fea0c35b23fc", + "sha256:1bb1e77a9a311346294621be905ea8a2c30d3ad371fc15bb72e98bfcfae532df", + "sha256:1fd78b911aea9cec3b7e1e2622c8018d51c0d2bbcf8faaf53c2497eb114911c1", + "sha256:20d1a2a76bb4eb00e4d36b9699f9b7aba93271c9c29220ad4c6a9581a0320235", + "sha256:21b154aba06df42e4b96fc915512ab39595105f6c483991287021ed95776d934", + "sha256:2c2e58e45fe53fab81f85474e5d4d226eeab0f27b45aa062856c89389da2f0d9", + "sha256:2c3b2803e730dc2797a017335827e9da6da0e84c745ce0f552e66400abdfb9a1", + "sha256:3146b8e16fa60427e03884301bf8209221f5761ac754ee6b267642a2fd354c48", + "sha256:344e714bd0fe921fc72d97404ebbdbf9127bac0ca1ff66d7b79efc143cf7c0c4", + "sha256:387065e420aed3c71b61af7e82c7b6bc1c592f7e3c7a66e9f78dd178699da4fe", + "sha256:3f04becd4fcda03c0160d0da9c8f0c246bc78f2f7af0feea1ec0930e7c93fa4a", + "sha256:4a42e1eff0ca9a7cb7dc9ecda41dfc7cbc17cb1d02117214be0561bd1134772b", + "sha256:4ea748802cc0de4de92ef8244dd84ffd793bd2e7be784cd8394d557a3c751e21", + "sha256:55416d7385774285b6e2a5feca0af9652f7f444a4fa3d29d8ab052fafef9d00d", + "sha256:5d0391fb4cfc171ce40437f67eb050a340fdbd0f9f49d6353a387f1b7f9dd4fa", + "sha256:63cdeaac4ae85a179a8d6bc09b77b564c096250d759eed343a89d91bce8b6367", + "sha256:72fcae5bcac3333a4cf3b8f34eec99cea1187acd55af723bcbd559adfdcb5535", + "sha256:7c4ed4e9f3b123aa403ab424430b426a1992e6f4c8fd3cb56ea520446e04d152", + "sha256:83957d349838a636e768251c7e9979e899a569794b44c3728eaebd11d848e58e", + "sha256:87ecc7c9a1a9f912e306997ffee020297ccb5ea388421fe62a2a02747e4d5539", + "sha256:8f69770f5ca1994cb32c38965e95f57504d3aea96b6c024624fdd5bb1aa494a1", + "sha256:8f6c930fd70d91ddee53194e93029e3ef2aabe26725aa3c2753df057e296b925", + "sha256:965ee3e782c7892befc25575fa171b521d33798132692df428a09efacaffe8d0", + "sha256:974bc90d6f6c1e59ceb1516ab00cf1cdfbb2e555795d49fa9571d611f449bcb2", + "sha256:981b4df72c93e3bc04478153df516d385317628bd9c10be699c93c26ddcca8ab", + "sha256:aa784405f0c640940595fa0f14064d8e84aff0b0f762fa18393e2760a2cf5841", + "sha256:ae7863a1d8db6a014b6f2ff9c1582ab1aad55a6d25bac19710a8df68921b6e30", + "sha256:aeae2aa38395b18106e552833f2a50c27ea0000122bde421c31d11ed7e6f9c91", + "sha256:b2317d5ed777bf5a033e83d4f1389fd4ef045763141d8f10eb09a7035cee774c", + "sha256:be19931a8dcbe6ab464f3339966856996b12a00f9fe53f346ab3be872d03e257", + "sha256:be9824c1c874b73b96288c6d3de793bf7f3a597770205068c6163ea1f326e8b9", + "sha256:c0045f8f23a5fb30b2eb3b8a83664d8dc4fb58faddf8155d7109166adb9f2040", + "sha256:c86bd45d1659b1ae3d0ba1909326b03598affbc9ed71520e0ff8c31a993ad911", + "sha256:ca0f34363e2634deffd390a0fef1aa99168ae9ed2af01af4a1f5865e362f8623", + "sha256:d298c2815fa4891edd9abe5ad6e6cb4207104c7dd9fd13aea3fdebf6f9b91259", + "sha256:d2a3a6146fe9319926e1d477842ca2a63fe99af5ae690b1f5c11e6af074a6b5c", + "sha256:dfd393094cd82ceb9b40df4c77976015a314b267d498268a076e940fe7be6b79", + "sha256:e58c0d41d336569d63d1b113bd573db8363bc4146f39444125b7f8060e4e04f5", + "sha256:ea3f5bc91d7d457da7d48c7a732beaf79d0c8131df3ab278e6bba6297e23c6c4", + "sha256:ea53151d87c52e98133eb8ac78f1206498c015849662ca8dc246255265d9c3c4", + "sha256:eb0edc3ce9760d2f21637766c3aa04822030e7451981ce569a1b3456b7053f22", + "sha256:f649dd53833b495c3ebd04d6eec58479454a1784987af8afb77540d6c1767abd", + "sha256:f760073fcf8f3d6933178d67754f4f2d4e924e321f4bb0dcef0424ca0215eba1", + "sha256:fa546d66639d69aa967bf08156eb8c9d0cd6f6de84be9e8c9819f52ad499c910", + "sha256:fd214917cabdd6f673a29d708574e9fbdb892cb77eb426d0eae3490d95ca7859", + "sha256:fff5aaa6becf2c6a1699ae6a39e2e6fb0672c2d42eca8eb0cafa91cf2e9bd312" ], "index": "pypi", - "version": "==5.8.0" + "version": "==7.2.3" + }, + "pyinstaller": { + "hashes": [ + "sha256:247b99c52dc3cf69eba905da30dbca0a8ea309e1058cab44658ac838d9b8f2f0", + "sha256:2d16641a495593d174504263b038a6d3d46b3b15a381ccb216cf6cce67723512", + "sha256:333b4ffda38d9c0a561c38429dd9848d37aa78f3b8ea8a6f2b2e69a60d523c02", + "sha256:6afc7aa4885ffd3e6121a8cf2138830099f874c18cb5869bed8c1a42db82d060", + "sha256:6ecc464bf56919bf2d6bff275f38d85ff08ae747b8ead3a0c26cf85573b3c723", + "sha256:7a1db833bb0302b66ae3ae337fbd5487699658ce869ca4d538b5359b8179e83a", + "sha256:85e39e36d03355423636907a26a9bfa06fdc93cb1086441b19d2d0ca448479fa", + "sha256:915a502802c751bafd92d568ac57468ec6cdf252b8308aa9a167bbc2c565ad2d", + "sha256:9e9a38f41f8280c8e29b294716992852281b41fbe64ba330ebab671efe27b26d", + "sha256:bb7de35cd209a0a0358aec761a273ae951d2161c03728f15d9a640d06a88e472", + "sha256:df97aaf1103a1c485aa3c9947792a86675e370f5ce9b436b4a84e34a4180c8d2", + "sha256:f677fbc151db1eb00ada94e86ed128e7b359cbd6bf3f6ea815afdde687692d46" + ], + "index": "pypi", + "version": "==5.10.1" }, "pyinstaller-hooks-contrib": { "hashes": [ - "sha256:29d052eb73e0ab8f137f11df8e73d464c1c6d4c3044d9dc8df2af44639d8bfbf", - "sha256:bd578781cd6a33ef713584bf3726f7cd60a3e656ec08a6cc7971e39990808cc0" + "sha256:7fb856a81fd06a717188a3175caa77e902035cc067b00b583c6409c62497b23f", + "sha256:e02c5f0ee3d4f5814588c2128caf5036c058ba764aaf24d957bb5311ad8690ad" ], "markers": "python_version >= '3.7'", - "version": "==2023.0" + "version": "==2023.2" }, "setuptools": { "hashes": [ - "sha256:e5fd0a713141a4a105412233c63dc4e17ba0090c8e8334594ac790ec97792330", - "sha256:f106dee1b506dee5102cc3f3e9e68137bbad6d47b616be7991714b0c62204251" + "sha256:6f0839fbdb7e3cfef1fc38d7954f5c1c26bf4eebb155a55c9bf8faf997b9fb67", + "sha256:bb16732e8eb928922eabaa022f881ae2b7cdcfaf9993ef1f5e841a96d32b8e0c" ], "markers": "python_version >= '3.7'", - "version": "==67.4.0" + "version": "==67.7.1" } } } diff --git a/diceplayer/__main__.py b/diceplayer/__main__.py index c461bb8..c4dd5db 100644 --- a/diceplayer/__main__.py +++ b/diceplayer/__main__.py @@ -1,3 +1,4 @@ +from diceplayer.shared.external.dice import Dice from diceplayer.player import Player from pathlib import Path @@ -9,7 +10,8 @@ import sys __VERSION = "v0.0.1" -if __name__ == "__main__": + +def main(): """ Read and store the arguments passed to the program and set the usage and help messages @@ -63,3 +65,6 @@ if __name__ == "__main__": player = Player(args.infile) player.start() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/diceplayer/shared/config/dice_dto.py b/diceplayer/shared/config/dice_dto.py index 832e290..935665f 100644 --- a/diceplayer/shared/config/dice_dto.py +++ b/diceplayer/shared/config/dice_dto.py @@ -24,27 +24,27 @@ class DiceDTO(Dataclass): def __post_init__(self): - if self.ljname is None: + if not isinstance(self.ljname, str): raise ValueError( "Error: 'ljname' keyword not specified in config file" ) - if self.outname is None: + if not isinstance(self.outname, str): raise ValueError( "Error: 'outname' keyword not specified in config file" ) - if self.dens is None: + if not isinstance(self.dens, float): raise ValueError( "Error: 'dens' keyword not specified in config file" ) - if self.nmol == 0: + if not isinstance(self.nmol, list): raise ValueError( "Error: 'nmol' keyword not defined appropriately in config file" ) - if self.nstep == 0: + if not isinstance(self.nstep, list): raise ValueError( "Error: 'nstep' keyword not defined appropriately in config file" ) diff --git a/diceplayer/shared/config/step_dto.py b/diceplayer/shared/config/step_dto.py new file mode 100644 index 0000000..2a1145f --- /dev/null +++ b/diceplayer/shared/config/step_dto.py @@ -0,0 +1,18 @@ +from diceplayer.shared.environment.molecule import Molecule + +from dataclasses import dataclass +from typing import List + + +@dataclass +class StepDTO: + nprocs: int = None + ncores: int = None + altsteps: int = None + switchcyc: int = None + opt: str = None + nmol: List[int] = None + molecule: List[Molecule] = None + + charges: List[float] = None + position: List[float] = None \ No newline at end of file diff --git a/diceplayer/shared/environment/molecule.py b/diceplayer/shared/environment/molecule.py index 8e88d41..390f3fc 100644 --- a/diceplayer/shared/environment/molecule.py +++ b/diceplayer/shared/environment/molecule.py @@ -79,14 +79,13 @@ class Molecule: """ Updated positions based on the center of mass of the molecule """ - - self.center_of_mass() - for atom in self.atom: atom.rx -= self.com[0] atom.ry -= self.com[1] atom.rz -= self.com[2] + self.center_of_mass() + def charges_and_dipole(self) -> List[float]: """ Calculates the charges and dipole of the molecule atoms @@ -119,16 +118,15 @@ class Molecule: distances = [] dim = len(self.atom) - for atom1 in self.atom: - if atom1.na != ghost_number: - for atom2 in self.atom: - if atom2.na != ghost_number: - dx = atom1.rx - atom2.rx - dy = atom1.ry - atom2.ry - dz = atom1.rz - atom2.rz - distances.append(math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)) + for index1, atom1 in enumerate(self.atom): + for index2, atom2 in enumerate(self.atom): + if index1 != index2: + dx = atom1.rx - atom2.rx + dy = atom1.ry - atom2.ry + dz = atom1.rz - atom2.rz + distances.append(math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)) - return np.array(distances).reshape(dim, dim) + return np.array(distances).reshape(dim, dim-1) def inertia_tensor(self) -> NDArray[Shape["3, 3"], Float]: """ @@ -156,40 +154,6 @@ class Molecule: return np.array([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]]) - def axes(self) -> NDArray[Shape["3, 3"], Float]: - """ - Calculates the axes of the molecule - - Returns: - NDArray[Shape["3, 3"], Float]: Returns the axes of molecule - """ - - eixos = np.zeros(3) - if len(self.atom) == 2: - - position1 = np.array([self.atom[0].rx, self.atom[0].ry, self.atom[0].rz]) - position2 = np.array([self.atom[1].rx, self.atom[1].ry, self.atom[1].rz]) - eixos = position2 - position1 - eixos /= linalg.norm(eixos) - - elif len(self.atom) > 2: - - position1 = np.array([self.atom[0].rx, self.atom[0].ry, self.atom[0].rz]) - position2 = np.array([self.atom[1].rx, self.atom[1].ry, self.atom[1].rz]) - position3 = np.array([self.atom[2].rx, self.atom[2].ry, self.atom[2].rz]) - v1 = position2 - position1 - v2 = position3 - position1 - v3 = np.cross(v1, v2) - v2 = np.cross(v1, v3) - v1 /= linalg.norm(v1) - v2 /= linalg.norm(v2) - v3 /= linalg.norm(v3) - eixos = np.array( - [[v1[0], v1[1], v1[2]], [v2[0], v2[1], v2[2]], [v3[0], v3[1], v3[2]]] - ) - - return eixos - def principal_axes(self) -> Tuple[np.ndarray, np.ndarray]: """ Calculates the principal axes of the molecule @@ -201,7 +165,7 @@ class Molecule: try: evals, evecs = linalg.eigh(self.inertia_tensor()) - except: + except ValueError: raise RuntimeError("Error: diagonalization of inertia tensor did not converge") return evals, evecs @@ -221,38 +185,38 @@ class Molecule: return position - def updateCharges(self, charges: List[float]) -> None: + def update_charges(self, charges: List[float]) -> None: for i, atom in enumerate(self.atom): atom.chg = charges[i] - def update_hessian( - self, - step: np.ndarray, - cur_gradient: np.ndarray, - old_gradient: np.ndarray, - hessian: np.ndarray, - ) -> np.ndarray: - """ - Updates the Hessian of the molecule based on the current hessian, the current gradient and the previous gradient - - Args: - step (np.ndarray): step value of the iteration - cur_gradient (np.ndarray): current gradient - old_gradient (np.ndarray): previous gradient - hessian (np.ndarray): current hessian - - Returns: - np.ndarray: updated hessian of the molecule - """ - - dif_gradient = cur_gradient - old_gradient - - mat1 = 1 / np.dot(dif_gradient, step) * np.matmul(dif_gradient.T, dif_gradient) - mat2 = 1 / np.dot(step, np.matmul(hessian, step.T).T) - mat2 *= np.matmul(np.matmul(hessian, step.T), np.matmul(step, hessian)) - - return hessian + mat1 - mat2 + # @staticmethod + # def update_hessian( + # step: np.ndarray, + # cur_gradient: np.ndarray, + # old_gradient: np.ndarray, + # hessian: np.ndarray, + # ) -> np.ndarray: + # """ + # Updates the Hessian of the molecule based on the current hessian, the current gradient and the previous gradient + # + # Args: + # step (np.ndarray): step value of the iteration + # cur_gradient (np.ndarray): current gradient + # old_gradient (np.ndarray): previous gradient + # hessian (np.ndarray): current hessian + # + # Returns: + # np.ndarray: updated hessian of the molecule + # """ + # + # dif_gradient = cur_gradient - old_gradient + # + # mat1 = 1 / np.dot(dif_gradient, step) * np.matmul(dif_gradient.T, dif_gradient) + # mat2 = 1 / np.dot(step, np.matmul(hessian, step.T).T) + # mat2 *= np.matmul(np.matmul(hessian, step.T), np.matmul(step, hessian)) + # + # return hessian + mat1 - mat2 def sizes_of_molecule(self) -> List[float]: """ @@ -267,10 +231,9 @@ class Molecule: z_list = [] for atom in self.atom: - if atom.na != ghost_number: - x_list.append(atom.rx) - y_list.append(atom.ry) - z_list.append(atom.rz) + x_list.append(atom.rx) + y_list.append(atom.ry) + z_list.append(atom.rz) x_max = max(x_list) x_min = min(x_list) diff --git a/diceplayer/shared/environment/system.py b/diceplayer/shared/environment/system.py index 2b1389e..b34449b 100644 --- a/diceplayer/shared/environment/system.py +++ b/diceplayer/shared/environment/system.py @@ -1,64 +1,47 @@ -import math -from copy import deepcopy -from typing import List, Tuple, TextIO - -import numpy as np -from numpy import linalg - from diceplayer.shared.environment.molecule import Molecule -from diceplayer.shared.utils.misc import BOHR2ANG from diceplayer.shared.utils.ptable import atomsymb +from diceplayer.shared.utils.misc import BOHR2ANG + +from typing import List, Tuple, TextIO +from copy import deepcopy +from numpy import linalg +import numpy as np +import math class System: """ - System class declaration. This class is used throughout the DicePlayer program to represent the system containing the molecules. + System class declaration. This class is used throughout the DicePlayer program to represent the system containing the molecules. - Atributes: - molecule (List[Molecule]): List of molecules of the system - nmols (List[int]): List of number of molecules in the system - """ + Atributes: + molecule (List[Molecule]): List of molecules of the system + nmols (List[int]): List of number of molecules in the system + """ def __init__(self) -> None: """ - Initializes a empty system object that will be populated afterwards - """ + Initializes a empty system object that will be populated afterwards + """ self.molecule: List[Molecule] = [] self.nmols: List[int] = [] def add_type(self, nmols: int, m: Molecule) -> None: """ - Adds a new molecule type to the system + Adds a new molecule type to the system - Args: - nmols (int): Number of molecules of the new type in the system - m (Molecule): The instance of the new type of molecule - """ + Args: + nmols (int): Number of molecules of the new type in the system + m (Molecule): The instance of the new type of molecule + """ + if isinstance(m, Molecule) is False: + raise TypeError("Error: molecule is not a Molecule instance") self.molecule.append(m) + + if isinstance(nmols, int) is False: + raise TypeError("Error: nmols is not an integer") self.nmols.append(nmols) - def center_of_mass_distance(self, a: int, b: int) -> float: - """ - Calculates the distance between the center of mass of two molecules - - Args: - a (Molecule): First Molecule Instance - b (Molecule): Second Molecule Instance - - Returns: - float: module of the distance between the two center of masses - """ - - com1 = self.molecule[a].center_of_mass() - com2 = self.molecule[b].center_of_mass() - dx = com1[0] - com2[0] - dy = com1[1] - com2[1] - dz = com1[2] - com2[2] - distance = math.sqrt(dx**2 + dy**2 + dz**2) - - return distance - def rmsd_fit(self, p_index: int, r_index: int) -> Tuple[float, Molecule]: projecting_mol = self.molecule[p_index] @@ -118,9 +101,9 @@ class System: rmsd = 0 for i in range(dim): rmsd += ( - (x[i, 0] - y[i, 0]) ** 2 - + (x[i, 1] - y[i, 1]) ** 2 - + (x[i, 2] - y[i, 2]) ** 2 + (x[i, 0] - y[i, 0]) ** 2 + + (x[i, 1] - y[i, 1]) ** 2 + + (x[i, 2] - y[i, 2]) ** 2 ) rmsd = math.sqrt(rmsd / dim) @@ -135,13 +118,71 @@ class System: return rmsd, projected_mol + # def center_of_mass_distance(self, a: int, b: int) -> float: + # """ + # Calculates the distance between the center of mass of two molecules + # + # Args: + # a (Molecule): First Molecule Instance + # b (Molecule): Second Molecule Instance + # + # Returns: + # float: module of the distance between the two center of masses + # """ + # + # com1 = self.molecule[a].center_of_mass() + # com2 = self.molecule[b].center_of_mass() + # dx = com1[0] - com2[0] + # dy = com1[1] - com2[1] + # dz = com1[2] - com2[2] + # distance = math.sqrt(dx**2 + dy**2 + dz**2) + # + # return distance + + # def nearest_image( + # self, + # index_r: int, + # index_m: int, + # lx: float, + # ly: float, + # lz: float, + # criterium=None, + # ) -> Tuple[float, Molecule]: + # + # if criterium in None: + # criterium = "com" + # + # if criterium != "com" and criterium != "min": + # raise RuntimeError("Error in value passed to function nearest_image") + # + # min_dist = 1e20 + # + # for i in range(-1, 2): + # for j in range(-1, 2): + # for k in range(-1, 2): + # + # tr_vector = [i * lx, j * ly, k * lz] + # self.add_molecule(self.molecule[index_m].translate(tr_vector)) + # + # if criterium == "com": + # dist = self.center_of_mass_distance(index_r, -1) + # else: + # dist = self.minimum_distance(index_r, -1) + # + # if dist < min_dist: + # min_dist = dist + # nearestmol = deepcopy(self.molecule[-1]) + # + # self.molecule.pop(-1) + # + # return min_dist, nearestmol def update_molecule(self, position: np.ndarray, fh: TextIO) -> None: """Updates the position of the molecule in the Output file - Args: - position (np.ndarray): numpy position vector - fh (TextIO): Output file - """ + Args: + position (np.ndarray): numpy position vector + fh (TextIO): Output file + """ position_in_ang = (position * BOHR2ANG).tolist() self.add_type(self.nmols[0], deepcopy(self.molecule[0])) @@ -158,52 +199,15 @@ class System: fh.write("\nProjected new conformation of reference molecule with RMSD fit\n") fh.write("RMSD = {:>8.5f} Angstrom\n".format(rmsd)) - def nearest_image( - self, - index_r: int, - index_m: int, - lx: float, - ly: float, - lz: float, - criterium=None, - ) -> Tuple[float, Molecule]: - - if criterium in None: - criterium = "com" - - if criterium != "com" and criterium != "min": - raise RuntimeError("Error in value passed to function nearest_image") - - min_dist = 1e20 - - for i in range(-1, 2): - for j in range(-1, 2): - for k in range(-1, 2): - - tr_vector = [i * lx, j * ly, k * lz] - self.add_molecule(self.molecule[index_m].translate(tr_vector)) - - if criterium == "com": - dist = self.center_of_mass_distance(index_r, -1) - else: - dist = self.minimum_distance(index_r, -1) - - if dist < min_dist: - min_dist = dist - nearestmol = deepcopy(self.molecule[-1]) - - self.molecule.pop(-1) - - return min_dist, nearestmol def print_geom(self, cycle: int, fh: TextIO) -> None: """ - Print the geometry of the molecule in the Output file + Print the geometry of the molecule in the Output file - Args: - cycle (int): Number of the cycle - fh (TextIO): Output file - """ + Args: + cycle (int): Number of the cycle + fh (TextIO): Output file + """ fh.write("Cycle # {}\n".format(cycle)) fh.write("Number of site: {}\n".format(len(self.molecule[0].atom))) @@ -217,12 +221,12 @@ class System: def printChargesAndDipole(self, cycle: int, fh: TextIO) -> None: """ - Print the charges and dipole of the molecule in the Output file + Print the charges and dipole of the molecule in the Output file - Args: - cycle (int): Number of the cycle - fh (TextIO): Output file - """ + Args: + cycle (int): Number of the cycle + fh (TextIO): Output file + """ fh.write("Cycle # {}\n".format(cycle)) fh.write("Number of site: {}\n".format(len(self.molecule[0].atom))) diff --git a/diceplayer/shared/external/__external.py b/diceplayer/shared/external/__external.py index 775ec34..030d4bf 100644 --- a/diceplayer/shared/external/__external.py +++ b/diceplayer/shared/external/__external.py @@ -18,7 +18,7 @@ class External(ABC): pass @abstractmethod - def start(self): + def start(self, cycle: int): pass @abstractmethod diff --git a/diceplayer/shared/external/__init__.py b/diceplayer/shared/external/__init__.py index e69de29..25c400b 100644 --- a/diceplayer/shared/external/__init__.py +++ b/diceplayer/shared/external/__init__.py @@ -0,0 +1 @@ +from .__external import External diff --git a/diceplayer/shared/external/dice.py b/diceplayer/shared/external/dice.py index 676209c..464d6eb 100644 --- a/diceplayer/shared/external/dice.py +++ b/diceplayer/shared/external/dice.py @@ -1,9 +1,13 @@ -from diceplayer.shared.utils.dataclass_protocol import Dataclass -from diceplayer.shared.external.__external import External from diceplayer.shared.config.dice_dto import DiceDTO +from diceplayer.shared.external import External + +from multiprocessing import Process, connection +from setproctitle import setproctitle +import sys class Dice(External): + __slots__ = ['config', 'step'] def __init__(self, data: dict): self.config: DiceDTO = self.set_config(data) @@ -12,11 +16,38 @@ class Dice(External): def set_config(data: dict) -> DiceDTO: return DiceDTO.from_dict(data) - def configure(self): - pass + def configure(self, step: any): + self.step = step - def start(self): - pass + def start(self, cycle: int): + procs = [ + Process(target=self._simulation_process, args=(cycle, proc)) + for proc in range(1, self.config.ncores+1) + ] + + for proc in procs: + proc.start() + + connection.wait(p.sentinel for p in procs) def reset(self): - pass + del self.step + + def _simulation_process(self, cycle: int, proc: int): + setproctitle(f"diceplayer-step{cycle:0d}-p{proc:0d}") + + try: + self._make_proc_dir(cycle, proc) + self._make_dice_inputs(cycle, proc) + self._run_dice(cycle, proc) + except Exception as err: + sys.exit(err) + + def _make_proc_dir(self, cycle, proc): + raise NotImplementedError + + def _make_dice_inputs(self, cycle, proc): + raise NotImplementedError + + def _run_dice(self, cycle, proc): + raise NotImplementedError diff --git a/diceplayer/shared/external/gaussian.py b/diceplayer/shared/external/gaussian.py index 325cc23..c96ebd4 100644 --- a/diceplayer/shared/external/gaussian.py +++ b/diceplayer/shared/external/gaussian.py @@ -1,6 +1,5 @@ -from diceplayer.shared.utils.dataclass_protocol import Dataclass from diceplayer.shared.config.gaussian_dto import GaussianDTO -from diceplayer.shared.external.__external import External +from diceplayer.shared.external import External class Gaussian(External): diff --git a/tests/shared/config/__init__.py b/tests/shared/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/shared/config/test_dice_dto.py b/tests/shared/config/test_dice_dto.py new file mode 100644 index 0000000..cd6a8c5 --- /dev/null +++ b/tests/shared/config/test_dice_dto.py @@ -0,0 +1,89 @@ +from diceplayer.shared.config.dice_dto import DiceDTO + +import unittest + + +class TestDiceDto(unittest.TestCase): + def test_class_instantiation(self): + dice_dto = DiceDTO( + ljname='test', + outname='test', + ncores=1, + dens=1.0, + nmol=[1], + nstep=[1], + ) + + self.assertIsInstance(dice_dto, DiceDTO) + + def test_validate_jname(self): + with self.assertRaises(ValueError) as ex: + DiceDTO( + ljname=None, + outname='test', + ncores=1, + dens=1.0, + nmol=[1], + nstep=[1], + ) + self.assertEqual(ex.exception, "Error: 'ljname' keyword not specified in config file") + + def test_validate_outname(self): + with self.assertRaises(ValueError) as ex: + DiceDTO( + ljname='test', + outname=None, + ncores=1, + dens=1.0, + nmol=[1], + nstep=[1], + ) + self.assertEqual(ex.exception, "Error: 'outname' keyword not specified in config file") + + def test_validate_dens(self): + with self.assertRaises(ValueError) as ex: + DiceDTO( + ljname='test', + outname='test', + ncores=1, + dens=None, + nmol=[1], + nstep=[1], + ) + self.assertEqual(ex.exception, "Error: 'dens' keyword not specified in config file") + + def test_validate_nmol(self): + with self.assertRaises(ValueError) as ex: + DiceDTO( + ljname='test', + outname='test', + ncores=1, + dens=1.0, + nmol=0, + nstep=[1], + ) + self.assertEqual(ex.exception, "Error: 'nmol' keyword not defined appropriately in config file") + + def test_validate_nstep(self): + with self.assertRaises(ValueError) as ex: + DiceDTO( + ljname='test', + outname='test', + ncores=1, + dens=1.0, + nmol=[1], + nstep=0, + ) + self.assertEqual(ex.exception, "Error: 'nstep' keyword not defined appropriately in config file") + + def test_from_dict(self): + dice_dto = DiceDTO.from_dict({ + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1], + }) + + self.assertIsInstance(dice_dto, DiceDTO) \ No newline at end of file diff --git a/tests/shared/environment/molecule.py b/tests/shared/environment/molecule.py deleted file mode 100644 index 7928af1..0000000 --- a/tests/shared/environment/molecule.py +++ /dev/null @@ -1,61 +0,0 @@ -from diceplayer.shared.environment.molecule import Molecule -from diceplayer.shared.environment.atom import Atom - -import numpy.testing as npt -import unittest - - -class TestMolecule(unittest.TestCase): - def test_class_instantiation(self): - mol = Molecule('test') - - self.assertIsInstance(mol, Molecule) - - def test_add_atom(self): - mol = Molecule('test') - - mol.add_atom( - Atom( - lbl=1, - na=1, - rx=1.0, - ry=1.0, - rz=1.0, - chg=1.0, - eps=1.0, - sig=1.0, - ) - ) - - self.assertEqual(len(mol.atom), 1) - npt.assert_equal(mol.com, [1., 1., 1.]) - - def test_center_of_mass(self): - mol = Molecule('test') - - mol.add_atom( - Atom( - lbl=1, - na=1, - rx=1.0, - ry=1.0, - rz=1.0, - chg=1.0, - eps=1.0, - sig=1.0, - ) - ) - mol.add_atom( - Atom( - lbl=1, - na=1, - rx=0.0, - ry=0.0, - rz=0.0, - chg=1.0, - eps=1.0, - sig=1.0, - ) - ) - - npt.assert_equal(mol.com, [.5, .5, .5]) diff --git a/tests/shared/environment/atom.py b/tests/shared/environment/test_atom.py similarity index 100% rename from tests/shared/environment/atom.py rename to tests/shared/environment/test_atom.py diff --git a/tests/shared/environment/test_molecule.py b/tests/shared/environment/test_molecule.py new file mode 100644 index 0000000..1eb8c2e --- /dev/null +++ b/tests/shared/environment/test_molecule.py @@ -0,0 +1,147 @@ +from diceplayer.shared.environment.molecule import Molecule +from diceplayer.shared.environment.atom import Atom + +import numpy.testing as npt +import unittest + + +class TestMolecule(unittest.TestCase): + def test_class_instantiation(self): + mol = Molecule('test') + + self.assertIsInstance(mol, Molecule) + + def test_add_atom(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0) + ) + + self.assertEqual(len(mol.atom), 1) + npt.assert_equal(mol.com, [1., 1., 1.]) + + def test_center_of_mass(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0) + ) + mol.add_atom( + Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0) + ) + + npt.assert_equal(mol.com, [.5, .5, .5]) + + def test_center_of_mass_to_origin(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0) + ) + + mol.center_of_mass_to_origin() + + npt.assert_equal(mol.com, [0, 0, 0]) + + def test_charges_and_dipole(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0) + ) + + actual_charge_dipole_array = mol.charges_and_dipole() + + expected_charge_dipole_array = [1.0, 0.0, 0.0, 0.0, 0.0] + + npt.assert_equal( + actual_charge_dipole_array, + expected_charge_dipole_array + ) + + def test_distances_between_atoms(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0) + ) + mol.add_atom( + Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0) + ) + + expected_distance_between_atoms = [[1.73205081], [1.73205081]] + actual_distance_between_atoms = mol.distances_between_atoms() + + npt.assert_almost_equal( + expected_distance_between_atoms, + actual_distance_between_atoms + ) + + def test_inertia_tensor(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0) + ) + mol.add_atom( + Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0) + ) + + expected_inertia_tensor = [[1.00790, -0.50395, -0.50395], + [-0.50395, 1.0079, -0.50395], + [-0.50395, -0.50395, 1.0079]] + + actual_inertia_tensor = mol.inertia_tensor() + + npt.assert_equal( + expected_inertia_tensor, + actual_inertia_tensor + ) + + def test_principal_axes(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0) + ) + + expected_evals, expected_evecs = [0., 0., 0.], [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]] + + evals, evecs = mol.principal_axes() + + npt.assert_equal(expected_evals, evals) + npt.assert_equal(expected_evecs, evecs) + + def test_read_position(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0) + ) + + expected_position = mol.read_position() + + actual_position = mol.read_position() + + npt.assert_equal( + expected_position, + actual_position + ) + + def test_update_charges(self): + mol = Molecule('test') + + mol.add_atom( + Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0) + ) + + expected_charges = [2.] + mol.update_charges(expected_charges) + + actual_charges = list(map(lambda a: a.chg, mol.atom)) + + npt.assert_equal( + expected_charges, + actual_charges + ) \ No newline at end of file diff --git a/tests/shared/environment/test_system.py b/tests/shared/environment/test_system.py new file mode 100644 index 0000000..cab9cbe --- /dev/null +++ b/tests/shared/environment/test_system.py @@ -0,0 +1,50 @@ +from diceplayer.shared.environment.atom import Atom +from diceplayer.shared.environment.molecule import Molecule +from diceplayer.shared.environment.system import System + +import unittest + + +class TestSystem(unittest.TestCase): + def test_class_instantiation(self): + system = System() + + self.assertIsInstance(system, System) + + def test_add_type(self): + system = System() + system.add_type(0, Molecule('test')) + + self.assertIsInstance(system.molecule, list) + self.assertIsInstance(system.nmols, list) + + with self.assertRaises(TypeError) as ex: + system.add_type(0, 'test') + self.assertEqual(ex.exception, 'Error: molecule is not a Molecule instance') + + with self.assertRaises(TypeError) as ex: + system.add_type('test', Molecule('test')) + self.assertEqual(ex.exception, 'Error: nmols is not an integer') + + def test_center_of_mass_distance(self): + system = System() + + a = Molecule('test') + a.add_atom( + Atom(lbl=0, na=1, rx=0, ry=0, rz=0, chg=0, eps=0, sig=0) + ) + system.add_type(1, a) + + b = Molecule('test') + b.add_atom( + Atom(lbl=0, na=1, rx=0, ry=0, rz=0, chg=0, eps=0, sig=0) + ) + system.add_type(1, b) + + self.assertIsInstance(system.center_of_mass_distance(0, 1), float) + + + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/shared/external/__init__.py b/tests/shared/external/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/shared/external/test_dice.py b/tests/shared/external/test_dice.py new file mode 100644 index 0000000..8c51f95 --- /dev/null +++ b/tests/shared/external/test_dice.py @@ -0,0 +1,116 @@ +from diceplayer.shared.external.dice import Dice + +from unittest import mock +import unittest + + +class TestDice(unittest.TestCase): + def test_class_instantiation(self): + dice = Dice( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1], + } + ) + + self.assertIsInstance(dice, Dice) + + def test_configure(self): + dice = Dice( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1], + } + ) + + self.assertFalse(hasattr(dice, 'step')) + + dice.configure('test') + + self.assertTrue(hasattr(dice, 'step')) + + def test_reset(self): + dice = Dice( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1], + } + ) + + dice.configure('test') + + self.assertTrue(hasattr(dice, 'step')) + + dice.reset() + + self.assertFalse(hasattr(dice, 'step')) + + @mock.patch('diceplayer.shared.external.dice.connection') + @mock.patch('diceplayer.shared.external.dice.Process') + def test_start(self, mock_process, mock_connection): + dice = Dice( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1], + } + ) + dice.start(1) + + self.assertTrue(mock_process.called) + self.assertTrue(mock_connection.wait.called) + + def test_simulation_process_raises_exception(self): + dice = Dice( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1], + } + ) + + with self.assertRaises(SystemExit): + dice._simulation_process(1, 1) + + @mock.patch('diceplayer.shared.external.dice.Dice._make_proc_dir') + @mock.patch('diceplayer.shared.external.dice.Dice._make_dice_inputs') + @mock.patch('diceplayer.shared.external.dice.Dice._run_dice') + def test_simulation_process(self, mock_run_dice, mock_make_dice_inputs, mock_make_proc_dir): + dice = Dice( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1], + } + ) + + dice._simulation_process(1, 1) + + self.assertTrue(dice._make_proc_dir.called) + self.assertTrue(dice._make_dice_inputs.called) + self.assertTrue(dice._run_dice.called) + + +if __name__ == '__main__': + unittest.main()