from __future__ import annotations import numpy as np from induced_exchange import ( ExchangeBond, ExchangeDataset, ExternalInducedResponse, InducedMomentResponse, MagneticCrystal, MagneticSite, UnitMetadata, compare_exchange_datasets, compare_induced_response, validate_dataset_compatibility, ) def site(index: int, moment: float = 1.0, position=(0.0, 0.0, 0.0)) -> MagneticSite: return MagneticSite(index, index, position, moment, (0.0, 0.0, 1.0)) def bond(i: int, j: int, displacement, jij: float) -> ExchangeBond: return ExchangeBond(i, j, tuple(displacement), jij) def crystal(bonds, *, moments=(1.0,), cell=None, energy="unspecified") -> MagneticCrystal: if cell is None: cell = np.eye(3) sites = [site(index + 1, moment) for index, moment in enumerate(moments)] return MagneticCrystal(np.asarray(cell, dtype=float), sites, list(bonds), UnitMetadata(energy=energy)) def test_identical_datasets_have_equal_raw_robust_dressed_and_export_tables(tmp_path): model = crystal( [bond(1, 1, (1, 0, 0), 1.0), bond(1, 1, (-1, 0, 0), 1.0), bond(2, 1, (0, 0, 0), 2.0), bond(1, 2, (0, 0, 0), 2.0)], moments=(1.0, 1.0), ) result = compare_exchange_datasets( ExchangeDataset(model, label="LKAG", robust_sites=(1,), induced_sites=(2,), x=0.5), ExchangeDataset(model, label="frozen magnon", robust_sites=(1,), induced_sites=(2,), x=0.5), [[0.0, 0.0, 0.0], [0.25, 0.0, 0.0]], include_magnons=False, ) assert result.compatibility.compatible assert np.allclose(result.raw_a, result.raw_b) assert np.allclose(result.robust_a, result.robust_b) assert np.allclose(result.dressed_a, result.dressed_b) written = result.export(tmp_path) assert (tmp_path / "imx06_raw_jq_eigenvalues.csv").exists() assert written["summary"].exists() def test_structural_compatibility_rejects_different_basis_but_allows_exchange_difference(): a = crystal([], moments=(1.0,)) b = crystal([], moments=(1.0,), cell=[[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 1.0]]) report = validate_dataset_compatibility(a, b) assert not report.compatible assert any("cells" in error for error in report.errors) def test_sign_reversed_dataset_changes_raw_ordering_diagnostic(): a = crystal([bond(1, 1, (1, 0, 0), 1.0), bond(1, 1, (-1, 0, 0), 1.0)]) b = crystal([bond(1, 1, (1, 0, 0), -1.0), bond(1, 1, (-1, 0, 0), -1.0)]) result = compare_exchange_datasets(a, b, [[0.0, 0.0, 0.0], [0.5, 0.0, 0.0]], include_magnons=False, stiffness_q_max=None) assert result.dataset_a.raw_ordering.kind == "FM at Gamma" assert result.dataset_b.raw_ordering.kind == "AF-like/non-Gamma" assert any("Dataset B raw ordering" in item for item in result.diagnostics) def test_response_comparison_matching_and_mismatch_are_quantified(): model = crystal([bond(2, 1, (0, 0, 0), 2.0), bond(1, 2, (0, 0, 0), 2.0)], moments=(1.0, 1.0)) response_a = InducedMomentResponse(model, [1], [2], x=0.5) response_b = InducedMomentResponse(model, [1], [2], x=0.5) q = [[0.0, 0.0, 0.0], [0.25, 0.0, 0.0]] matching = compare_induced_response(response_a, response_b, q, [[1.0], [1.0]], external=ExternalInducedResponse([1.0, 1.0], q=np.asarray(q))) assert matching.metrics_a is not None and matching.metrics_a.rmse == 0.0 mismatching = compare_induced_response(response_a, response_b, q, [[1.0], [1.0]], external=ExternalInducedResponse([1.0, 2.0], q=np.asarray(q))) assert mismatching.metrics_a is not None and mismatching.metrics_a.strongly_disagrees assert any("do not reproduce" in warning for warning in mismatching.warnings) def test_response_comparison_converts_normalized_model_response_to_physical_moments(): model = crystal([bond(2, 1, (0, 0, 0), 2.0), bond(1, 2, (0, 0, 0), 2.0)], moments=(3.0, 1.5)) response = InducedMomentResponse(model, [1], [2], x=0.5) external = ExternalInducedResponse([1.5, 1.5], q=np.asarray([[0.0, 0.0, 0.0], [0.25, 0.0, 0.0]])) result = compare_induced_response(response, response, external.q, [[1.0], [1.0]], external=external) assert np.allclose(result.model_a, [[1.0], [1.0]]) assert result.metrics_a is not None and result.metrics_a.rmse == 0.0 def test_external_response_file_accepts_both_supported_layouts(tmp_path): q_file = tmp_path / "q_response.dat" q_file.write_text("0 0 0 1\n0.5 0 0 0.2\n", encoding="utf-8") path_file = tmp_path / "path_response.dat" path_file.write_text("0 1\n1 0.2\n", encoding="utf-8") assert ExternalInducedResponse.from_file(q_file).q.shape == (2, 3) assert ExternalInducedResponse.from_file(path_file).path_coordinate.tolist() == [0.0, 1.0]