|
26 | 26 | import pytest |
27 | 27 | from nomad.client import normalize_all |
28 | 28 | from nomad.datamodel import EntryArchive, EntryMetadata |
| 29 | +from nomad.units import ureg |
29 | 30 | from nomad.utils import get_logger |
30 | 31 |
|
31 | 32 | from nomad_simulation_parsers.parsers.lammps.file_parsers import LogParser |
@@ -836,6 +837,74 @@ def test_md_method_barostat_extraction(): |
836 | 837 | assert barostat is None |
837 | 838 |
|
838 | 839 |
|
| 840 | +def test_md_method_barostat_modulus_extraction(): |
| 841 | + """Test modulus extraction and conversion to compressibility.""" |
| 842 | + |
| 843 | + writer = LammpsArchiveWriter() |
| 844 | + writer._log_parser = LogParser() |
| 845 | + # Initialize units system for apply_unit to work |
| 846 | + writer._log_parser._units = { |
| 847 | + 'pressure': ureg.atmosphere, |
| 848 | + 'time': ureg.femtosecond, |
| 849 | + } |
| 850 | + |
| 851 | + # Test NPT with modulus keyword (bulk modulus = 20000 atm) |
| 852 | + # compressibility should be 1/20000 atm^-1 |
| 853 | + fix_npt_modulus = [ |
| 854 | + [ |
| 855 | + '1', |
| 856 | + 'all', |
| 857 | + 'npt', |
| 858 | + 'temp', |
| 859 | + '300', |
| 860 | + '300', |
| 861 | + '100', |
| 862 | + 'iso', |
| 863 | + '1.0', |
| 864 | + '1.0', |
| 865 | + '1000.0', |
| 866 | + 'modulus', |
| 867 | + '20000.0', |
| 868 | + ] |
| 869 | + ] |
| 870 | + barostat = writer._extract_barostat_settings(fix_npt_modulus) |
| 871 | + assert barostat is not None |
| 872 | + assert barostat.compressibility is not None |
| 873 | + |
| 874 | + # Check compressibility value (1/20000 atm in SI units) |
| 875 | + # 1 atm = 101325 Pa, so 1/20000 atm^-1 = 1/(20000*101325) Pa^-1 |
| 876 | + expected_compressibility = 1.0 / (20000.0 * 101325.0) # Pa^-1 |
| 877 | + assert barostat.compressibility[0, 0].magnitude == pytest.approx( |
| 878 | + expected_compressibility, rel=1e-6 |
| 879 | + ) |
| 880 | + assert barostat.compressibility[1, 1].magnitude == pytest.approx( |
| 881 | + expected_compressibility, rel=1e-6 |
| 882 | + ) |
| 883 | + assert barostat.compressibility[2, 2].magnitude == pytest.approx( |
| 884 | + expected_compressibility, rel=1e-6 |
| 885 | + ) |
| 886 | + |
| 887 | + # Test NPH with modulus |
| 888 | + fix_nph_modulus = [ |
| 889 | + ['1', 'all', 'nph', 'iso', '1.0', '1.0', '1000.0', 'modulus', '10000.0'] |
| 890 | + ] |
| 891 | + barostat = writer._extract_barostat_settings(fix_nph_modulus) |
| 892 | + assert barostat is not None |
| 893 | + assert barostat.compressibility is not None |
| 894 | + expected_compressibility = 1.0 / (10000.0 * 101325.0) |
| 895 | + assert barostat.compressibility[0, 0].magnitude == pytest.approx( |
| 896 | + expected_compressibility, rel=1e-6 |
| 897 | + ) |
| 898 | + |
| 899 | + # Test without modulus (should be None) |
| 900 | + fix_npt_no_modulus = [ |
| 901 | + ['1', 'all', 'npt', 'temp', '300', '300', '100', 'iso', '1.0', '1.0', '1000.0'] |
| 902 | + ] |
| 903 | + barostat = writer._extract_barostat_settings(fix_npt_no_modulus) |
| 904 | + assert barostat is not None |
| 905 | + assert barostat.compressibility is None |
| 906 | + |
| 907 | + |
839 | 908 | def test_md_method_ensemble_detection(): |
840 | 909 | """Test ensemble type detection.""" |
841 | 910 |
|
@@ -1014,3 +1083,61 @@ def test_md_method_helper_methods(): |
1014 | 1083 | writer._log_parser._results = {'thermo': [50, 100, 200]} |
1015 | 1084 | thermo_freq = writer._extract_thermo_frequency() |
1016 | 1085 | assert thermo_freq == 200 |
| 1086 | + |
| 1087 | + |
| 1088 | +def test_md_method_integrator_type_extraction(): |
| 1089 | + """Test integrator type extraction from run_style command.""" |
| 1090 | + |
| 1091 | + class MockLogParser: |
| 1092 | + """Mock LogParser for testing.""" |
| 1093 | + |
| 1094 | + def __init__(self): |
| 1095 | + self._results = {} |
| 1096 | + |
| 1097 | + def get(self, key, default=None): |
| 1098 | + return self._results.get(key, default) |
| 1099 | + |
| 1100 | + writer = LammpsArchiveWriter() |
| 1101 | + writer._log_parser = MockLogParser() |
| 1102 | + |
| 1103 | + # Test verlet |
| 1104 | + writer._log_parser._results = {'run_style': 'verlet'} |
| 1105 | + integrator = writer._extract_integrator_type() |
| 1106 | + assert integrator == 'velocity_verlet' |
| 1107 | + |
| 1108 | + # Test verlet/split |
| 1109 | + writer._log_parser._results = {'run_style': 'verlet/split'} |
| 1110 | + integrator = writer._extract_integrator_type() |
| 1111 | + assert integrator == 'velocity_verlet_split' |
| 1112 | + |
| 1113 | + # Test respa |
| 1114 | + writer._log_parser._results = {'run_style': 'respa'} |
| 1115 | + integrator = writer._extract_integrator_type() |
| 1116 | + assert integrator == 'respa' |
| 1117 | + |
| 1118 | + # Test respa/omp |
| 1119 | + writer._log_parser._results = {'run_style': 'respa/omp'} |
| 1120 | + integrator = writer._extract_integrator_type() |
| 1121 | + assert integrator == 'respa' |
| 1122 | + |
| 1123 | + # Test with command args (respa with levels) |
| 1124 | + writer._log_parser._results = { |
| 1125 | + 'run_style': ['respa', '4', '2', '2', '2', 'bond', '1', 'kspace', '4'] |
| 1126 | + } |
| 1127 | + integrator = writer._extract_integrator_type() |
| 1128 | + assert integrator == 'respa' |
| 1129 | + |
| 1130 | + # Test with nested list of commands (take last) |
| 1131 | + writer._log_parser._results = {'run_style': [['verlet'], ['respa', '4', '2']]} |
| 1132 | + integrator = writer._extract_integrator_type() |
| 1133 | + assert integrator == 'respa' |
| 1134 | + |
| 1135 | + # Test None case |
| 1136 | + writer._log_parser._results = {} |
| 1137 | + integrator = writer._extract_integrator_type() |
| 1138 | + assert integrator is None |
| 1139 | + |
| 1140 | + # Test unknown style (fallback to velocity_verlet) |
| 1141 | + writer._log_parser._results = {'run_style': 'unknown_style'} |
| 1142 | + integrator = writer._extract_integrator_type() |
| 1143 | + assert integrator == 'velocity_verlet' |
0 commit comments