| from pathlib import Path |
|
|
| import numpy as np |
| import pytest |
|
|
| from induced_exchange import ( |
| VectorMappingError, |
| infer_target_site, |
| map_exchange_file, |
| map_exchange_vector, |
| prepare_positions, |
| read_jfile, |
| ) |
|
|
|
|
| def test_maptypes_2_and_3_agree_for_a_basis_inside_the_first_cell(): |
| positions = [(1, "Fe", 0.0, 0.0, 0.0)] |
| prepared = prepare_positions(positions, np.eye(3)) |
|
|
| map2 = map_exchange_vector(1, 1, [1, 0, 0], positions=prepared, maptype=2) |
| map3 = map_exchange_vector(1, 1, [1, 0, 0], positions=prepared, maptype=3) |
|
|
| assert np.allclose(map2, [1, 0, 0]) |
| assert np.allclose(map2, map3) |
|
|
|
|
| def test_maptypes_2_and_3_differ_for_an_out_of_cell_basis_position(tmp_path: Path): |
| positions = [(1, "Fe", -0.25, 0.0, 0.0), (2, "Pt", 0.25, 0.0, 0.0)] |
| jfile = tmp_path / "jfile" |
| jfile.write_text("1 2 0 0 0 4\n", encoding="utf-8") |
| prepared = prepare_positions(positions, np.eye(3)) |
|
|
| map2 = map_exchange_file(jfile, positions=prepared, maptype=2) |
| map3 = map_exchange_file(jfile, positions=prepared, maptype=3) |
|
|
| assert np.allclose(map2[0].rij_cart, [-0.5, 0, 0]) |
| assert np.allclose(map3[0].rij_cart, [0.5, 0, 0]) |
| assert map2[0].inferred_target_cell_offset == (0, 0, 0) |
| assert map3[0].inferred_target_cell_offset == (1, 0, 0) |
|
|
|
|
| def test_direct_coordinate_positions_are_converted_before_folding(): |
| cell = np.diag([2.0, 3.0, 4.0]) |
| prepared = prepare_positions([(1, 1, 0.5, 0.5, 0.5)], cell, posfiletype="D") |
|
|
| assert np.allclose(prepared.positions_raw[1], [1.0, 1.5, 2.0]) |
| assert np.allclose(prepared.positions_folded[1], [1.0, 1.5, 2.0]) |
|
|
|
|
| def test_cartesian_coordinate_positions_are_preserved(): |
| position = [0.25, 0.5, 0.75] |
| prepared = prepare_positions([(1, 1, *position)], np.diag([2.0, 3.0, 4.0]), posfiletype="C") |
|
|
| assert np.allclose(prepared.positions_raw[1], position) |
| assert np.allclose(prepared.positions_folded[1], position) |
|
|
|
|
| def test_maptype_1_uses_cartesian_bond_vectors_without_basis_difference(): |
| prepared = prepare_positions([(1, 1, 0.0, 0.0, 0.0), (2, 2, 0.25, 0.0, 0.0)], np.eye(3)) |
|
|
| result = map_exchange_vector(1, 2, [0.75, 0, 0], positions=prepared, maptype=1, posfiletype="C") |
|
|
| assert np.allclose(result, [0.75, 0, 0]) |
|
|
|
|
| def test_duplicate_cartesian_vectors_use_the_last_jij(tmp_path: Path): |
| jfile = tmp_path / "jfile" |
| jfile.write_text("1 1 1 0 0 1\n1 1 1 0 0 2\n", encoding="utf-8") |
|
|
| records = map_exchange_file( |
| jfile, |
| positions=[(1, "Fe", 0.0, 0.0, 0.0)], |
| cell=np.eye(3), |
| maptype=1, |
| ) |
|
|
| assert len(records) == 1 |
| assert records[0].Jij == 2.0 |
|
|
|
|
| def test_jfile_reader_ignores_trailing_columns_and_text(tmp_path: Path): |
| jfile = tmp_path / "jfile" |
| jfile.write_text("1 1 1 0 0 2 trailing columns and text\n", encoding="utf-8") |
|
|
| parsed = read_jfile(jfile) |
| mapped = map_exchange_file( |
| jfile, |
| positions=[(1, "Fe", 0.0, 0.0, 0.0)], |
| cell=np.eye(3), |
| maptype=1, |
| ) |
|
|
| assert len(parsed) == 1 |
| assert parsed[0].Jij == 2.0 |
| assert mapped[0].Jij == 2.0 |
| assert mapped[0].supplied_distance is None |
|
|
|
|
| def test_periodic_offsets_are_reduced_and_free_offsets_are_rejected(tmp_path: Path): |
| jfile = tmp_path / "jfile" |
| jfile.write_text("1 1 2 0 0 1\n", encoding="utf-8") |
| positions = [(1, 1, 0.0, 0.0, 0.0)] |
|
|
| periodic = map_exchange_file(jfile, positions=positions, cell=np.eye(3), maptype=2, ncell=(2, 2, 2), bc=("P", "P", "P")) |
| assert periodic[0].inferred_target_cell_offset == (0, 0, 0) |
|
|
| with pytest.raises(VectorMappingError, match="outside the non-periodic supercell"): |
| map_exchange_file(jfile, positions=positions, cell=np.eye(3), maptype=2, ncell=(2, 2, 2), bc=("F", "F", "F")) |
|
|
|
|
| def test_random_alloy_rows_retain_metadata_but_use_basis_atom_types(tmp_path: Path): |
| jfile = tmp_path / "jfile" |
| jfile.write_text("1 1 Fe Pt 1 0 0 3\n", encoding="utf-8") |
|
|
| parsed = read_jfile(jfile) |
| mapped = map_exchange_file( |
| jfile, |
| positions=[(1, "basis-Fe", 0.0, 0.0, 0.0)], |
| cell=np.eye(3), |
| maptype=1, |
| ) |
|
|
| assert parsed[0].chemical_i == "Fe" |
| assert parsed[0].chemical_j == "Pt" |
| assert mapped[0].atom_type_i == "basis-Fe" |
| assert mapped[0].atom_type_j == "basis-Fe" |
|
|
|
|
| def test_vector_mapping_reports_invalid_maptype_missing_site_and_unmatched_target(): |
| prepared = prepare_positions([(1, 1, 0.0, 0.0, 0.0)], np.eye(3)) |
|
|
| with pytest.raises(VectorMappingError, match="maptype must be 1, 2, or 3"): |
| map_exchange_vector(1, 1, [0, 0, 0], positions=prepared, maptype=4) |
| with pytest.raises(VectorMappingError, match="missing from positions"): |
| map_exchange_vector(1, 2, [0, 0, 0], positions=prepared, maptype=1) |
| with pytest.raises(VectorMappingError, match="does not match a basis site"): |
| infer_target_site([0.25, 0.0, 0.0], prepared) |
|
|