@@ -94,47 +94,52 @@ Return `true` if a symmetry that holds for the primal part is broken by
9494a perturbation in the lattice or in the positions, `false` otherwise.
9595"""
9696function is_symmetry_broken_by_dual (lattice, atoms, positions, symmetry:: SymOp ; tol_symmetry)
97+ # The symmetry operation is given by (W,w) where W is a 3x3 matrix and w a 3-vector,
98+ # both in reduced (fractional) coordinates.
9799 # For any lattice atom at position x, W*x + w should be in the lattice.
98- # In cartesian coordinates, with a perturbed lattice A = A₀ + εA₁,
99- # this means that for any atom position xcart in the unit cell and any 3 integers u,
100- # there should be an atom at position ycart and 3 integers v such that:
101- # Wcart * (xcart + A*u) + wcart = ycart + A*v
102- # where
103- # Wcart = A₀ * W * A₀⁻¹; note that W is an integer matrix
104- # wcart = A₀ * w.
105- #
106- # In relative coordinates this gives:
107- # A⁻¹ * Wcart * (A*x + A*u) + A⁻¹ * wcart = y + v (*)
100+ # This means that for any atom position x (in reduced coordinates) in the unit cell
101+ # and any 3 integers u, there should be an atom at position y and 3 integers v such that:
102+ # W * (x + u) + w = y + v
108103 #
104+ # Additionally, Wcart = A * W * A⁻¹ (in cartesian coordinates) must be orthogonal.
105+ #
109106 # The strategy is then to check that:
110- # 1. A⁻¹ * Wcart * A is still an integer matrix (i.e. no dual part),
111- # such that any change in u is easily compensated for in v.
112- # 2. The primal component of (*), i.e. with ε=0, is already known to hold.
113- # Since v does not have a dual component, it is enough to check that
114- # the dual part of the following is 0:
115- # A⁻¹ * Wcart * A*x + A⁻¹ * wcart - y
116-
117- lattice_primal = ForwardDiff. value .(lattice)
118- W = (compute_inverse_lattice (lattice) * lattice_primal
119- * symmetry. W * compute_inverse_lattice (lattice_primal) * lattice)
120- w = compute_inverse_lattice (lattice) * lattice_primal * symmetry. w
121-
107+ # 1. Metric invariance: Verify that the perturbed lattice metric
108+ # G = A' * A satisfies W' * G * W = G.
109+ # (This is equivalent to checking that Wcart = A * W * A⁻¹ is orthogonal.)
110+ # 2. Atomic mapping:
111+ # W is an integer matrix, such that any change in u is easily compensated for in v.
112+ # Thus we only need to check that for each atomic position `x`
113+ # `W*x + w = y (mod 1)` is still satisfied for some equivalent atom `y`
114+ # of the same species, up to numerical tolerance.
115+ # Only the dual (perturbation) components are checked,
116+ # the primal part is assumed to already obey the symmetry.
117+
118+ # TODO : work out the case of nested Duals for higher-order derivatives
119+ # (eg. symmetry breaking at higher order)
122120 is_dual_nonzero (x:: AbstractArray ) = any (x) do xi
123121 maximum (abs, ForwardDiff. partials (xi)) >= tol_symmetry
124122 end
125- # Check 1.
126- if is_dual_nonzero (W)
127- return true
123+
124+ W = symmetry. W
125+ w = symmetry. w
126+
127+ # 1) Metric tensor check in fractional frame: Wᵀ G W == G (to first order)
128+ G = lattice' lattice # 3x3 metric tensor on the unit cube
129+ ΔG = W' * G * W - G
130+ if is_dual_nonzero (ΔG)
131+ return true # anisotropic lattice perturbation breaks this symmetry
128132 end
129133
134+ # 2) Atomic mapping check stays purely in reduced coordinates.
130135 atom_groups = [findall (Ref (pot) .== atoms) for pot in Set (atoms)]
131136 for group in atom_groups
132137 positions_group = positions[group]
133- for position in positions_group
134- i_other_at = find_symmetry_preimage (positions_group, position , symmetry; tol_symmetry)
135-
136- # Check 2. with x = positions_group[i_other_at] and y = position
137- if is_dual_nonzero (positions_group[i_other_at] + inv (W) * ( w - position) )
138+ for y in positions_group
139+ i_other = find_symmetry_preimage (positions_group, y , symmetry; tol_symmetry)
140+ x = positions_group[i_other]
141+ # Check that W*x + w - y has zero dual part (primal already ok by construction)
142+ if is_dual_nonzero (W * x + w - y )
138143 return true
139144 end
140145 end
0 commit comments