File size: 4,881 Bytes
fd35ee7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59bbad9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd35ee7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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)