|
8 | 8 |
|
9 | 9 | import MDAnalysis as mda |
10 | 10 | import numpy as np |
| 11 | +import numpy.linalg as la |
11 | 12 | import pytest |
12 | 13 |
|
13 | 14 | import tests.data as data |
@@ -753,6 +754,77 @@ def test_process_vibrational_only_levels(self): |
753 | 754 | self.assertIn(1.11, results) |
754 | 755 | self.assertIn(2.22, results) |
755 | 756 |
|
| 757 | + def test_process_vibrational_entropy_else_branch(self): |
| 758 | + """ |
| 759 | + Atomic unit test for EntropyManager._process_vibrational_entropy else-branch: |
| 760 | + - forcetorque_matrix is None |
| 761 | + - force/torque matrices are filtered |
| 762 | + - ve.vibrational_entropy_calculation called for force & torque |
| 763 | + - results logged as Transvibrational/Rovibrational |
| 764 | + - group label added from mol_container residues/atoms |
| 765 | + """ |
| 766 | + manager = MagicMock() |
| 767 | + manager._args = MagicMock(temperature=300) |
| 768 | + |
| 769 | + manager._level_manager = MagicMock() |
| 770 | + manager._data_logger = MagicMock() |
| 771 | + |
| 772 | + force_matrix = np.eye(3) |
| 773 | + torque_matrix = np.eye(3) * 2 |
| 774 | + |
| 775 | + filtered_force = np.eye(3) * 7 |
| 776 | + filtered_torque = np.eye(3) * 9 |
| 777 | + manager._level_manager.filter_zero_rows_columns.side_effect = [ |
| 778 | + filtered_force, |
| 779 | + filtered_torque, |
| 780 | + ] |
| 781 | + |
| 782 | + ve = MagicMock() |
| 783 | + ve.vibrational_entropy_calculation.side_effect = [1.11, 2.22] |
| 784 | + |
| 785 | + res1 = MagicMock(resname="ALA") |
| 786 | + res2 = MagicMock(resname="GLY") |
| 787 | + res3 = MagicMock(resname="ALA") |
| 788 | + mol_container = MagicMock() |
| 789 | + mol_container.residues = [res1, res2, res3] |
| 790 | + mol_container.atoms = [MagicMock(), MagicMock(), MagicMock(), MagicMock()] |
| 791 | + |
| 792 | + EntropyManager._process_vibrational_entropy( |
| 793 | + manager, |
| 794 | + group_id=0, |
| 795 | + mol_container=mol_container, |
| 796 | + number_frames=10, |
| 797 | + ve=ve, |
| 798 | + level="Vibrational", |
| 799 | + force_matrix=force_matrix, |
| 800 | + torque_matrix=torque_matrix, |
| 801 | + forcetorque_matrix=None, |
| 802 | + highest=True, |
| 803 | + ) |
| 804 | + |
| 805 | + filter_calls = manager._level_manager.filter_zero_rows_columns.call_args_list |
| 806 | + assert len(filter_calls) == 2 |
| 807 | + |
| 808 | + np.testing.assert_array_equal(filter_calls[0].args[0], force_matrix) |
| 809 | + np.testing.assert_array_equal(filter_calls[1].args[0], torque_matrix) |
| 810 | + |
| 811 | + ve_calls = ve.vibrational_entropy_calculation.call_args_list |
| 812 | + assert len(ve_calls) == 2 |
| 813 | + |
| 814 | + np.testing.assert_array_equal(ve_calls[0].args[0], filtered_force) |
| 815 | + assert ve_calls[0].args[1:] == ("force", 300, True) |
| 816 | + |
| 817 | + np.testing.assert_array_equal(ve_calls[1].args[0], filtered_torque) |
| 818 | + assert ve_calls[1].args[1:] == ("torque", 300, True) |
| 819 | + |
| 820 | + manager._data_logger.add_results_data.assert_any_call( |
| 821 | + 0, "Vibrational", "Transvibrational", 1.11 |
| 822 | + ) |
| 823 | + manager._data_logger.add_results_data.assert_any_call( |
| 824 | + 0, "Vibrational", "Rovibrational", 2.22 |
| 825 | + ) |
| 826 | + manager._data_logger.add_group_label.assert_called_once_with(0, "ALA_GLY", 3, 4) |
| 827 | + |
756 | 828 | def test_compute_entropies_polymer_branch(self): |
757 | 829 | """ |
758 | 830 | Test _compute_entropies triggers _process_vibrational_entropy for 'polymer' |
@@ -1546,6 +1618,108 @@ def test_vibrational_entropy_polymer_torque(self): |
1546 | 1618 |
|
1547 | 1619 | assert S_vib == pytest.approx(48.45003266069881) |
1548 | 1620 |
|
| 1621 | + def test_vibrational_entropy_calculation_forcetorqueTRANS(self): |
| 1622 | + """ |
| 1623 | + Test for matrix_type='forcetorqueTRANS': |
| 1624 | + - verifies S_vib_total = sum(S_components[:3]) |
| 1625 | + """ |
| 1626 | + run_manager = MagicMock() |
| 1627 | + run_manager.change_lambda_units.side_effect = lambda x: x |
| 1628 | + kT = 2.47e-21 |
| 1629 | + run_manager.get_KT2J.return_value = kT |
| 1630 | + |
| 1631 | + ve = VibrationalEntropy( |
| 1632 | + run_manager, |
| 1633 | + MagicMock(), |
| 1634 | + MagicMock(), |
| 1635 | + MagicMock(), |
| 1636 | + MagicMock(), |
| 1637 | + MagicMock(), |
| 1638 | + MagicMock(), |
| 1639 | + MagicMock(), |
| 1640 | + ) |
| 1641 | + |
| 1642 | + orig_eigvals = la.eigvals |
| 1643 | + la.eigvals = lambda m: np.array( |
| 1644 | + [1.0] * 6 |
| 1645 | + ) # length 6 -> 6 frequencies/components |
| 1646 | + |
| 1647 | + try: |
| 1648 | + freqs = np.array([6.0, 5.0, 4.0, 3.0, 2.0, 1.0]) |
| 1649 | + ve.frequency_calculation = MagicMock(return_value=freqs) |
| 1650 | + |
| 1651 | + matrix = np.identity(6) |
| 1652 | + |
| 1653 | + result = ve.vibrational_entropy_calculation( |
| 1654 | + matrix=matrix, |
| 1655 | + matrix_type="forcetorqueTRANS", |
| 1656 | + temp=298, |
| 1657 | + highest_level=True, |
| 1658 | + ) |
| 1659 | + |
| 1660 | + sorted_freqs = np.sort(freqs) |
| 1661 | + exponent = ve._PLANCK_CONST * sorted_freqs / kT |
| 1662 | + power_positive = np.exp(exponent) |
| 1663 | + power_negative = np.exp(-exponent) |
| 1664 | + S_components = exponent / (power_positive - 1) - np.log(1 - power_negative) |
| 1665 | + S_components *= ve._GAS_CONST |
| 1666 | + |
| 1667 | + expected = float(np.sum(S_components[:3])) |
| 1668 | + self.assertAlmostEqual(result, expected, places=12) |
| 1669 | + |
| 1670 | + finally: |
| 1671 | + la.eigvals = orig_eigvals |
| 1672 | + |
| 1673 | + def test_vibrational_entropy_calculation_forcetorqueROT(self): |
| 1674 | + """ |
| 1675 | + Test for matrix_type='forcetorqueROT': |
| 1676 | + - verifies S_vib_total = sum(S_components[3:]) |
| 1677 | + """ |
| 1678 | + run_manager = MagicMock() |
| 1679 | + run_manager.change_lambda_units.side_effect = lambda x: x |
| 1680 | + kT = 2.47e-21 |
| 1681 | + run_manager.get_KT2J.return_value = kT |
| 1682 | + |
| 1683 | + ve = VibrationalEntropy( |
| 1684 | + run_manager, |
| 1685 | + MagicMock(), |
| 1686 | + MagicMock(), |
| 1687 | + MagicMock(), |
| 1688 | + MagicMock(), |
| 1689 | + MagicMock(), |
| 1690 | + MagicMock(), |
| 1691 | + MagicMock(), |
| 1692 | + ) |
| 1693 | + |
| 1694 | + orig_eigvals = la.eigvals |
| 1695 | + la.eigvals = lambda m: np.array([1.0] * 6) |
| 1696 | + |
| 1697 | + try: |
| 1698 | + freqs = np.array([6.0, 5.0, 4.0, 3.0, 2.0, 1.0]) |
| 1699 | + ve.frequency_calculation = MagicMock(return_value=freqs) |
| 1700 | + |
| 1701 | + matrix = np.identity(6) |
| 1702 | + |
| 1703 | + result = ve.vibrational_entropy_calculation( |
| 1704 | + matrix=matrix, |
| 1705 | + matrix_type="forcetorqueROT", |
| 1706 | + temp=298, |
| 1707 | + highest_level=True, |
| 1708 | + ) |
| 1709 | + |
| 1710 | + sorted_freqs = np.sort(freqs) |
| 1711 | + exponent = ve._PLANCK_CONST * sorted_freqs / kT |
| 1712 | + power_positive = np.exp(exponent) |
| 1713 | + power_negative = np.exp(-exponent) |
| 1714 | + S_components = exponent / (power_positive - 1) - np.log(1 - power_negative) |
| 1715 | + S_components *= ve._GAS_CONST |
| 1716 | + |
| 1717 | + expected = float(np.sum(S_components[3:])) |
| 1718 | + self.assertAlmostEqual(result, expected, places=12) |
| 1719 | + |
| 1720 | + finally: |
| 1721 | + la.eigvals = orig_eigvals |
| 1722 | + |
1549 | 1723 | def test_calculate_water_orientational_entropy(self): |
1550 | 1724 | """ |
1551 | 1725 | Test that orientational entropy values are correctly extracted from Sorient_dict |
|
0 commit comments