From 5643322b6bc95de2aa82f36c0d310a013726591c Mon Sep 17 00:00:00 2001 From: bc118 Date: Tue, 29 Jun 2021 15:37:52 -0400 Subject: [PATCH 01/14] This adds checking if the RB_to_OPLS dihedral conversion is exact when the standard OPLS format does not have a constant (i.e., f0/2). If the RB is not directly convertible to the standard OPLS, then it prints a list. Then, it compares to the printed list with a user input list of known dihedrals that are not analytically perfect, and if it is not in the list, it throws an error. This will also help catch other random user-entered errors for the dihedrals in the MoSDeF standard FF XMLs. --- foyer/tests/test_forcefield_parameters.py | 459 ++++++++++++++++ foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 505 ++++++++++++++++++ 2 files changed, 964 insertions(+) create mode 100644 foyer/utils/check_xml_dihedrals_RB_to_OPLS.py diff --git a/foyer/tests/test_forcefield_parameters.py b/foyer/tests/test_forcefield_parameters.py index 495b4491..a0138c23 100644 --- a/foyer/tests/test_forcefield_parameters.py +++ b/foyer/tests/test_forcefield_parameters.py @@ -4,7 +4,9 @@ from foyer import Forcefield, forcefields from foyer.exceptions import MissingForceError, MissingParametersError from foyer.forcefield import get_available_forcefield_loaders +from foyer.forcefields.forcefields import get_forcefield from foyer.tests.utils import get_fn +from foyer.utils.check_xml_dihedrals_RB_to_OPLS import test_xml_dihedral_rb_to_opls, _test_xml_dihedrals, run_xml_test @pytest.mark.skipif( @@ -291,3 +293,460 @@ def test_missing_scaling_factors(self): assert ff.lj14scale with pytest.raises(AttributeError): assert ff.coulomb14scale + + + # check xml files for dihedral conversions from RB to OPLS + # Note: the non-exact conversions are most likely due to the OPLS equation + # not using the f0/2 term (i.e., f0 = 0), regardless if the analytical + # conversion provides a non-zero f0 term. + def test_oplsaa_xml_dihedral_rb_to_opls(self): + oplsaa_non_exact_conversion_list = [ + [ + ['CT', 'OS', 'P', 'OS'], + [1.046, 3.138, 10.0416, -4.184, 0.0, 0.0], + [20.0832, 0.0, -10.0416, 2.092, -0.0], + 10.0416, + False + ] + ] + + pass_oplsaa_rb_to_opls = test_xml_dihedral_rb_to_opls("oplsaa", + 'all_oplsaa_rb_to_opls_errors.txt', + oplsaa_non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4) + assert pass_oplsaa_rb_to_opls is True + + def test_trappeua_xml_dihedral_rb_to_opls(self): + trappeua_non_exact_conversion_list = [ + [ + ['CH3', 'CH2', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH2', 'CH', 'CH2'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH2', 'CH', 'CH'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH2', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH2', 'CH2', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH2', 'CH2', 'CH', 'CH2'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH2', 'CH2', 'CH', 'CH'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH2', 'CH2', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH', 'CH2', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH', 'CH2', 'CH', 'CH2'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH', 'CH2', 'CH', 'CH'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH', 'CH2', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['C', 'CH2', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['C', 'CH2', 'CH', 'CH2'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['C', 'CH2', 'CH', 'CH'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['C', 'CH2', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH', 'CH', 'CH2'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH', 'CH', 'CH'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH2', 'CH', 'CH', 'CH2'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH2', 'CH', 'CH', 'CH'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH2', 'CH', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH', 'CH', 'CH', 'CH'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH', 'CH', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['C', 'CH', 'CH', 'C'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], + + [ + ['CH3', 'CH', 'O', 'H'], + [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], + [3.59118, 3.281385, 0.52315, -2.892105, -0.0], + 1.79559, + False + ], + + [ + ['CH2', 'CH', 'O', 'H'], + [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], + [3.59118, 3.281385, 0.52315, -2.892105, -0.0], + 1.79559, + False + ], + + [ + ['CH', 'CH', 'O', 'H'], + [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], + [3.59118, 3.281385, 0.52315, -2.892105, -0.0], + 1.79559, + False + ], + + [ + ['C', 'CH', 'O', 'H'], + [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], + [3.59118, 3.281385, 0.52315, -2.892105, -0.0], + 1.79559, + False + ], + ] + + pass_trappeua_rb_to_opls = test_xml_dihedral_rb_to_opls("trappe-ua", + 'all_trappeua_rb_to_opls_errors.txt', + trappeua_non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4) + assert pass_trappeua_rb_to_opls is True + + def test_xml_dihedrals_function_error_not_float(self): + xml_file_directory_and_filename = 'test_ff' + output_file_name = 'test_filename.txt' + error_tolerance_rb_to_opls = 's' + + with pytest.raises( + TypeError, + match=f"The error_tolerance_rb_to_opls variable must be a float, " + f"is type {type(error_tolerance_rb_to_opls)}.", + ): + _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) + + # error_tolerance_rb_to_opls range is 1e-1 to 1e-10 + def test_xml_dihedrals_function_error_too_high(self): + xml_file_directory_and_filename = 'test_ff' + output_file_name = 'test_filename.txt' + error_tolerance_rb_to_opls = 1.01e-1 + + with pytest.raises( + ValueError, + match= f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " + f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", + ): + _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) + + def test_xml_dihedrals_function_error_too_low(self): + xml_file_directory_and_filename = 'test_ff' + output_file_name = 'test_filename.txt' + error_tolerance_rb_to_opls = 0.999e-10 + + with pytest.raises( + ValueError, + match= f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " + f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", + ): + _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) + + def test_xml_dihedrals_function_ff_xml_not_good_extension(self): + xml_file_directory_and_filename = 'test_ff.out' + output_file_name = 'test_filename.txt' + error_tolerance_rb_to_opls = 1e-4 + + with pytest.raises( + ValueError, + match= r"Please make sure you are entering the correct " + "foyer FF name and not a path to a FF file. " + "If you are entering a path to a FF file, " + "please use the forcefield_files variable with the " + "proper XML extension \(.xml\).", + ): + _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) + + def test_xml_dihedrals_function_user_defined_ff_xml_not_exist(self): + xml_file_directory_and_filename = 'test_ff.xml' + output_file_name = 'test_filename.txt' + error_tolerance_rb_to_opls = 1e-4 + + with pytest.raises( + ValueError, + match= "Please make sure you are entering the correct foyer FF path, " + "including the FF file name.xml " + "If you are using the pre-build FF files in foyer, " + "only use the string name without any extension." + ): + _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) + + def test_xml_dihedrals_function_foyer_std_ff_xml_not_exist(self): + xml_file_directory_and_filename = 'test_ff' + output_file_name = 'test_filename.txt' + error_tolerance_rb_to_opls = 1e-4 + + with pytest.raises( + ValueError, + match= "Please make sure you are entering the correct foyer FF name " + "without the .xml extension." + ): + _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) + + + def test_run_xml_test_input_not_dict(self): + xml_and_error_file_dict = ['test_ff.xml', 'test_filename.txt'] + + with pytest.raises( + TypeError, + match= f"The xml_and_error_file_dict variable must be a dict, " + f"is type {type(xml_and_error_file_dict)}." + ): + run_xml_test(xml_and_error_file_dict) + + def test_run_xml_test_input_key_not_str(self): + xml_key = 1 + xml_value = 'test_filename.txt' + xml_and_error_file_dict = {xml_key: xml_value} + + with pytest.raises( + TypeError, + match= f"The xml_and_error_file_dict keys are not all strings, " + f"and has the type {type(xml_key)}." + ): + run_xml_test(xml_and_error_file_dict) + + def test_run_xml_test_input_value_not_str(self): + xml_key = 'test_ff.xml' + xml_value = 1 + xml_and_error_file_dict = {xml_key: xml_value} + + with pytest.raises( + TypeError, + match = f"The xml_and_error_file_dict values are not all strings, " + f"and has the type {type(xml_value)}." + ): + + run_xml_test(xml_and_error_file_dict) + + def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): + xml_filename = 'test_ff.xml' + error_filename = 'test_filename.txt' + non_exact_conversion_list = 'str' + + with pytest.raises( + TypeError, + match = f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation." + ): + test_xml_dihedral_rb_to_opls(xml_filename, + error_filename, + non_exact_conversion_list) + + def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): + xml_filename = 'test_ff.xml' + error_filename = 'test_filename.txt' + non_exact_conversion_list = [ + [ + ['CH3', 'CH2', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + 'False' + ] + ] + + with pytest.raises( + TypeError, + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation." + ): + test_xml_dihedral_rb_to_opls(xml_filename, + error_filename, + non_exact_conversion_list) + + def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_4_length(self): + xml_filename = 'test_ff.xml' + error_filename = 'test_filename.txt' + non_exact_conversion_list = [ + [ + ['CH3', 'CH2', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734 + ] + ] + + with pytest.raises( + TypeError, + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation." + ): + test_xml_dihedral_rb_to_opls(xml_filename, + error_filename, + non_exact_conversion_list) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py new file mode 100644 index 00000000..e4347722 --- /dev/null +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -0,0 +1,505 @@ +import numpy as np +import os +import xml.etree.ElementTree as ET +from foyer.forcefields import forcefields +from foyer.forcefields.forcefields import get_forcefield +from mbuild.utils.conversion import RB_to_OPLS +from warnings import warn + + +def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, + error_tolerance_rb_to_opls=1e-4, + ): + r"""In in file function for the conversion of the Ryckaert-Bellemans (RB) type to OPLS + type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput + files with the errors for the single xml file provided, containing conversion errors + and their info. + + .. math:: + RB_{torsions} &= c_0 + c_1*cos(psi) + c_2*cos(psi)^2 + c_3*cos(psi)^3 + \\ + &= c_4*cos(psi)^4 + c_5*cos(5*psi)^5 + + .. math:: + Exact_OPLS_torsions &= \frac{f_0}{2} + \frac{f_1}{2}*(1+cos(t)) + \frac{f_2}{2}*(1-cos(2*t)) + \\ + &= \frac{f_3}{2}*(1+cos(3*t)) + \frac{f_4}{2}*(1-cos(4*t)) + + .. math:: + Standard_ OPLS_torsions &= \frac{f_1}{2}*(1+cos(t)) + \frac{f_2}{2}*(1-cos(2*t)) + \\ + &= \frac{f_3}{2}*(1+cos(3*t)) + \frac{f_4}{2}*(1-cos(4*t)) + + where :math:`psi = t - pi = t - 180 degrees` + + xml_file_directory_and_filename : str + A string with the RB torsions xml file path, file names, and + extensions (i.e., '.xml'), or the just the name of the standard foyer force + without the extension. + output_file_name : str + The string which will be the names error output files for the xml_file_list. + error_tolerance_rb_to_opls : float (range 1e-1 to 1e-10), default=1e-4 + The acceptable absolute tolerance between the RB to OPLS conversion. + This function converts it to an absolute tolerance. + The working range is 1e-1 to 1e-10, since the calculated values are rounded + down to the working range plus 2 decimal places (1e-1 -> 3 decimal places and + 2e-10 -> 11 decimal places). + + Returns + ------- + Generates text (.txt) file containing the dihedral errors in the mBuild + RB_to_OPLS function conversion process for the specified force field + xml file. These files contain the following columns and have the specified meaning: + + dihedral_atoms_or_beads: list of strings [str, ..., str] + A list of the foyer atoms or beads types in the dihedral, in order. + rb_constants_c0_c1_c2_c3_c4_c5: list of floats [float, ..., float] + A list of the RB-torsion values for the dihedral, which includes + c0, c1, c2, c3, c4, c5. + opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals : list of floats [float, ..., float] + A list of the OPLS dihedral values for the dihedral, which includes + f0, f1, f2, f3, f4. + NOTE: The standard OPLS dihedral function does not include + the f0 constant. However, the f0 is included as it is required for the exact analytical conversion. + Otherwise, for the standard OPLS dihedral, the f0 term must be 0 + for the solution to be analytically correct. + max_abs_delta_rb_to_opls_calc : float + The maximum difference between the RB and standard OPLS dihedral functions + for 100 equally spaced points over the span of 2 Pi. + exact_rb_to_opls_possible : bool + This states whether the conversion is possible from RB to OPLS, since + OPLS does not include the f0 constant. Therefore, if f0/2 is equal to zero + within the set tolerance (error_tolerance_rb_to_opls), it will be True. + Otherwise, it will be False. + """ + if not isinstance(error_tolerance_rb_to_opls, float): + raise TypeError( + f"The error_tolerance_rb_to_opls variable must be a float, " + f"is type {type(error_tolerance_rb_to_opls)}." + ) + else: + if not 1e-10 <= error_tolerance_rb_to_opls <= 1e-1: + print(f"printed The error_tolerance_rb_to_opls variable is not 1e-10 <= " + f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1.") + raise ValueError( + f"The error_tolerance_rb_to_opls variable is not 1e-10 <= " + f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1." + ) + + if ( + os.path.splitext(xml_file_directory_and_filename)[1] == ".xml" + ): + foyer_packaged_ff = False + + elif ( + os.path.splitext(xml_file_directory_and_filename)[1] == "" + ): + foyer_packaged_ff = True + + else: + print_error_message = ( + r"Please make sure you are entering the correct " + "foyer FF name and not a path to a FF file. " + "If you are entering a path to a FF file, " + "please use the forcefield_files variable with the " + "proper XML extension (.xml)." + ) + raise ValueError(print_error_message) + + if foyer_packaged_ff is False: + ff_full_path_and_filename = xml_file_directory_and_filename + try: + ff_xml = ET.parse(ff_full_path_and_filename) + except: + print_error_message = ( + "Please make sure you are entering the correct foyer FF path, " + "including the FF file name.xml " + "If you are using the pre-build FF files in foyer, " + "only use the string name without any extension." + ) + raise ValueError(print_error_message) + + elif foyer_packaged_ff is True: + ff_full_path_and_filename = ( + forcefields.get_ff_path()[0] + + "/xml/" + + xml_file_directory_and_filename + + ".xml" + ) + try: + ff_xml = ET.parse(ff_full_path_and_filename) + except: + print_error_message = ( + "Please make sure you are entering the correct foyer FF name " + "without the .xml extension." + ) + raise ValueError(print_error_message) + + with open(output_file_name, "w") as data_opls_rb: + ff_root = ff_xml.getroot() + rb_torsionForce_proper_root = ff_root.findall('RBTorsionForce/Proper') + + count_no = 0 + for child in rb_torsionForce_proper_root: + count_no += 1 + class1_iter = child.attrib['class1'] + class2_iter = child.attrib['class2'] + class3_iter = child.attrib['class3'] + class4_iter = child.attrib['class4'] + + class_list_iter = [class1_iter, + class2_iter, + class3_iter, + class4_iter] + + c0_iter = float(child.attrib['c0']) + c1_iter = float(child.attrib['c1']) + c2_iter = float(child.attrib['c2']) + c3_iter = float(child.attrib['c3']) + c4_iter = float(child.attrib['c4']) + c5_iter = float(child.attrib['c5']) + + cX_list_iter = [c0_iter, + c1_iter, + c2_iter, + c3_iter, + c4_iter, + c5_iter] + + fX_list_iter = RB_to_OPLS(c0_iter, + c1_iter, + c2_iter, + c3_iter, + c4_iter, + c5_iter, + error_tolerance = error_tolerance_rb_to_opls, + error_if_outside_tolerance=False + ) + + f0_iter = fX_list_iter[0] + f1_iter = fX_list_iter[1] + f2_iter = fX_list_iter[2] + f3_iter = fX_list_iter[3] + f4_iter = fX_list_iter[4] + + fx_list_iter_6_decimals = [np.round(fX_list_iter[0], decimals=6), + np.round(fX_list_iter[1], decimals=6), + np.round(fX_list_iter[2], decimals=6), + np.round(fX_list_iter[3], decimals=6), + np.round(fX_list_iter[4], decimals=6) + ] + + # generate a 100 list for the functions over range of 2 * Pi + no_points = 100 + radian_range = 2 * np.pi + rb_torsion_list = [] + rb_to_opls_list = [] + rad_list = [] + abs_differene_opls_rb_list = [] + for rad_fract in range(0, no_points): + rad_iter = rad_fract * radian_range / no_points + rad_list.append(rad_iter) + + rad_psi_iter = rad_iter + np.pi + + # RB torsions + rb_torsion_iter = c0_iter \ + + c1_iter * (np.cos(rad_psi_iter)) ** 1 \ + + c2_iter * (np.cos(rad_psi_iter)) ** 2 \ + + c3_iter * (np.cos(rad_psi_iter)) ** 3 \ + + c4_iter * (np.cos(rad_psi_iter)) ** 4 \ + + c5_iter * (np.cos(rad_psi_iter)) ** 5 + rb_torsion_list.append(rb_torsion_iter) + + + # RB torsions converted to standard OPLS version via alternate function; + # the f0 is removed here. It is not a part of the standard OPLS dihedral + # since it has to be zero for the standard OPLS dihedral to be + # an exact conversion. + # If f0/2 is added to the rb_to_opls_iter variable, the RB to OPLS conversion + # should be analytically correct within the machine precision tolerance. + rb_to_opls_iter = (1 / 2) * ( + + f1_iter * (1 + np.cos(rad_iter)) + + f2_iter * (1 - np.cos(2 * rad_iter)) + + f3_iter * (1 + np.cos(3 * rad_iter)) + + f4_iter * (1 - np.cos(4 * rad_iter)) + ) + rb_to_opls_list.append(rb_to_opls_iter) + + abs_differene_opls_rb_iter = abs(rb_to_opls_iter - rb_torsion_iter) + abs_differene_opls_rb_list.append(abs_differene_opls_rb_iter) + + if count_no == 1: + title_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}\n" \ + "".format("dihedral_atoms_or_beads", + "rb_constants_c0_c1_c2_c3_c4_c5", + "opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals", + "max_abs_delta_rb_to_opls_calc", + "exact_rb_to_opls_possible" + ) + + data_opls_rb.write(title_to_output) + + # Is the iteration RB_to_opls conversion possible + if bool(np.isclose(c5_iter, 0, atol= error_tolerance_rb_to_opls, rtol=0)) is True \ + and bool(np.isclose(f0_iter, 0, atol= error_tolerance_rb_to_opls, rtol=0)) is True: + rb_to_opls_convertable_iter = True + + else: + rb_to_opls_convertable_iter = False + + max_abs_diff_new_opls_rb = np.round(np.max(abs_differene_opls_rb_list), + decimals=int(np.log10((1/error_tolerance_rb_to_opls))+2) + ) + if max_abs_diff_new_opls_rb > error_tolerance_rb_to_opls: + warning_to_output = "WARNING: The {} atoms with the {} RB constants not an exact conversion " \ + "for the RB_to_OPLS conversion!. " \ + "Max diff = {}, f0 = {}.\n" \ + "".format(class_list_iter, + cX_list_iter, + max_abs_diff_new_opls_rb, + f0_iter + ) + text_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}" \ + "\n".format(str(class_list_iter), + str(cX_list_iter), + str(fx_list_iter_6_decimals), + str(max_abs_diff_new_opls_rb), + str(rb_to_opls_convertable_iter) + ) + + warn(warning_to_output) + data_opls_rb.write(text_to_output) + + data_opls_rb.close() + + +def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): + r"""In in file function for the conversion of the Ryckaert-Bellemans (RB) type to OPLS + type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput + files with the errors for each xml file provided, containing conversion errors + and their info. + + .. math:: + RB_{torsions} &= c_0 + c_1*cos(psi) + c_2*cos(psi)^2 + c_3*cos(psi)^3 + \\ + &= c_4*cos(psi)^4 + c_5*cos(5*psi)^5 + + .. math:: + Exact_OPLS_torsions &= \frac{f_0}{2} + \frac{f_1}{2}*(1+cos(t)) + \frac{f_2}{2}*(1-cos(2*t)) + \\ + &= \frac{f_3}{2}*(1+cos(3*t)) + \frac{f_4}{2}*(1-cos(4*t)) + + .. math:: + Standard_ OPLS_torsions &= \frac{f_1}{2}*(1+cos(t)) + \frac{f_2}{2}*(1-cos(2*t)) + \\ + &= \frac{f_3}{2}*(1+cos(3*t)) + \frac{f_4}{2}*(1-cos(4*t)) + + where :math:`psi = t - pi = t - 180 degrees` + + + Parameters + ---------- + xml_and_error_file_dict : dict, {str, str} and {xml_file: output_file_name, ...} + A dictionary that contains the key as the xml_file and the value as the output_file_name. + The xml_file (key) is the RB torsions xml file paths with file names and + extensions (i.e., '.xml'), or the just the name of the standard foyer force + without the extension. + The output_file_name (value) is the names error output files that will be + generated. + error_tolerance_rb_to_opls : float (range 1e-1 to 1e-10), default=1e-4 + The acceptable absolute tolerance between the RB to OPLS conversion. + This function converts it to an absolute tolerance. + The working range is 1e-1 to 1e-10, since the calculated values are rounded + down to the working range plus 2 decimal places (1e-1 -> 3 decimal places and + 2e-10 -> 11 decimal places). + + Returns + ------- + Generates text (.txt) files containing the dihedral errors in the + RB to OPLS function conversion process. The number of files generated + is equal to the number of foyer force field xml files provided. + These files contain the following columns and have the specified meaning. + + dihedral_atoms_or_beads: list of strings [str, ..., str] + A list of the foyer atoms or beads types in the dihedral, in order. + rb_constants_c0_c1_c2_c3_c4_c5: list of floats [float, ..., float] + A list of the RB-torsion values for the dihedral, which includes + c0, c1, c2, c3, c4, c5. + opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals : list of floats [float, ..., float] + A list of the OPLS dihedral values for the dihedral, which includes + f0, f1, f2, f3, f4. + NOTE: The standard OPLS dihedral function does not include + the f0 constant. However, the f0 is included as it is required for the exact analytical conversion. + Otherwise, for the standard OPLS dihedral, the f0 term must be 0 + for the solution to be analytically correct. + max_abs_delta_rb_to_opls_calc : float + The maximum difference between the RB and standard OPLS dihedral functions + for 100 equally spaced points over the span of 2 Pi. + exact_rb_to_opls_possible : bool + This states whether the conversion is possible from RB to OPLS, where + OPLS does not include the f0 constant. Therefore, if f0/2 is equal to zero + within the set tolerance (error_tolerance_rb_to_opls), it will be True. + Otherwise, it will be False. + """ + if not isinstance(xml_and_error_file_dict, dict): + raise TypeError( + f"The xml_and_error_file_dict variable must be a dict, " + f"is type {type(xml_and_error_file_dict)}." + ) + + xml_file_list = list(xml_and_error_file_dict.keys()) + output_file_name_list = [] + for val_iter in xml_file_list: + output_file_name_list.append(xml_and_error_file_dict[val_iter]) + + for i_iter in range(0, len(xml_file_list)): + if not isinstance(xml_file_list[i_iter], str): + raise TypeError(f"The xml_and_error_file_dict keys are not all strings, " + f"and has the type {type(xml_file_list[i_iter])}." + ) + + if not isinstance(output_file_name_list[i_iter], str): + raise TypeError(f"The xml_and_error_file_dict values are not all strings, " + f"and has the type {type(output_file_name_list[i_iter])}." + ) + + _test_xml_dihedrals(xml_file_list[i_iter], + output_file_name_list[i_iter], + error_tolerance_rb_to_opls + ) + + +def test_xml_dihedral_rb_to_opls(xml_filename, + error_filename, + non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4): + """ + Test that all the xml dihedrals either pass the RB to standard OPLS conversion + via the mBuild RB_to_OPLS function, or they are in the know list that is not + a direct analytical conversion. + + + .. math:: + RB_{torsions} &= c_0 + c_1*cos(psi) + c_2*cos(psi)^2 + c_3*cos(psi)^3 + \\ + &= c_4*cos(psi)^4 + c_5*cos(5*psi)^5 + + .. math:: + Exact_OPLS_torsions &= \frac{f_0}{2} + \frac{f_1}{2}*(1+cos(t)) + \frac{f_2}{2}*(1-cos(2*t)) + \\ + &= \frac{f_3}{2}*(1+cos(3*t)) + \frac{f_4}{2}*(1-cos(4*t)) + + .. math:: + Standard_ OPLS_torsions &= \frac{f_1}{2}*(1+cos(t)) + \frac{f_2}{2}*(1-cos(2*t)) + \\ + &= \frac{f_3}{2}*(1+cos(3*t)) + \frac{f_4}{2}*(1-cos(4*t)) + + where :math:`psi = t - pi = t - 180 degrees` + + Parameters + ---------- + xml_filename : str + The RB torsions RB torsions xml file paths with file names and + extensions (i.e., '.xml'), or just the name of the standard foyer force + without the extension. + error_filename : str + The error_filename is the name error output file that will be read and + compared to the non_exact_conversion_list data. + non_exact_conversion_list : list + This is a list of the known dihedrals that do not analyitically pass + the RB to standard OPLS conversion via the mBuild RB_to_OPLS function. + + This is a list of the following five data in the list, in order (i.e., [(1), (2), (3), (4), (5)]). + --- (1) dihedral_atoms_or_beads: list of strings [str, ..., str] + A list of the foyer atoms or beads types in the dihedral, in order. + --- (2) rb_constants_c0_c1_c2_c3_c4_c5: list of floats [float, ..., float] + A list of the RB-torsion values for the dihedral, which includes + c0, c1, c2, c3, c4, c5. + --- (3) opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals : list of floats [float, ..., float] + A list of the OPLS dihedral values for the dihedral, which includes + f0, f1, f2, f3, f4. + NOTE: The standard OPLS dihedral function does not include + the f0 constant. However, the f0 is included as it is required for the exact analytical conversion. + Otherwise, for the standard OPLS dihedral, the f0 term must be 0 + for the solution to be analytically correct. + --- (4) max_abs_delta_rb_to_opls_calc : float + The maximum difference between the RB and standard OPLS dihedral functions + for 100 equally spaced points over the span of 2 Pi. + --- (5) exact_rb_to_opls_possible : bool + This states whether the conversion is possible from RB to OPLS, where + OPLS does not include the f0 constant. Therefore, if f0/2 is equal to zero + within the set tolerance (error_tolerance_rb_to_opls), it will be True. + Otherwise, it will be False. + Example list : [ + [ + ['CH3', 'CH2', 'CH', 'CH3'], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + False + ], ...., + [ + ['CH2', 'CH2', 'CH', 'CH2'], + [0.0, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + True + ] + ] + error_tolerance_rb_to_opls : float (range 1e-1 to 1e-10), default=1e-4 + The acceptable absolute tolerance between the RB to OPLS conversion. + This function converts it to an absolute tolerance. + The working range is 1e-1 to 1e-10, since the calculated values are rounded + down to the working range plus 2 decimal places (1e-1 -> 3 decimal places and + 2e-10 -> 11 decimal places). + + Returns + ------- + passed_test : bool + True if all the RB to standard OPLS dihedral conversions via the mBuild RB_to_OPLS + function are analytical correct or are in the existing list known + non-analytical correct conversions list (i.e., non_exact_conversion_list). + False if at least one of the RB to standard OPLS dihedral conversions via the mBuild RB_to_OPLS + function are not analytical correct or are not in the existing list known + non-analytical correct conversions list (i.e., non_exact_conversion_list). + """ + passed_test = True + + non_exact_conversion_list_error_txt = f"The non_exact_conversion_list variables is not formated correctly. "\ + f"Please see the test_xml_dihedral_rb_to_opls function for the " \ + f"proper format infomation." + if isinstance(non_exact_conversion_list, list): + len_non_exact_conversion_list = len(non_exact_conversion_list) + for error_list_i in non_exact_conversion_list: + if len(error_list_i) == 5: + if not isinstance( error_list_i[0], list) \ + or not isinstance( error_list_i[1], list) \ + or not isinstance( error_list_i[2], list) \ + or not isinstance( error_list_i[3], float) \ + or not isinstance( error_list_i[4], bool): + raise TypeError(non_exact_conversion_list_error_txt) + else: + raise TypeError(non_exact_conversion_list_error_txt) + else: + raise TypeError(non_exact_conversion_list_error_txt) + + # put the non_exact_conversion_list in the same string format with the same + # spacing as the printed/read files. + correct_file_strings_list = [] + for j_iter in non_exact_conversion_list: + correct_file_strings_list.append( + '{:35s}{:60s}{:60s}{:35s}{:35s}\n'.format(str(j_iter[0]), + str(j_iter[1]), + str(j_iter[2]), + str(j_iter[3]), + str(j_iter[4]) + ) + ) + + run_xml_test({xml_filename: error_filename}, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls + ) + + with open(error_filename, "r") as fp: + out_gomc = fp.readlines() + for i_iter, line in enumerate(out_gomc): + split_line = line.split() + + # check the values for the dihedrals + if i_iter != 0 and len(split_line) > 0 \ + and line not in correct_file_strings_list: + passed_test = False + + return passed_test \ No newline at end of file From f72d4d5f160df5b4c3b2de69db8c66bc45bc48ca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Jun 2021 19:39:29 +0000 Subject: [PATCH 02/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- foyer/tests/test_forcefield_parameters.py | 385 +++++++++--------- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 319 ++++++++------- 2 files changed, 367 insertions(+), 337 deletions(-) diff --git a/foyer/tests/test_forcefield_parameters.py b/foyer/tests/test_forcefield_parameters.py index a0138c23..9d6ed282 100644 --- a/foyer/tests/test_forcefield_parameters.py +++ b/foyer/tests/test_forcefield_parameters.py @@ -6,7 +6,11 @@ from foyer.forcefield import get_available_forcefield_loaders from foyer.forcefields.forcefields import get_forcefield from foyer.tests.utils import get_fn -from foyer.utils.check_xml_dihedrals_RB_to_OPLS import test_xml_dihedral_rb_to_opls, _test_xml_dihedrals, run_xml_test +from foyer.utils.check_xml_dihedrals_RB_to_OPLS import ( + _test_xml_dihedrals, + run_xml_test, + test_xml_dihedral_rb_to_opls, +) @pytest.mark.skipif( @@ -294,7 +298,6 @@ def test_missing_scaling_factors(self): with pytest.raises(AttributeError): assert ff.coulomb14scale - # check xml files for dihedral conversions from RB to OPLS # Note: the non-exact conversions are most likely due to the OPLS equation # not using the f0/2 term (i.e., f0 = 0), regardless if the analytical @@ -302,451 +305,437 @@ def test_missing_scaling_factors(self): def test_oplsaa_xml_dihedral_rb_to_opls(self): oplsaa_non_exact_conversion_list = [ [ - ['CT', 'OS', 'P', 'OS'], + ["CT", "OS", "P", "OS"], [1.046, 3.138, 10.0416, -4.184, 0.0, 0.0], [20.0832, 0.0, -10.0416, 2.092, -0.0], 10.0416, - False + False, ] ] - pass_oplsaa_rb_to_opls = test_xml_dihedral_rb_to_opls("oplsaa", - 'all_oplsaa_rb_to_opls_errors.txt', - oplsaa_non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4) + pass_oplsaa_rb_to_opls = test_xml_dihedral_rb_to_opls( + "oplsaa", + "all_oplsaa_rb_to_opls_errors.txt", + oplsaa_non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, + ) assert pass_oplsaa_rb_to_opls is True def test_trappeua_xml_dihedral_rb_to_opls(self): trappeua_non_exact_conversion_list = [ [ - ['CH3', 'CH2', 'CH', 'CH3'], + ["CH3", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH2', 'CH', 'CH2'], + ["CH3", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH2', 'CH', 'CH'], + ["CH3", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH2', 'CH', 'C'], + ["CH3", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'CH3'], + ["CH2", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'CH2'], + ["CH2", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'CH'], + ["CH2", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'C'], + ["CH2", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'CH3'], + ["CH", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'CH2'], + ["CH", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'CH'], + ["CH", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'C'], + ["CH", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'CH3'], + ["C", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'CH2'], + ["C", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'CH'], + ["C", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'C'], + ["C", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'CH3'], + ["CH3", "CH", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'CH2'], + ["CH3", "CH", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'CH'], + ["CH3", "CH", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'C'], + ["CH3", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH', 'CH', 'CH2'], + ["CH2", "CH", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH', 'CH', 'CH'], + ["CH2", "CH", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH', 'CH', 'C'], + ["CH2", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH', 'CH', 'CH'], + ["CH", "CH", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH', 'CH', 'C'], + ["CH", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH', 'CH', 'C'], + ["C", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'O', 'H'], + ["CH3", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], - [ - ['CH2', 'CH', 'O', 'H'], + ["CH2", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], - [ - ['CH', 'CH', 'O', 'H'], + ["CH", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], - [ - ['C', 'CH', 'O', 'H'], + ["C", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], ] - pass_trappeua_rb_to_opls = test_xml_dihedral_rb_to_opls("trappe-ua", - 'all_trappeua_rb_to_opls_errors.txt', - trappeua_non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4) + pass_trappeua_rb_to_opls = test_xml_dihedral_rb_to_opls( + "trappe-ua", + "all_trappeua_rb_to_opls_errors.txt", + trappeua_non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, + ) assert pass_trappeua_rb_to_opls is True def test_xml_dihedrals_function_error_not_float(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' - error_tolerance_rb_to_opls = 's' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" + error_tolerance_rb_to_opls = "s" with pytest.raises( TypeError, match=f"The error_tolerance_rb_to_opls variable must be a float, " - f"is type {type(error_tolerance_rb_to_opls)}.", + f"is type {type(error_tolerance_rb_to_opls)}.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) # error_tolerance_rb_to_opls range is 1e-1 to 1e-10 def test_xml_dihedrals_function_error_too_high(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1.01e-1 with pytest.raises( ValueError, - match= f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " - f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", + match=f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " + f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_error_too_low(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 0.999e-10 with pytest.raises( ValueError, - match= f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " - f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", + match=f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " + f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_ff_xml_not_good_extension(self): - xml_file_directory_and_filename = 'test_ff.out' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff.out" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1e-4 with pytest.raises( ValueError, - match= r"Please make sure you are entering the correct " - "foyer FF name and not a path to a FF file. " - "If you are entering a path to a FF file, " - "please use the forcefield_files variable with the " - "proper XML extension \(.xml\).", + match=r"Please make sure you are entering the correct " + "foyer FF name and not a path to a FF file. " + "If you are entering a path to a FF file, " + "please use the forcefield_files variable with the " + "proper XML extension \(.xml\).", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_user_defined_ff_xml_not_exist(self): - xml_file_directory_and_filename = 'test_ff.xml' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff.xml" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1e-4 with pytest.raises( ValueError, - match= "Please make sure you are entering the correct foyer FF path, " - "including the FF file name.xml " - "If you are using the pre-build FF files in foyer, " - "only use the string name without any extension." + match="Please make sure you are entering the correct foyer FF path, " + "including the FF file name.xml " + "If you are using the pre-build FF files in foyer, " + "only use the string name without any extension.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_foyer_std_ff_xml_not_exist(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1e-4 with pytest.raises( ValueError, - match= "Please make sure you are entering the correct foyer FF name " - "without the .xml extension." + match="Please make sure you are entering the correct foyer FF name " + "without the .xml extension.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) - + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_run_xml_test_input_not_dict(self): - xml_and_error_file_dict = ['test_ff.xml', 'test_filename.txt'] + xml_and_error_file_dict = ["test_ff.xml", "test_filename.txt"] with pytest.raises( TypeError, - match= f"The xml_and_error_file_dict variable must be a dict, " - f"is type {type(xml_and_error_file_dict)}." + match=f"The xml_and_error_file_dict variable must be a dict, " + f"is type {type(xml_and_error_file_dict)}.", ): run_xml_test(xml_and_error_file_dict) def test_run_xml_test_input_key_not_str(self): xml_key = 1 - xml_value = 'test_filename.txt' + xml_value = "test_filename.txt" xml_and_error_file_dict = {xml_key: xml_value} with pytest.raises( TypeError, - match= f"The xml_and_error_file_dict keys are not all strings, " - f"and has the type {type(xml_key)}." + match=f"The xml_and_error_file_dict keys are not all strings, " + f"and has the type {type(xml_key)}.", ): run_xml_test(xml_and_error_file_dict) def test_run_xml_test_input_value_not_str(self): - xml_key = 'test_ff.xml' + xml_key = "test_ff.xml" xml_value = 1 xml_and_error_file_dict = {xml_key: xml_value} with pytest.raises( TypeError, - match = f"The xml_and_error_file_dict values are not all strings, " - f"and has the type {type(xml_value)}." + match=f"The xml_and_error_file_dict values are not all strings, " + f"and has the type {type(xml_value)}.", ): run_xml_test(xml_and_error_file_dict) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): - xml_filename = 'test_ff.xml' - error_filename = 'test_filename.txt' - non_exact_conversion_list = 'str' + xml_filename = "test_ff.xml" + error_filename = "test_filename.txt" + non_exact_conversion_list = "str" with pytest.raises( TypeError, - match = f"The non_exact_conversion_list variables is not formated correctly. " - f"Please see the test_xml_dihedral_rb_to_opls function for the " - f"proper format infomation." + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation.", ): - test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list) + test_xml_dihedral_rb_to_opls( + xml_filename, error_filename, non_exact_conversion_list + ) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): - xml_filename = 'test_ff.xml' - error_filename = 'test_filename.txt' + xml_filename = "test_ff.xml" + error_filename = "test_filename.txt" non_exact_conversion_list = [ - [ - ['CH3', 'CH2', 'CH', 'CH3'], - [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], - [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], - 2.08734, - 'False' - ] + [ + ["CH3", "CH2", "CH", "CH3"], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + "False", + ] ] with pytest.raises( - TypeError, - match=f"The non_exact_conversion_list variables is not formated correctly. " - f"Please see the test_xml_dihedral_rb_to_opls function for the " - f"proper format infomation." + TypeError, + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation.", ): - test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list) + test_xml_dihedral_rb_to_opls( + xml_filename, error_filename, non_exact_conversion_list + ) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_4_length(self): - xml_filename = 'test_ff.xml' - error_filename = 'test_filename.txt' + xml_filename = "test_ff.xml" + error_filename = "test_filename.txt" non_exact_conversion_list = [ - [ - ['CH3', 'CH2', 'CH', 'CH3'], - [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], - [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], - 2.08734 - ] + [ + ["CH3", "CH2", "CH", "CH3"], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + ] ] with pytest.raises( - TypeError, - match=f"The non_exact_conversion_list variables is not formated correctly. " - f"Please see the test_xml_dihedral_rb_to_opls function for the " - f"proper format infomation." + TypeError, + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation.", ): - test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list) + test_xml_dihedral_rb_to_opls( + xml_filename, error_filename, non_exact_conversion_list + ) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index e4347722..9a2900ad 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -1,15 +1,19 @@ -import numpy as np import os import xml.etree.ElementTree as ET +from warnings import warn + +import numpy as np +from mbuild.utils.conversion import RB_to_OPLS + from foyer.forcefields import forcefields from foyer.forcefields.forcefields import get_forcefield -from mbuild.utils.conversion import RB_to_OPLS -from warnings import warn -def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=1e-4, - ): +def _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=1e-4, +): r"""In in file function for the conversion of the Ryckaert-Bellemans (RB) type to OPLS type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput files with the errors for the single xml file provided, containing conversion errors @@ -71,26 +75,24 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, """ if not isinstance(error_tolerance_rb_to_opls, float): raise TypeError( - f"The error_tolerance_rb_to_opls variable must be a float, " - f"is type {type(error_tolerance_rb_to_opls)}." - ) + f"The error_tolerance_rb_to_opls variable must be a float, " + f"is type {type(error_tolerance_rb_to_opls)}." + ) else: if not 1e-10 <= error_tolerance_rb_to_opls <= 1e-1: - print(f"printed The error_tolerance_rb_to_opls variable is not 1e-10 <= " - f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1.") + print( + f"printed The error_tolerance_rb_to_opls variable is not 1e-10 <= " + f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1." + ) raise ValueError( f"The error_tolerance_rb_to_opls variable is not 1e-10 <= " - f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1." + f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1." ) - if ( - os.path.splitext(xml_file_directory_and_filename)[1] == ".xml" - ): + if os.path.splitext(xml_file_directory_and_filename)[1] == ".xml": foyer_packaged_ff = False - elif ( - os.path.splitext(xml_file_directory_and_filename)[1] == "" - ): + elif os.path.splitext(xml_file_directory_and_filename)[1] == "": foyer_packaged_ff = True else: @@ -118,10 +120,10 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, elif foyer_packaged_ff is True: ff_full_path_and_filename = ( - forcefields.get_ff_path()[0] - + "/xml/" - + xml_file_directory_and_filename - + ".xml" + forcefields.get_ff_path()[0] + + "/xml/" + + xml_file_directory_and_filename + + ".xml" ) try: ff_xml = ET.parse(ff_full_path_and_filename) @@ -134,44 +136,49 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, with open(output_file_name, "w") as data_opls_rb: ff_root = ff_xml.getroot() - rb_torsionForce_proper_root = ff_root.findall('RBTorsionForce/Proper') + rb_torsionForce_proper_root = ff_root.findall("RBTorsionForce/Proper") count_no = 0 for child in rb_torsionForce_proper_root: count_no += 1 - class1_iter = child.attrib['class1'] - class2_iter = child.attrib['class2'] - class3_iter = child.attrib['class3'] - class4_iter = child.attrib['class4'] - - class_list_iter = [class1_iter, - class2_iter, - class3_iter, - class4_iter] - - c0_iter = float(child.attrib['c0']) - c1_iter = float(child.attrib['c1']) - c2_iter = float(child.attrib['c2']) - c3_iter = float(child.attrib['c3']) - c4_iter = float(child.attrib['c4']) - c5_iter = float(child.attrib['c5']) - - cX_list_iter = [c0_iter, - c1_iter, - c2_iter, - c3_iter, - c4_iter, - c5_iter] - - fX_list_iter = RB_to_OPLS(c0_iter, - c1_iter, - c2_iter, - c3_iter, - c4_iter, - c5_iter, - error_tolerance = error_tolerance_rb_to_opls, - error_if_outside_tolerance=False - ) + class1_iter = child.attrib["class1"] + class2_iter = child.attrib["class2"] + class3_iter = child.attrib["class3"] + class4_iter = child.attrib["class4"] + + class_list_iter = [ + class1_iter, + class2_iter, + class3_iter, + class4_iter, + ] + + c0_iter = float(child.attrib["c0"]) + c1_iter = float(child.attrib["c1"]) + c2_iter = float(child.attrib["c2"]) + c3_iter = float(child.attrib["c3"]) + c4_iter = float(child.attrib["c4"]) + c5_iter = float(child.attrib["c5"]) + + cX_list_iter = [ + c0_iter, + c1_iter, + c2_iter, + c3_iter, + c4_iter, + c5_iter, + ] + + fX_list_iter = RB_to_OPLS( + c0_iter, + c1_iter, + c2_iter, + c3_iter, + c4_iter, + c5_iter, + error_tolerance=error_tolerance_rb_to_opls, + error_if_outside_tolerance=False, + ) f0_iter = fX_list_iter[0] f1_iter = fX_list_iter[1] @@ -179,12 +186,13 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, f3_iter = fX_list_iter[3] f4_iter = fX_list_iter[4] - fx_list_iter_6_decimals = [np.round(fX_list_iter[0], decimals=6), - np.round(fX_list_iter[1], decimals=6), - np.round(fX_list_iter[2], decimals=6), - np.round(fX_list_iter[3], decimals=6), - np.round(fX_list_iter[4], decimals=6) - ] + fx_list_iter_6_decimals = [ + np.round(fX_list_iter[0], decimals=6), + np.round(fX_list_iter[1], decimals=6), + np.round(fX_list_iter[2], decimals=6), + np.round(fX_list_iter[3], decimals=6), + np.round(fX_list_iter[4], decimals=6), + ] # generate a 100 list for the functions over range of 2 * Pi no_points = 100 @@ -200,15 +208,16 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, rad_psi_iter = rad_iter + np.pi # RB torsions - rb_torsion_iter = c0_iter \ - + c1_iter * (np.cos(rad_psi_iter)) ** 1 \ - + c2_iter * (np.cos(rad_psi_iter)) ** 2 \ - + c3_iter * (np.cos(rad_psi_iter)) ** 3 \ - + c4_iter * (np.cos(rad_psi_iter)) ** 4 \ - + c5_iter * (np.cos(rad_psi_iter)) ** 5 + rb_torsion_iter = ( + c0_iter + + c1_iter * (np.cos(rad_psi_iter)) ** 1 + + c2_iter * (np.cos(rad_psi_iter)) ** 2 + + c3_iter * (np.cos(rad_psi_iter)) ** 3 + + c4_iter * (np.cos(rad_psi_iter)) ** 4 + + c5_iter * (np.cos(rad_psi_iter)) ** 5 + ) rb_torsion_list.append(rb_torsion_iter) - # RB torsions converted to standard OPLS version via alternate function; # the f0 is removed here. It is not a part of the standard OPLS dihedral # since it has to be zero for the standard OPLS dihedral to be @@ -216,54 +225,72 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, # If f0/2 is added to the rb_to_opls_iter variable, the RB to OPLS conversion # should be analytically correct within the machine precision tolerance. rb_to_opls_iter = (1 / 2) * ( - + f1_iter * (1 + np.cos(rad_iter)) - + f2_iter * (1 - np.cos(2 * rad_iter)) - + f3_iter * (1 + np.cos(3 * rad_iter)) - + f4_iter * (1 - np.cos(4 * rad_iter)) - ) + +f1_iter * (1 + np.cos(rad_iter)) + + f2_iter * (1 - np.cos(2 * rad_iter)) + + f3_iter * (1 + np.cos(3 * rad_iter)) + + f4_iter * (1 - np.cos(4 * rad_iter)) + ) rb_to_opls_list.append(rb_to_opls_iter) - abs_differene_opls_rb_iter = abs(rb_to_opls_iter - rb_torsion_iter) + abs_differene_opls_rb_iter = abs( + rb_to_opls_iter - rb_torsion_iter + ) abs_differene_opls_rb_list.append(abs_differene_opls_rb_iter) if count_no == 1: - title_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}\n" \ - "".format("dihedral_atoms_or_beads", - "rb_constants_c0_c1_c2_c3_c4_c5", - "opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals", - "max_abs_delta_rb_to_opls_calc", - "exact_rb_to_opls_possible" - ) + title_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}\n" "".format( + "dihedral_atoms_or_beads", + "rb_constants_c0_c1_c2_c3_c4_c5", + "opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals", + "max_abs_delta_rb_to_opls_calc", + "exact_rb_to_opls_possible", + ) data_opls_rb.write(title_to_output) # Is the iteration RB_to_opls conversion possible - if bool(np.isclose(c5_iter, 0, atol= error_tolerance_rb_to_opls, rtol=0)) is True \ - and bool(np.isclose(f0_iter, 0, atol= error_tolerance_rb_to_opls, rtol=0)) is True: + if ( + bool( + np.isclose( + c5_iter, 0, atol=error_tolerance_rb_to_opls, rtol=0 + ) + ) + is True + and bool( + np.isclose( + f0_iter, 0, atol=error_tolerance_rb_to_opls, rtol=0 + ) + ) + is True + ): rb_to_opls_convertable_iter = True else: rb_to_opls_convertable_iter = False - max_abs_diff_new_opls_rb = np.round(np.max(abs_differene_opls_rb_list), - decimals=int(np.log10((1/error_tolerance_rb_to_opls))+2) - ) + max_abs_diff_new_opls_rb = np.round( + np.max(abs_differene_opls_rb_list), + decimals=int(np.log10((1 / error_tolerance_rb_to_opls)) + 2), + ) if max_abs_diff_new_opls_rb > error_tolerance_rb_to_opls: - warning_to_output = "WARNING: The {} atoms with the {} RB constants not an exact conversion " \ - "for the RB_to_OPLS conversion!. " \ - "Max diff = {}, f0 = {}.\n" \ - "".format(class_list_iter, - cX_list_iter, - max_abs_diff_new_opls_rb, - f0_iter - ) - text_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}" \ - "\n".format(str(class_list_iter), - str(cX_list_iter), - str(fx_list_iter_6_decimals), - str(max_abs_diff_new_opls_rb), - str(rb_to_opls_convertable_iter) - ) + warning_to_output = ( + "WARNING: The {} atoms with the {} RB constants not an exact conversion " + "for the RB_to_OPLS conversion!. " + "Max diff = {}, f0 = {}.\n" + "".format( + class_list_iter, + cX_list_iter, + max_abs_diff_new_opls_rb, + f0_iter, + ) + ) + text_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}" "\n".format( + str(class_list_iter), + str(cX_list_iter), + str(fx_list_iter_6_decimals), + str(max_abs_diff_new_opls_rb), + str(rb_to_opls_convertable_iter), + ) warn(warning_to_output) data_opls_rb.write(text_to_output) @@ -349,25 +376,30 @@ def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): for i_iter in range(0, len(xml_file_list)): if not isinstance(xml_file_list[i_iter], str): - raise TypeError(f"The xml_and_error_file_dict keys are not all strings, " - f"and has the type {type(xml_file_list[i_iter])}." - ) + raise TypeError( + f"The xml_and_error_file_dict keys are not all strings, " + f"and has the type {type(xml_file_list[i_iter])}." + ) if not isinstance(output_file_name_list[i_iter], str): - raise TypeError(f"The xml_and_error_file_dict values are not all strings, " - f"and has the type {type(output_file_name_list[i_iter])}." - ) + raise TypeError( + f"The xml_and_error_file_dict values are not all strings, " + f"and has the type {type(output_file_name_list[i_iter])}." + ) - _test_xml_dihedrals(xml_file_list[i_iter], - output_file_name_list[i_iter], - error_tolerance_rb_to_opls - ) + _test_xml_dihedrals( + xml_file_list[i_iter], + output_file_name_list[i_iter], + error_tolerance_rb_to_opls, + ) -def test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4): +def test_xml_dihedral_rb_to_opls( + xml_filename, + error_filename, + non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, +): """ Test that all the xml dihedrals either pass the RB to standard OPLS conversion via the mBuild RB_to_OPLS function, or they are in the know list that is not @@ -457,40 +489,46 @@ def test_xml_dihedral_rb_to_opls(xml_filename, """ passed_test = True - non_exact_conversion_list_error_txt = f"The non_exact_conversion_list variables is not formated correctly. "\ - f"Please see the test_xml_dihedral_rb_to_opls function for the " \ - f"proper format infomation." + non_exact_conversion_list_error_txt = ( + f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation." + ) if isinstance(non_exact_conversion_list, list): len_non_exact_conversion_list = len(non_exact_conversion_list) for error_list_i in non_exact_conversion_list: if len(error_list_i) == 5: - if not isinstance( error_list_i[0], list) \ - or not isinstance( error_list_i[1], list) \ - or not isinstance( error_list_i[2], list) \ - or not isinstance( error_list_i[3], float) \ - or not isinstance( error_list_i[4], bool): + if ( + not isinstance(error_list_i[0], list) + or not isinstance(error_list_i[1], list) + or not isinstance(error_list_i[2], list) + or not isinstance(error_list_i[3], float) + or not isinstance(error_list_i[4], bool) + ): raise TypeError(non_exact_conversion_list_error_txt) else: raise TypeError(non_exact_conversion_list_error_txt) else: raise TypeError(non_exact_conversion_list_error_txt) - # put the non_exact_conversion_list in the same string format with the same + # put the non_exact_conversion_list in the same string format with the same # spacing as the printed/read files. correct_file_strings_list = [] for j_iter in non_exact_conversion_list: correct_file_strings_list.append( - '{:35s}{:60s}{:60s}{:35s}{:35s}\n'.format(str(j_iter[0]), - str(j_iter[1]), - str(j_iter[2]), - str(j_iter[3]), - str(j_iter[4]) - ) + "{:35s}{:60s}{:60s}{:35s}{:35s}\n".format( + str(j_iter[0]), + str(j_iter[1]), + str(j_iter[2]), + str(j_iter[3]), + str(j_iter[4]), + ) ) - run_xml_test({xml_filename: error_filename}, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls - ) + run_xml_test( + {xml_filename: error_filename}, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) with open(error_filename, "r") as fp: out_gomc = fp.readlines() @@ -498,8 +536,11 @@ def test_xml_dihedral_rb_to_opls(xml_filename, split_line = line.split() # check the values for the dihedrals - if i_iter != 0 and len(split_line) > 0 \ - and line not in correct_file_strings_list: - passed_test = False - - return passed_test \ No newline at end of file + if ( + i_iter != 0 + and len(split_line) > 0 + and line not in correct_file_strings_list + ): + passed_test = False + + return passed_test From b1f71e8401c169d509c8be1ea41313b779df224e Mon Sep 17 00:00:00 2001 From: bc118 Date: Tue, 29 Jun 2021 17:29:17 -0400 Subject: [PATCH 03/14] fixed a few errors for pre-commit --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 126 ++++++++++++++++-- 1 file changed, 118 insertions(+), 8 deletions(-) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index e4347722..900b90c0 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -3,14 +3,119 @@ import xml.etree.ElementTree as ET from foyer.forcefields import forcefields from foyer.forcefields.forcefields import get_forcefield -from mbuild.utils.conversion import RB_to_OPLS +#from mbuild.utils.conversion import RB_to_OPLS from warnings import warn +# **************************************************************************** +# remove this RB_to_OPLS once the new version of foyer imports the new mBuild (START) +# **************************************************************************** +def RB_to_OPLS( + c0, + c1, + c2, + c3, + c4, + c5, + error_tolerance=1e-4, + error_if_outside_tolerance=True, +): + r"""Convert Ryckaert-Bellemans type dihedrals to OPLS type. + + .. math:: + RB_{torsions} &= c_0 + c_1*cos(psi) + c_2*cos(psi)^2 + c_3*cos(psi)^3 + \\ + &= c_4*cos(psi)^4 + c_5*cos(psi)^5 + + .. math:: + OPLS_torsions &= \frac{f_0}{2} + \frac{f_1}{2}*(1+cos(t)) + \frac{f_2}{2}*(1-cos(2*t)) + \\ + &= \frac{f_3}{2}*(1+cos(3*t)) + \frac{f_4}{2}*(1-cos(4*t)) + + where :math:`psi = t - pi = t - 180 degrees` + + Parameters + ---------- + c0, c1, c2, c3, c4, c5 : Ryckaert-Belleman coefficients (in kcal/mol) + error_tolerance : float, default=1e-4 + The acceptable absolute tolerance between the RB to OPLS conversion. + Any value entered is converted to an absolute value. + error_if_outside_tolerance : bool, default=True + This variable determines whether to provide a ValueError if the RB to OPLS + conversion is greater than the error_tolerance term (i.e., error_if_outside_tolerance=True), + or a warning if the RB to OPLS conversion is greater than the error_tolerance term + (i.e., error_if_outside_tolerance=False). + + Returns + ------- + opls_coeffs : np.array, shape=(5,) + Array containing the OPLS dihedrals coeffs f0, f1, f2, f3, and f4 + (in kcal/mol). + + + Notes + ----- + c5 must equal zero, or this conversion is not possible. + + (c0 + c1 + c2 + c3 + c4 + c5) must equal zero, to have the exact + energies represented in the dihedral. + + NOTE: fO IS TYPICALLY NOT IN THE OPLS DIHEDRAL EQUATION AND IS + ONLY USED TO TEST IF THIS FUNCTION CAN BE UTILIZED, AND + DETERMINE THE fO VALUE FOR LATER ENERGY SCALING IN MOLECULAR DYNAMICS + (MD) SIMULATIONS. THIS FUNCTION TESTS IF f0 IS ZERO (f0=0). + + .. warning:: The :math:`\frac{f_{0}}{2}` term is the constant for the OPLS dihedral equation. + If the f0 term is not zero, the dihedral is not an exact conversion; + since this constant does not contribute to the force equation, + this should provide matching results for MD, but the energy for each + dihedral will be shifted by the :math:`\frac{f_{0}}{2}` value. + """ + if not isinstance(error_tolerance, float): + raise TypeError( + f"The error_tolerance variable must be a float, is type {type(error_tolerance)}." + ) + error_tolerance = abs(error_tolerance) + + if not isinstance(error_if_outside_tolerance, bool): + raise TypeError( + f"The text_for_error_tolerance variable must be a bool, is type {type(error_if_outside_tolerance)}." + ) + + if not np.all(np.isclose(c5, 0, atol=1e-10, rtol=0)): + raise ValueError( + "c5 must equal zero, so this conversion is not possible." + ) + + f0 = 2.0 * (c0 + c1 + c2 + c3 + c4 + c5) + if not np.all(np.isclose(f0 / 2, 0, atol=error_tolerance, rtol=0)): + text_for_error_tolerance = ( + "f0 = 2 * ( c0 + c1 + c2 + c3 + c4 + c5 ) is not zero. " + "The f0/2 term is the constant for the OPLS dihedral. " + "Since the f0 term is not zero, the dihedral is not an " + "exact conversion; since this constant does not contribute " + "to the force equation, this should provide matching results " + "for MD, but the energy for each dihedral will be shifted " + "by the f0/2 value." + ) + if error_if_outside_tolerance is True: + raise ValueError(text_for_error_tolerance) + elif error_if_outside_tolerance is False: + warn(text_for_error_tolerance) + + f1 = -2 * c1 - (3 * c3) / 2 + f2 = -c2 - c4 + f3 = -c3 / 2 + f4 = -c4 / 4 + return np.array([f0, f1, f2, f3, f4]) +# **************************************************************************** +# remove this RB_to_OPLS once the new version of foyer imports the new mBuild (END) +# **************************************************************************** def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=1e-4, - ): - r"""In in file function for the conversion of the Ryckaert-Bellemans (RB) type to OPLS + error_tolerance_rb_to_opls=1e-4, + ): + r""" + Tests a single FF, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files + + Checks the conversion of the Ryckaert-Bellemans (RB) type to OPLS type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput files with the errors for the single xml file provided, containing conversion errors and their info. @@ -272,7 +377,10 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): - r"""In in file function for the conversion of the Ryckaert-Bellemans (RB) type to OPLS + r""" + Tests multiple FFs, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files + + Checks the conversion of the Ryckaert-Bellemans (RB) type to OPLS type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput files with the errors for each xml file provided, containing conversion errors and their info. @@ -363,12 +471,14 @@ def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): error_tolerance_rb_to_opls ) - def test_xml_dihedral_rb_to_opls(xml_filename, error_filename, non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4): - """ + error_tolerance_rb_to_opls=1e-4 + ): + r""" + Tests and compare RB to standard OPLS conversion for accuracy and against with known errors + Test that all the xml dihedrals either pass the RB to standard OPLS conversion via the mBuild RB_to_OPLS function, or they are in the know list that is not a direct analytical conversion. From 65498089631bb2e8f75270580083f8fc6a52b73b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Jun 2021 21:32:00 +0000 Subject: [PATCH 04/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- foyer/tests/test_forcefield_parameters.py | 385 +++++++++--------- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 330 ++++++++------- 2 files changed, 375 insertions(+), 340 deletions(-) diff --git a/foyer/tests/test_forcefield_parameters.py b/foyer/tests/test_forcefield_parameters.py index a0138c23..9d6ed282 100644 --- a/foyer/tests/test_forcefield_parameters.py +++ b/foyer/tests/test_forcefield_parameters.py @@ -6,7 +6,11 @@ from foyer.forcefield import get_available_forcefield_loaders from foyer.forcefields.forcefields import get_forcefield from foyer.tests.utils import get_fn -from foyer.utils.check_xml_dihedrals_RB_to_OPLS import test_xml_dihedral_rb_to_opls, _test_xml_dihedrals, run_xml_test +from foyer.utils.check_xml_dihedrals_RB_to_OPLS import ( + _test_xml_dihedrals, + run_xml_test, + test_xml_dihedral_rb_to_opls, +) @pytest.mark.skipif( @@ -294,7 +298,6 @@ def test_missing_scaling_factors(self): with pytest.raises(AttributeError): assert ff.coulomb14scale - # check xml files for dihedral conversions from RB to OPLS # Note: the non-exact conversions are most likely due to the OPLS equation # not using the f0/2 term (i.e., f0 = 0), regardless if the analytical @@ -302,451 +305,437 @@ def test_missing_scaling_factors(self): def test_oplsaa_xml_dihedral_rb_to_opls(self): oplsaa_non_exact_conversion_list = [ [ - ['CT', 'OS', 'P', 'OS'], + ["CT", "OS", "P", "OS"], [1.046, 3.138, 10.0416, -4.184, 0.0, 0.0], [20.0832, 0.0, -10.0416, 2.092, -0.0], 10.0416, - False + False, ] ] - pass_oplsaa_rb_to_opls = test_xml_dihedral_rb_to_opls("oplsaa", - 'all_oplsaa_rb_to_opls_errors.txt', - oplsaa_non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4) + pass_oplsaa_rb_to_opls = test_xml_dihedral_rb_to_opls( + "oplsaa", + "all_oplsaa_rb_to_opls_errors.txt", + oplsaa_non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, + ) assert pass_oplsaa_rb_to_opls is True def test_trappeua_xml_dihedral_rb_to_opls(self): trappeua_non_exact_conversion_list = [ [ - ['CH3', 'CH2', 'CH', 'CH3'], + ["CH3", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH2', 'CH', 'CH2'], + ["CH3", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH2', 'CH', 'CH'], + ["CH3", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH2', 'CH', 'C'], + ["CH3", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'CH3'], + ["CH2", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'CH2'], + ["CH2", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'CH'], + ["CH2", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH2', 'CH', 'C'], + ["CH2", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'CH3'], + ["CH", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'CH2'], + ["CH", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'CH'], + ["CH", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH2', 'CH', 'C'], + ["CH", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'CH3'], + ["C", "CH2", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'CH2'], + ["C", "CH2", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'CH'], + ["C", "CH2", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH2', 'CH', 'C'], + ["C", "CH2", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'CH3'], + ["CH3", "CH", "CH", "CH3"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'CH2'], + ["CH3", "CH", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'CH'], + ["CH3", "CH", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'CH', 'C'], + ["CH3", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH', 'CH', 'CH2'], + ["CH2", "CH", "CH", "CH2"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH', 'CH', 'CH'], + ["CH2", "CH", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH2', 'CH', 'CH', 'C'], + ["CH2", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH', 'CH', 'CH'], + ["CH", "CH", "CH", "CH"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH', 'CH', 'CH', 'C'], + ["CH", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['C', 'CH', 'CH', 'C'], + ["C", "CH", "CH", "C"], [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], 2.08734, - False + False, ], - [ - ['CH3', 'CH', 'O', 'H'], + ["CH3", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], - [ - ['CH2', 'CH', 'O', 'H'], + ["CH2", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], - [ - ['CH', 'CH', 'O', 'H'], + ["CH", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], - [ - ['C', 'CH', 'O', 'H'], + ["C", "CH", "O", "H"], [2.51338, -5.97885, -0.52315, 5.78421, 0.0, 0.0], [3.59118, 3.281385, 0.52315, -2.892105, -0.0], 1.79559, - False + False, ], ] - pass_trappeua_rb_to_opls = test_xml_dihedral_rb_to_opls("trappe-ua", - 'all_trappeua_rb_to_opls_errors.txt', - trappeua_non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4) + pass_trappeua_rb_to_opls = test_xml_dihedral_rb_to_opls( + "trappe-ua", + "all_trappeua_rb_to_opls_errors.txt", + trappeua_non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, + ) assert pass_trappeua_rb_to_opls is True def test_xml_dihedrals_function_error_not_float(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' - error_tolerance_rb_to_opls = 's' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" + error_tolerance_rb_to_opls = "s" with pytest.raises( TypeError, match=f"The error_tolerance_rb_to_opls variable must be a float, " - f"is type {type(error_tolerance_rb_to_opls)}.", + f"is type {type(error_tolerance_rb_to_opls)}.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) # error_tolerance_rb_to_opls range is 1e-1 to 1e-10 def test_xml_dihedrals_function_error_too_high(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1.01e-1 with pytest.raises( ValueError, - match= f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " - f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", + match=f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " + f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_error_too_low(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 0.999e-10 with pytest.raises( ValueError, - match= f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " - f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", + match=f"The error_tolerance_rb_to_opls variable is not 1e-10 \<= " + f"\( entered value is {error_tolerance_rb_to_opls} \) \<= 1e-1.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_ff_xml_not_good_extension(self): - xml_file_directory_and_filename = 'test_ff.out' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff.out" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1e-4 with pytest.raises( ValueError, - match= r"Please make sure you are entering the correct " - "foyer FF name and not a path to a FF file. " - "If you are entering a path to a FF file, " - "please use the forcefield_files variable with the " - "proper XML extension \(.xml\).", + match=r"Please make sure you are entering the correct " + "foyer FF name and not a path to a FF file. " + "If you are entering a path to a FF file, " + "please use the forcefield_files variable with the " + "proper XML extension \(.xml\).", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_user_defined_ff_xml_not_exist(self): - xml_file_directory_and_filename = 'test_ff.xml' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff.xml" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1e-4 with pytest.raises( ValueError, - match= "Please make sure you are entering the correct foyer FF path, " - "including the FF file name.xml " - "If you are using the pre-build FF files in foyer, " - "only use the string name without any extension." + match="Please make sure you are entering the correct foyer FF path, " + "including the FF file name.xml " + "If you are using the pre-build FF files in foyer, " + "only use the string name without any extension.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_xml_dihedrals_function_foyer_std_ff_xml_not_exist(self): - xml_file_directory_and_filename = 'test_ff' - output_file_name = 'test_filename.txt' + xml_file_directory_and_filename = "test_ff" + output_file_name = "test_filename.txt" error_tolerance_rb_to_opls = 1e-4 with pytest.raises( ValueError, - match= "Please make sure you are entering the correct foyer FF name " - "without the .xml extension." + match="Please make sure you are entering the correct foyer FF name " + "without the .xml extension.", ): - _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, - ) - + _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) def test_run_xml_test_input_not_dict(self): - xml_and_error_file_dict = ['test_ff.xml', 'test_filename.txt'] + xml_and_error_file_dict = ["test_ff.xml", "test_filename.txt"] with pytest.raises( TypeError, - match= f"The xml_and_error_file_dict variable must be a dict, " - f"is type {type(xml_and_error_file_dict)}." + match=f"The xml_and_error_file_dict variable must be a dict, " + f"is type {type(xml_and_error_file_dict)}.", ): run_xml_test(xml_and_error_file_dict) def test_run_xml_test_input_key_not_str(self): xml_key = 1 - xml_value = 'test_filename.txt' + xml_value = "test_filename.txt" xml_and_error_file_dict = {xml_key: xml_value} with pytest.raises( TypeError, - match= f"The xml_and_error_file_dict keys are not all strings, " - f"and has the type {type(xml_key)}." + match=f"The xml_and_error_file_dict keys are not all strings, " + f"and has the type {type(xml_key)}.", ): run_xml_test(xml_and_error_file_dict) def test_run_xml_test_input_value_not_str(self): - xml_key = 'test_ff.xml' + xml_key = "test_ff.xml" xml_value = 1 xml_and_error_file_dict = {xml_key: xml_value} with pytest.raises( TypeError, - match = f"The xml_and_error_file_dict values are not all strings, " - f"and has the type {type(xml_value)}." + match=f"The xml_and_error_file_dict values are not all strings, " + f"and has the type {type(xml_value)}.", ): run_xml_test(xml_and_error_file_dict) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): - xml_filename = 'test_ff.xml' - error_filename = 'test_filename.txt' - non_exact_conversion_list = 'str' + xml_filename = "test_ff.xml" + error_filename = "test_filename.txt" + non_exact_conversion_list = "str" with pytest.raises( TypeError, - match = f"The non_exact_conversion_list variables is not formated correctly. " - f"Please see the test_xml_dihedral_rb_to_opls function for the " - f"proper format infomation." + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation.", ): - test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list) + test_xml_dihedral_rb_to_opls( + xml_filename, error_filename, non_exact_conversion_list + ) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): - xml_filename = 'test_ff.xml' - error_filename = 'test_filename.txt' + xml_filename = "test_ff.xml" + error_filename = "test_filename.txt" non_exact_conversion_list = [ - [ - ['CH3', 'CH2', 'CH', 'CH3'], - [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], - [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], - 2.08734, - 'False' - ] + [ + ["CH3", "CH2", "CH", "CH3"], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + "False", + ] ] with pytest.raises( - TypeError, - match=f"The non_exact_conversion_list variables is not formated correctly. " - f"Please see the test_xml_dihedral_rb_to_opls function for the " - f"proper format infomation." + TypeError, + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation.", ): - test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list) + test_xml_dihedral_rb_to_opls( + xml_filename, error_filename, non_exact_conversion_list + ) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_4_length(self): - xml_filename = 'test_ff.xml' - error_filename = 'test_filename.txt' + xml_filename = "test_ff.xml" + error_filename = "test_filename.txt" non_exact_conversion_list = [ - [ - ['CH3', 'CH2', 'CH', 'CH3'], - [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], - [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], - 2.08734 - ] + [ + ["CH3", "CH2", "CH", "CH3"], + [3.28629, 7.44211, 1.85995, -14.67569, 0.0, 0.0], + [-4.17468, 7.129315, -1.85995, 7.337845, -0.0], + 2.08734, + ] ] with pytest.raises( - TypeError, - match=f"The non_exact_conversion_list variables is not formated correctly. " - f"Please see the test_xml_dihedral_rb_to_opls function for the " - f"proper format infomation." + TypeError, + match=f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation.", ): - test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list) + test_xml_dihedral_rb_to_opls( + xml_filename, error_filename, non_exact_conversion_list + ) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 900b90c0..7b49e16c 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -1,10 +1,14 @@ -import numpy as np import os import xml.etree.ElementTree as ET + +# from mbuild.utils.conversion import RB_to_OPLS +from warnings import warn + +import numpy as np + from foyer.forcefields import forcefields from foyer.forcefields.forcefields import get_forcefield -#from mbuild.utils.conversion import RB_to_OPLS -from warnings import warn + # **************************************************************************** # remove this RB_to_OPLS once the new version of foyer imports the new mBuild (START) @@ -105,13 +109,18 @@ def RB_to_OPLS( f3 = -c3 / 2 f4 = -c4 / 4 return np.array([f0, f1, f2, f3, f4]) + + # **************************************************************************** # remove this RB_to_OPLS once the new version of foyer imports the new mBuild (END) # **************************************************************************** -def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, - error_tolerance_rb_to_opls=1e-4, - ): + +def _test_xml_dihedrals( + xml_file_directory_and_filename, + output_file_name, + error_tolerance_rb_to_opls=1e-4, +): r""" Tests a single FF, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files @@ -176,26 +185,24 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, """ if not isinstance(error_tolerance_rb_to_opls, float): raise TypeError( - f"The error_tolerance_rb_to_opls variable must be a float, " - f"is type {type(error_tolerance_rb_to_opls)}." - ) + f"The error_tolerance_rb_to_opls variable must be a float, " + f"is type {type(error_tolerance_rb_to_opls)}." + ) else: if not 1e-10 <= error_tolerance_rb_to_opls <= 1e-1: - print(f"printed The error_tolerance_rb_to_opls variable is not 1e-10 <= " - f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1.") + print( + f"printed The error_tolerance_rb_to_opls variable is not 1e-10 <= " + f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1." + ) raise ValueError( f"The error_tolerance_rb_to_opls variable is not 1e-10 <= " - f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1." + f"( entered value is {error_tolerance_rb_to_opls} ) <= 1e-1." ) - if ( - os.path.splitext(xml_file_directory_and_filename)[1] == ".xml" - ): + if os.path.splitext(xml_file_directory_and_filename)[1] == ".xml": foyer_packaged_ff = False - elif ( - os.path.splitext(xml_file_directory_and_filename)[1] == "" - ): + elif os.path.splitext(xml_file_directory_and_filename)[1] == "": foyer_packaged_ff = True else: @@ -223,10 +230,10 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, elif foyer_packaged_ff is True: ff_full_path_and_filename = ( - forcefields.get_ff_path()[0] - + "/xml/" - + xml_file_directory_and_filename - + ".xml" + forcefields.get_ff_path()[0] + + "/xml/" + + xml_file_directory_and_filename + + ".xml" ) try: ff_xml = ET.parse(ff_full_path_and_filename) @@ -239,44 +246,49 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, with open(output_file_name, "w") as data_opls_rb: ff_root = ff_xml.getroot() - rb_torsionForce_proper_root = ff_root.findall('RBTorsionForce/Proper') + rb_torsionForce_proper_root = ff_root.findall("RBTorsionForce/Proper") count_no = 0 for child in rb_torsionForce_proper_root: count_no += 1 - class1_iter = child.attrib['class1'] - class2_iter = child.attrib['class2'] - class3_iter = child.attrib['class3'] - class4_iter = child.attrib['class4'] - - class_list_iter = [class1_iter, - class2_iter, - class3_iter, - class4_iter] - - c0_iter = float(child.attrib['c0']) - c1_iter = float(child.attrib['c1']) - c2_iter = float(child.attrib['c2']) - c3_iter = float(child.attrib['c3']) - c4_iter = float(child.attrib['c4']) - c5_iter = float(child.attrib['c5']) - - cX_list_iter = [c0_iter, - c1_iter, - c2_iter, - c3_iter, - c4_iter, - c5_iter] - - fX_list_iter = RB_to_OPLS(c0_iter, - c1_iter, - c2_iter, - c3_iter, - c4_iter, - c5_iter, - error_tolerance = error_tolerance_rb_to_opls, - error_if_outside_tolerance=False - ) + class1_iter = child.attrib["class1"] + class2_iter = child.attrib["class2"] + class3_iter = child.attrib["class3"] + class4_iter = child.attrib["class4"] + + class_list_iter = [ + class1_iter, + class2_iter, + class3_iter, + class4_iter, + ] + + c0_iter = float(child.attrib["c0"]) + c1_iter = float(child.attrib["c1"]) + c2_iter = float(child.attrib["c2"]) + c3_iter = float(child.attrib["c3"]) + c4_iter = float(child.attrib["c4"]) + c5_iter = float(child.attrib["c5"]) + + cX_list_iter = [ + c0_iter, + c1_iter, + c2_iter, + c3_iter, + c4_iter, + c5_iter, + ] + + fX_list_iter = RB_to_OPLS( + c0_iter, + c1_iter, + c2_iter, + c3_iter, + c4_iter, + c5_iter, + error_tolerance=error_tolerance_rb_to_opls, + error_if_outside_tolerance=False, + ) f0_iter = fX_list_iter[0] f1_iter = fX_list_iter[1] @@ -284,12 +296,13 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, f3_iter = fX_list_iter[3] f4_iter = fX_list_iter[4] - fx_list_iter_6_decimals = [np.round(fX_list_iter[0], decimals=6), - np.round(fX_list_iter[1], decimals=6), - np.round(fX_list_iter[2], decimals=6), - np.round(fX_list_iter[3], decimals=6), - np.round(fX_list_iter[4], decimals=6) - ] + fx_list_iter_6_decimals = [ + np.round(fX_list_iter[0], decimals=6), + np.round(fX_list_iter[1], decimals=6), + np.round(fX_list_iter[2], decimals=6), + np.round(fX_list_iter[3], decimals=6), + np.round(fX_list_iter[4], decimals=6), + ] # generate a 100 list for the functions over range of 2 * Pi no_points = 100 @@ -305,15 +318,16 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, rad_psi_iter = rad_iter + np.pi # RB torsions - rb_torsion_iter = c0_iter \ - + c1_iter * (np.cos(rad_psi_iter)) ** 1 \ - + c2_iter * (np.cos(rad_psi_iter)) ** 2 \ - + c3_iter * (np.cos(rad_psi_iter)) ** 3 \ - + c4_iter * (np.cos(rad_psi_iter)) ** 4 \ - + c5_iter * (np.cos(rad_psi_iter)) ** 5 + rb_torsion_iter = ( + c0_iter + + c1_iter * (np.cos(rad_psi_iter)) ** 1 + + c2_iter * (np.cos(rad_psi_iter)) ** 2 + + c3_iter * (np.cos(rad_psi_iter)) ** 3 + + c4_iter * (np.cos(rad_psi_iter)) ** 4 + + c5_iter * (np.cos(rad_psi_iter)) ** 5 + ) rb_torsion_list.append(rb_torsion_iter) - # RB torsions converted to standard OPLS version via alternate function; # the f0 is removed here. It is not a part of the standard OPLS dihedral # since it has to be zero for the standard OPLS dihedral to be @@ -321,54 +335,72 @@ def _test_xml_dihedrals(xml_file_directory_and_filename, output_file_name, # If f0/2 is added to the rb_to_opls_iter variable, the RB to OPLS conversion # should be analytically correct within the machine precision tolerance. rb_to_opls_iter = (1 / 2) * ( - + f1_iter * (1 + np.cos(rad_iter)) - + f2_iter * (1 - np.cos(2 * rad_iter)) - + f3_iter * (1 + np.cos(3 * rad_iter)) - + f4_iter * (1 - np.cos(4 * rad_iter)) - ) + +f1_iter * (1 + np.cos(rad_iter)) + + f2_iter * (1 - np.cos(2 * rad_iter)) + + f3_iter * (1 + np.cos(3 * rad_iter)) + + f4_iter * (1 - np.cos(4 * rad_iter)) + ) rb_to_opls_list.append(rb_to_opls_iter) - abs_differene_opls_rb_iter = abs(rb_to_opls_iter - rb_torsion_iter) + abs_differene_opls_rb_iter = abs( + rb_to_opls_iter - rb_torsion_iter + ) abs_differene_opls_rb_list.append(abs_differene_opls_rb_iter) if count_no == 1: - title_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}\n" \ - "".format("dihedral_atoms_or_beads", - "rb_constants_c0_c1_c2_c3_c4_c5", - "opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals", - "max_abs_delta_rb_to_opls_calc", - "exact_rb_to_opls_possible" - ) + title_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}\n" "".format( + "dihedral_atoms_or_beads", + "rb_constants_c0_c1_c2_c3_c4_c5", + "opls_constants_f0_f1_f2_f3_f4_rounded_6_decimals", + "max_abs_delta_rb_to_opls_calc", + "exact_rb_to_opls_possible", + ) data_opls_rb.write(title_to_output) # Is the iteration RB_to_opls conversion possible - if bool(np.isclose(c5_iter, 0, atol= error_tolerance_rb_to_opls, rtol=0)) is True \ - and bool(np.isclose(f0_iter, 0, atol= error_tolerance_rb_to_opls, rtol=0)) is True: + if ( + bool( + np.isclose( + c5_iter, 0, atol=error_tolerance_rb_to_opls, rtol=0 + ) + ) + is True + and bool( + np.isclose( + f0_iter, 0, atol=error_tolerance_rb_to_opls, rtol=0 + ) + ) + is True + ): rb_to_opls_convertable_iter = True else: rb_to_opls_convertable_iter = False - max_abs_diff_new_opls_rb = np.round(np.max(abs_differene_opls_rb_list), - decimals=int(np.log10((1/error_tolerance_rb_to_opls))+2) - ) + max_abs_diff_new_opls_rb = np.round( + np.max(abs_differene_opls_rb_list), + decimals=int(np.log10((1 / error_tolerance_rb_to_opls)) + 2), + ) if max_abs_diff_new_opls_rb > error_tolerance_rb_to_opls: - warning_to_output = "WARNING: The {} atoms with the {} RB constants not an exact conversion " \ - "for the RB_to_OPLS conversion!. " \ - "Max diff = {}, f0 = {}.\n" \ - "".format(class_list_iter, - cX_list_iter, - max_abs_diff_new_opls_rb, - f0_iter - ) - text_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}" \ - "\n".format(str(class_list_iter), - str(cX_list_iter), - str(fx_list_iter_6_decimals), - str(max_abs_diff_new_opls_rb), - str(rb_to_opls_convertable_iter) - ) + warning_to_output = ( + "WARNING: The {} atoms with the {} RB constants not an exact conversion " + "for the RB_to_OPLS conversion!. " + "Max diff = {}, f0 = {}.\n" + "".format( + class_list_iter, + cX_list_iter, + max_abs_diff_new_opls_rb, + f0_iter, + ) + ) + text_to_output = "{:35s}{:60s}{:60s}{:35s}{:35s}" "\n".format( + str(class_list_iter), + str(cX_list_iter), + str(fx_list_iter_6_decimals), + str(max_abs_diff_new_opls_rb), + str(rb_to_opls_convertable_iter), + ) warn(warning_to_output) data_opls_rb.write(text_to_output) @@ -457,25 +489,30 @@ def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): for i_iter in range(0, len(xml_file_list)): if not isinstance(xml_file_list[i_iter], str): - raise TypeError(f"The xml_and_error_file_dict keys are not all strings, " - f"and has the type {type(xml_file_list[i_iter])}." - ) + raise TypeError( + f"The xml_and_error_file_dict keys are not all strings, " + f"and has the type {type(xml_file_list[i_iter])}." + ) if not isinstance(output_file_name_list[i_iter], str): - raise TypeError(f"The xml_and_error_file_dict values are not all strings, " - f"and has the type {type(output_file_name_list[i_iter])}." - ) - - _test_xml_dihedrals(xml_file_list[i_iter], - output_file_name_list[i_iter], - error_tolerance_rb_to_opls - ) - -def test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4 - ): + raise TypeError( + f"The xml_and_error_file_dict values are not all strings, " + f"and has the type {type(output_file_name_list[i_iter])}." + ) + + _test_xml_dihedrals( + xml_file_list[i_iter], + output_file_name_list[i_iter], + error_tolerance_rb_to_opls, + ) + + +def test_xml_dihedral_rb_to_opls( + xml_filename, + error_filename, + non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, +): r""" Tests and compare RB to standard OPLS conversion for accuracy and against with known errors @@ -567,40 +604,46 @@ def test_xml_dihedral_rb_to_opls(xml_filename, """ passed_test = True - non_exact_conversion_list_error_txt = f"The non_exact_conversion_list variables is not formated correctly. "\ - f"Please see the test_xml_dihedral_rb_to_opls function for the " \ - f"proper format infomation." + non_exact_conversion_list_error_txt = ( + f"The non_exact_conversion_list variables is not formated correctly. " + f"Please see the test_xml_dihedral_rb_to_opls function for the " + f"proper format infomation." + ) if isinstance(non_exact_conversion_list, list): len_non_exact_conversion_list = len(non_exact_conversion_list) for error_list_i in non_exact_conversion_list: if len(error_list_i) == 5: - if not isinstance( error_list_i[0], list) \ - or not isinstance( error_list_i[1], list) \ - or not isinstance( error_list_i[2], list) \ - or not isinstance( error_list_i[3], float) \ - or not isinstance( error_list_i[4], bool): + if ( + not isinstance(error_list_i[0], list) + or not isinstance(error_list_i[1], list) + or not isinstance(error_list_i[2], list) + or not isinstance(error_list_i[3], float) + or not isinstance(error_list_i[4], bool) + ): raise TypeError(non_exact_conversion_list_error_txt) else: raise TypeError(non_exact_conversion_list_error_txt) else: raise TypeError(non_exact_conversion_list_error_txt) - # put the non_exact_conversion_list in the same string format with the same + # put the non_exact_conversion_list in the same string format with the same # spacing as the printed/read files. correct_file_strings_list = [] for j_iter in non_exact_conversion_list: correct_file_strings_list.append( - '{:35s}{:60s}{:60s}{:35s}{:35s}\n'.format(str(j_iter[0]), - str(j_iter[1]), - str(j_iter[2]), - str(j_iter[3]), - str(j_iter[4]) - ) + "{:35s}{:60s}{:60s}{:35s}{:35s}\n".format( + str(j_iter[0]), + str(j_iter[1]), + str(j_iter[2]), + str(j_iter[3]), + str(j_iter[4]), + ) ) - run_xml_test({xml_filename: error_filename}, - error_tolerance_rb_to_opls=error_tolerance_rb_to_opls - ) + run_xml_test( + {xml_filename: error_filename}, + error_tolerance_rb_to_opls=error_tolerance_rb_to_opls, + ) with open(error_filename, "r") as fp: out_gomc = fp.readlines() @@ -608,8 +651,11 @@ def test_xml_dihedral_rb_to_opls(xml_filename, split_line = line.split() # check the values for the dihedrals - if i_iter != 0 and len(split_line) > 0 \ - and line not in correct_file_strings_list: - passed_test = False - - return passed_test \ No newline at end of file + if ( + i_iter != 0 + and len(split_line) > 0 + and line not in correct_file_strings_list + ): + passed_test = False + + return passed_test From 455b59011d973763d894d6c1084eb8cd5d1654e8 Mon Sep 17 00:00:00 2001 From: bc118 Date: Tue, 29 Jun 2021 17:36:27 -0400 Subject: [PATCH 05/14] fixed a few errors for pre-commit rev1 --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 7b49e16c..5319d503 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -122,7 +122,7 @@ def _test_xml_dihedrals( error_tolerance_rb_to_opls=1e-4, ): r""" - Tests a single FF, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files + Tests a single FF, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files. Checks the conversion of the Ryckaert-Bellemans (RB) type to OPLS type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput @@ -143,6 +143,8 @@ def _test_xml_dihedrals( where :math:`psi = t - pi = t - 180 degrees` + Parameters + ---------- xml_file_directory_and_filename : str A string with the RB torsions xml file path, file names, and extensions (i.e., '.xml'), or the just the name of the standard foyer force @@ -410,7 +412,7 @@ def _test_xml_dihedrals( def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): r""" - Tests multiple FFs, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files + Test multiple FFs, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files. Checks the conversion of the Ryckaert-Bellemans (RB) type to OPLS type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput @@ -514,7 +516,7 @@ def test_xml_dihedral_rb_to_opls( error_tolerance_rb_to_opls=1e-4, ): r""" - Tests and compare RB to standard OPLS conversion for accuracy and against with known errors + Test and compare RB to standard OPLS conversion for accuracy and against with known errors. Test that all the xml dihedrals either pass the RB to standard OPLS conversion via the mBuild RB_to_OPLS function, or they are in the know list that is not From 4eeb283f56337c38093230dcb5808631de4bf709 Mon Sep 17 00:00:00 2001 From: bc118 Date: Tue, 29 Jun 2021 17:38:45 -0400 Subject: [PATCH 06/14] fixed a few errors for pre-commit rev2 --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 5319d503..a9096c37 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -1,17 +1,15 @@ import os import xml.etree.ElementTree as ET - -# from mbuild.utils.conversion import RB_to_OPLS -from warnings import warn - import numpy as np +from warnings import warn from foyer.forcefields import forcefields from foyer.forcefields.forcefields import get_forcefield # **************************************************************************** # remove this RB_to_OPLS once the new version of foyer imports the new mBuild (START) +# add this when removed # from mbuild.utils.conversion import RB_to_OPLS # **************************************************************************** def RB_to_OPLS( c0, @@ -122,7 +120,7 @@ def _test_xml_dihedrals( error_tolerance_rb_to_opls=1e-4, ): r""" - Tests a single FF, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files. + Test a single FF, comparing the RB to standard OPLS conversion for accuracy and generates dihedral error files. Checks the conversion of the Ryckaert-Bellemans (RB) type to OPLS type dihedrals (i.e., the mBuild RB_to_OPLS function) for errors and generates ouput From 486760ffab3f502e4b28c65c43a58f751c6e11f4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Jun 2021 21:39:01 +0000 Subject: [PATCH 07/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index a9096c37..58aa6a26 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -1,8 +1,9 @@ import os import xml.etree.ElementTree as ET +from warnings import warn + import numpy as np -from warnings import warn from foyer.forcefields import forcefields from foyer.forcefields.forcefields import get_forcefield From d36faf887e9d638f5b17230eedf8ee44e903d46d Mon Sep 17 00:00:00 2001 From: bc118 Date: Tue, 29 Jun 2021 17:42:14 -0400 Subject: [PATCH 08/14] fixed a few errors for pre-commit rev3 --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 58aa6a26..08c61d63 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -8,10 +8,6 @@ from foyer.forcefields.forcefields import get_forcefield -# **************************************************************************** -# remove this RB_to_OPLS once the new version of foyer imports the new mBuild (START) -# add this when removed # from mbuild.utils.conversion import RB_to_OPLS -# **************************************************************************** def RB_to_OPLS( c0, c1, @@ -109,12 +105,6 @@ def RB_to_OPLS( f4 = -c4 / 4 return np.array([f0, f1, f2, f3, f4]) - -# **************************************************************************** -# remove this RB_to_OPLS once the new version of foyer imports the new mBuild (END) -# **************************************************************************** - - def _test_xml_dihedrals( xml_file_directory_and_filename, output_file_name, From aa0a4dbaa78f1c427c9acf232e81eacb993a86a5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Jun 2021 21:42:29 +0000 Subject: [PATCH 09/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 1 + 1 file changed, 1 insertion(+) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 08c61d63..bd5a14c5 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -105,6 +105,7 @@ def RB_to_OPLS( f4 = -c4 / 4 return np.array([f0, f1, f2, f3, f4]) + def _test_xml_dihedrals( xml_file_directory_and_filename, output_file_name, From 3902bb671a43b17935e2f60a07c06cbe9dc7df37 Mon Sep 17 00:00:00 2001 From: bc118 Date: Tue, 29 Jun 2021 17:47:18 -0400 Subject: [PATCH 10/14] fixed a few errors for pre-commit rev4 --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 1 + 1 file changed, 1 insertion(+) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index bd5a14c5..8ee55a34 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -1,3 +1,4 @@ +"""Test if the pre-packaged foyer XML file dihderals can be converted from RB to standard OPLS dihedrals (f0 is 0).""" import os import xml.etree.ElementTree as ET from warnings import warn From 9263f90d5a21ecbce5779ad4c6daf05c2ae09887 Mon Sep 17 00:00:00 2001 From: bc118 Date: Tue, 29 Jun 2021 19:44:57 -0400 Subject: [PATCH 11/14] fixed a few errors for the test file rev0 --- foyer/tests/test_forcefield_parameters.py | 2 +- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/foyer/tests/test_forcefield_parameters.py b/foyer/tests/test_forcefield_parameters.py index 9d6ed282..d1630de5 100644 --- a/foyer/tests/test_forcefield_parameters.py +++ b/foyer/tests/test_forcefield_parameters.py @@ -695,7 +695,7 @@ def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): xml_filename, error_filename, non_exact_conversion_list ) - def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): + def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_wrong_type_str(self): xml_filename = "test_ff.xml" error_filename = "test_filename.txt" non_exact_conversion_list = [ diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 8ee55a34..b0664376 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -500,11 +500,10 @@ def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): ) -def test_xml_dihedral_rb_to_opls( - xml_filename, - error_filename, - non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4, +def test_xml_dihedral_rb_to_opls(xml_filename, + error_filename, + non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4 ): r""" Test and compare RB to standard OPLS conversion for accuracy and against with known errors. From f56a3a0f19b70d0cdfd7f6a79a9daac0da2ea4e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Jun 2021 23:45:12 +0000 Subject: [PATCH 12/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- foyer/tests/test_forcefield_parameters.py | 4 +++- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/foyer/tests/test_forcefield_parameters.py b/foyer/tests/test_forcefield_parameters.py index d1630de5..e77786ca 100644 --- a/foyer/tests/test_forcefield_parameters.py +++ b/foyer/tests/test_forcefield_parameters.py @@ -695,7 +695,9 @@ def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): xml_filename, error_filename, non_exact_conversion_list ) - def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_wrong_type_str(self): + def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_wrong_type_str( + self, + ): xml_filename = "test_ff.xml" error_filename = "test_filename.txt" non_exact_conversion_list = [ diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index b0664376..8ee55a34 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -500,10 +500,11 @@ def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): ) -def test_xml_dihedral_rb_to_opls(xml_filename, - error_filename, - non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4 +def test_xml_dihedral_rb_to_opls( + xml_filename, + error_filename, + non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, ): r""" Test and compare RB to standard OPLS conversion for accuracy and against with known errors. From 048d8a4afe9d5c54c07b39bb258cccfd97e46dec Mon Sep 17 00:00:00 2001 From: bc118 Date: Wed, 30 Jun 2021 09:11:14 -0400 Subject: [PATCH 13/14] removed unused variables and imports --- foyer/tests/test_forcefield_parameters.py | 13 ++++++------- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 9 ++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/foyer/tests/test_forcefield_parameters.py b/foyer/tests/test_forcefield_parameters.py index e77786ca..e548d272 100644 --- a/foyer/tests/test_forcefield_parameters.py +++ b/foyer/tests/test_forcefield_parameters.py @@ -4,7 +4,6 @@ from foyer import Forcefield, forcefields from foyer.exceptions import MissingForceError, MissingParametersError from foyer.forcefield import get_available_forcefield_loaders -from foyer.forcefields.forcefields import get_forcefield from foyer.tests.utils import get_fn from foyer.utils.check_xml_dihedrals_RB_to_OPLS import ( _test_xml_dihedrals, @@ -681,7 +680,7 @@ def test_run_xml_test_input_value_not_str(self): run_xml_test(xml_and_error_file_dict) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): - xml_filename = "test_ff.xml" + ff_filename = "test_ff.xml" error_filename = "test_filename.txt" non_exact_conversion_list = "str" @@ -692,13 +691,13 @@ def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_str(self): f"proper format infomation.", ): test_xml_dihedral_rb_to_opls( - xml_filename, error_filename, non_exact_conversion_list + ff_filename, error_filename, non_exact_conversion_list ) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_wrong_type_str( self, ): - xml_filename = "test_ff.xml" + ff_filename = "test_ff.xml" error_filename = "test_filename.txt" non_exact_conversion_list = [ [ @@ -717,11 +716,11 @@ def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_wrong_type_str( f"proper format infomation.", ): test_xml_dihedral_rb_to_opls( - xml_filename, error_filename, non_exact_conversion_list + ff_filename, error_filename, non_exact_conversion_list ) def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_4_length(self): - xml_filename = "test_ff.xml" + ff_filename = "test_ff.xml" error_filename = "test_filename.txt" non_exact_conversion_list = [ [ @@ -739,5 +738,5 @@ def test_xml_dihedral_rb_to_opls_non_exact_conversion_list_4_length(self): f"proper format infomation.", ): test_xml_dihedral_rb_to_opls( - xml_filename, error_filename, non_exact_conversion_list + ff_filename, error_filename, non_exact_conversion_list ) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 8ee55a34..3e3319f5 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -6,7 +6,6 @@ import numpy as np from foyer.forcefields import forcefields -from foyer.forcefields.forcefields import get_forcefield def RB_to_OPLS( @@ -500,11 +499,8 @@ def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): ) -def test_xml_dihedral_rb_to_opls( - xml_filename, - error_filename, - non_exact_conversion_list, - error_tolerance_rb_to_opls=1e-4, +def test_xml_dihedral_rb_to_opls(xml_filename, error_filename, + non_exact_conversion_list, error_tolerance_rb_to_opls=1e-4, ): r""" Test and compare RB to standard OPLS conversion for accuracy and against with known errors. @@ -603,7 +599,6 @@ def test_xml_dihedral_rb_to_opls( f"proper format infomation." ) if isinstance(non_exact_conversion_list, list): - len_non_exact_conversion_list = len(non_exact_conversion_list) for error_list_i in non_exact_conversion_list: if len(error_list_i) == 5: if ( From 1dc94ca627b32addb5e0433ba4dd01c905d47987 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:12:07 +0000 Subject: [PATCH 14/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- foyer/utils/check_xml_dihedrals_RB_to_OPLS.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py index 3e3319f5..0679ef9a 100644 --- a/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py +++ b/foyer/utils/check_xml_dihedrals_RB_to_OPLS.py @@ -499,8 +499,11 @@ def run_xml_test(xml_and_error_file_dict, error_tolerance_rb_to_opls=1e-4): ) -def test_xml_dihedral_rb_to_opls(xml_filename, error_filename, - non_exact_conversion_list, error_tolerance_rb_to_opls=1e-4, +def test_xml_dihedral_rb_to_opls( + xml_filename, + error_filename, + non_exact_conversion_list, + error_tolerance_rb_to_opls=1e-4, ): r""" Test and compare RB to standard OPLS conversion for accuracy and against with known errors.