1# type: ignore 2import numpy as np 3 4from ase import Atoms 5from ase.io import read, write 6import ase.io.rmc6f as rmc6f 7from ase.lattice.compounds import TRI_Fe2O3 8 9 10rmc6f_input_text = """ 11(Version 6f format configuration file) 12Metadata owner: Joe Smith 13Metadata date: 31-01-1900 14Metadata material: SF6 15Metadata comment: Some comments go here... 16Metadata source: GSAS 17Number of moves generated: 89692 18Number of moves tried: 85650 19Number of moves accepted: 10074 20Number of prior configuration saves: 0 21Number of atoms: 7 22Number density (Ang^-3): 0.068606 23Supercell dimensions: 1 1 1 24Cell (Ang/deg): 4.672816 4.672816 4.672816 90.0 90.0 90.0 25Lattice vectors (Ang): 26 4.672816 0.000000 0.000000 27 0.000000 4.672816 0.000000 28 0.000000 0.000000 4.672816 29Atoms: 30 1 S 0.600452 0.525100 0.442050 1 0 0 0 31 2 F 0.911952 0.450722 0.382733 2 0 0 0 32 3 F 0.283794 0.616712 0.500094 3 0 0 0 33 4 F 0.679823 0.854839 0.343915 4 0 0 0 34 5 F 0.531660 0.229024 0.535688 5 0 0 0 35 6 F 0.692514 0.584931 0.746683 6 0 0 0 36 7 F 0.509687 0.449350 0.111960 7 0 0 0 37""" 38# Information for system 39positions = [ 40 [0.600452, 0.525100, 0.442050], 41 [0.911952, 0.450722, 0.382733], 42 [0.283794, 0.616712, 0.500094], 43 [0.679823, 0.854839, 0.343915], 44 [0.531660, 0.229024, 0.535688], 45 [0.692514, 0.584931, 0.746683], 46 [0.509687, 0.449350, 0.111960]] 47symbols = ['S', 'F', 'F', 'F', 'F', 'F', 'F'] 48lat = 4.672816 49 50# Intermediate data structures to test against 51symbol_xyz_list = [[s] + xyz for s, xyz in zip(symbols, positions)] 52symbol_xyz_dict = {i + 1: row for i, row in enumerate(symbol_xyz_list)} 53symbol_xyz_list_ext = [tuple([i + 1] + row + [0, 0, 0, 0]) 54 for i, row in enumerate(symbol_xyz_list)] 55 56# Target Atoms system 57lat_positions = lat * np.array(positions) 58cell = [lat, lat, lat] 59rmc6f_atoms = Atoms(symbols, positions=lat_positions, cell=cell, pbc=[1, 1, 1]) 60 61 62def test_rmc6f_read(): 63 """Test for reading rmc6f input file.""" 64 with open('input.rmc6f', 'w') as rmc6f_input_f: 65 rmc6f_input_f.write(rmc6f_input_text) 66 67 rmc6f_input_atoms = read('input.rmc6f') 68 assert len(rmc6f_input_atoms) == 7 69 assert rmc6f_input_atoms == rmc6f_atoms 70 71 72def test_rmc6f_write(): 73 """Test for writing rmc6f input file.""" 74 tol = 1e-5 75 write('output.rmc6f', rmc6f_atoms) 76 readback = read('output.rmc6f') 77 assert np.allclose(rmc6f_atoms.positions, readback.positions, rtol=tol) 78 assert readback == rmc6f_atoms 79 80 81def test_rmc6f_write_with_order(): 82 """Test for writing rmc6f input file with order passed in.""" 83 tol = 1e-5 84 write('output.rmc6f', rmc6f_atoms, order=['F', 'S']) 85 readback = read('output.rmc6f') 86 reordered_positions = np.vstack( 87 (rmc6f_atoms.positions[1:7], rmc6f_atoms.positions[0])) 88 assert np.allclose(reordered_positions, readback.positions, rtol=tol) 89 90 91def test_rmc6f_write_with_triclinic_system(): 92 """Test for writing rmc6f input file for triclinic system 93 """ 94 tol = 1e-5 95 fe4o6 = TRI_Fe2O3( 96 symbol=('Fe', 'O'), 97 latticeconstant={ 98 'a': 5.143, 99 'b': 5.383, 100 'c': 14.902, 101 'alpha': 90.391, 102 'beta': 90.014, 103 'gamma': 89.834}, 104 size=(1, 1, 1)) 105 106 # Make sure these are the returned cells (verified correct for rmc6f file) 107 va = [5.143, 0.0, 0.0] 108 vb = [0.015596, 5.382977, 0.0] 109 vc = [-0.00364124, -0.101684, 14.901653] 110 111 write('output.rmc6f', fe4o6) 112 readback = read('output.rmc6f') 113 assert np.allclose(fe4o6.positions, readback.positions, rtol=tol) 114 assert np.allclose(va, readback.cell[0], rtol=tol) 115 assert np.allclose(vb, readback.cell[1], rtol=tol) 116 assert np.allclose(vc, readback.cell[2], rtol=tol) 117 118 119def test_rmc6f_read_construct_regex(): 120 """Test for utility function that constructs rmc6f header regex.""" 121 header_lines = [ 122 "Number of atoms:", 123 " Supercell dimensions: ", 124 " Cell (Ang/deg): ", 125 " Lattice vectors (Ang): "] 126 result = rmc6f._read_construct_regex(header_lines) 127 target = "(Number\\s+of\\s+atoms:|Supercell\\s+dimensions:|Cell\\s+\\(Ang/deg\\):|Lattice\\s+vectors\\s+\\(Ang\\):)" # noqa: E501 128 assert result == target 129 130 131def test_rmc6f_read_line_of_atoms_section_style_no_labels(): 132 """Test for reading a line of atoms section 133 w/ 'no labels' style for rmc6f 134 """ 135 atom_line = "1 S 0.600452 0.525100 0.442050 1 0 0 0" 136 atom_id, props = rmc6f._read_line_of_atoms_section(atom_line.split()) 137 target_id = 1 138 target_props = ['S', 0.600452, 0.5251, 0.44205] 139 assert atom_id == target_id 140 assert props == target_props 141 142 143def test_rmc6f_read_line_of_atoms_section_style_labels(): 144 """Test for reading a line of atoms section 145 w/ 'labels'-included style for rmc6f 146 """ 147 atom_line = "1 S [1] 0.600452 0.525100 0.442050 1 0 0 0" 148 atom_id, props = rmc6f._read_line_of_atoms_section(atom_line.split()) 149 target_id = 1 150 target_props = ['S', 0.600452, 0.5251, 0.44205] 151 assert atom_id == target_id 152 assert props == target_props 153 154 155def test_rmc6f_read_line_of_atoms_section_style_magnetic(): 156 """Test for reading a line of atoms section 157 w/ 'magnetic' style for rmc6f 158 """ 159 atom_line = "1 S 0.600452 0.525100 0.442050 1 0 0 0 M: 0.1" 160 atom_id, props = rmc6f._read_line_of_atoms_section(atom_line.split()) 161 target_id = 1 162 target_props = ['S', 0.600452, 0.5251, 0.44205, 0.1] 163 assert atom_id == target_id 164 assert props == target_props 165 166 167def test_rmc6f_read_process_rmc6f_lines_to_pos_and_cell(): 168 """Test for utility function that processes lines of rmc6f using 169 regular expressions to capture atom properties and cell information 170 """ 171 tol = 1e-5 172 lines = rmc6f_input_text.split('\n') 173 props, cell = rmc6f._read_process_rmc6f_lines_to_pos_and_cell(lines) 174 175 target_cell = np.zeros((3, 3), float) 176 np.fill_diagonal(target_cell, 4.672816) 177 178 assert props == symbol_xyz_dict 179 assert np.allclose(cell, target_cell, rtol=tol) 180 181 182def test_rmc6f_read_process_rmc6f_lines_to_pos_and_cell_padded_whitespace(): 183 """Test for utility function that processes lines of rmc6f using 184 regular expressions to capture atom properties and cell information 185 with puposeful whitespace padded on one line 186 """ 187 tol = 1e-5 188 lines = rmc6f_input_text.split('\n') 189 lines[14] = " {} ".format(lines[14]) # intentional whitespace 190 props, cell = rmc6f._read_process_rmc6f_lines_to_pos_and_cell(lines) 191 192 target_cell = np.zeros((3, 3), float) 193 np.fill_diagonal(target_cell, 4.672816) 194 195 assert props == symbol_xyz_dict 196 assert np.allclose(cell, target_cell, rtol=tol) 197 198 199def test_rmc6f_write_output_column_format(): 200 """Test for utility function that processes the columns in array 201 and gets back out formatting information. 202 """ 203 cols = ['id', 'symbols', 'scaled_positions', 'ref_num', 'ref_cell'] 204 arrays = {} 205 arrays['id'] = np.array([1, 2, 3, 4, 5, 6, 7]) 206 arrays['symbols'] = np.array(symbols) 207 arrays['ref_num'] = np.zeros(7, int) 208 arrays['ref_cell'] = np.zeros((7, 3), int) 209 arrays['scaled_positions'] = lat_positions / lat 210 211 ncols, dtype_obj, fmt = rmc6f._write_output_column_format(cols, arrays) 212 213 target_ncols = [1, 1, 3, 1, 3] 214 target_fmt = "%8d %s%14.6f %14.6f %14.6f %8d %8d %8d %8d \n" 215 216 assert ncols == target_ncols 217 assert fmt == target_fmt 218 219 220def test_rmc6f_write_output(): 221 """Test for utility function for writing rmc6f output 222 """ 223 fileobj = "output.rmc6f" 224 header_lines = [ 225 '(Version 6f format configuration file)', 226 '(Generated by ASE - Atomic Simulation Environment https://wiki.fysik.dtu.dk/ase/ )', # noqa: E501 227 "Metadata date:18-007-'2019", 228 'Number of types of atoms: 2 ', 229 'Atom types present: S F', 230 'Number of each atom type: 1 6', 231 'Number of moves generated: 0', 232 'Number of moves tried: 0', 233 'Number of moves accepted: 0', 234 'Number of prior configuration saves: 0', 235 'Number of atoms: 7', 236 'Supercell dimensions: 1 1 1', 237 'Number density (Ang^-3): 0.06860598423060468', 238 'Cell (Ang/deg): 4.672816 4.672816 4.672816 90.0 90.0 90.0', 239 'Lattice vectors (Ang):', 240 ' 4.672816 0.000000 0.000000', 241 ' 0.000000 4.672816 0.000000', 242 ' 0.000000 0.000000 4.672816', 243 'Atoms:'] 244 245 data_array = symbol_xyz_list_ext 246 data_dtype = [ 247 ('id', '<i8'), 248 ('symbols', '<U1'), 249 ('scaled_positions0', '<f8'), 250 ('scaled_positions1', '<f8'), 251 ('scaled_positions2', '<f8'), 252 ('ref_num', '<i8'), 253 ('ref_cell0', '<i8'), 254 ('ref_cell1', '<i8'), 255 ('ref_cell2', '<i8')] 256 data = np.array(data_array, dtype=data_dtype) 257 fmt = "%8d %s%14.6f %14.6f %14.6f %8d %8d %8d %8d \n" 258 with open('output.rmc6f', 'w') as fileobj: 259 rmc6f._write_output(fileobj, header_lines, data, fmt) 260