Skip to content

Commit 31161db

Browse files
authored
Suggest standardize when a nonconventional cell is inferred (#461)
Also accept nonconventional hexagonal and tetragonal cells for lower-symmetry spacegroups (triclinic, monoclinic, and orthorhombic).
1 parent 308aa54 commit 31161db

File tree

9 files changed

+155
-85
lines changed

9 files changed

+155
-85
lines changed

docs/src/versions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
* Introduce [`set_spin_s_at!`](@ref) to set the local quantum spin-``s``
3232
([#454](@ref)).
3333
* Add missing 3D support for [`q_space_grid`](@ref) ([#457](@ref)).
34+
* If user-provided lattice vectors do not match crystallographic conventions,
35+
suggest the use of [`standardize`](@ref) ([#461](@ref)).
3436
* Fixes to [`SpinWaveTheoryKPM`](@ref) ([#462](@ref)).
3537

3638
## v0.7.8

src/Symmetry/AllowedCouplings.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,11 @@ end
136136
# Check whether a coupling matrix J is consistent with symmetries of a bond
137137
function is_coupling_valid(cryst::Crystal, b::BondPos, J)
138138
J isa Number && return true
139-
139+
140140
for (symop, parity) in symmetries_between_bonds(cryst, b, b)
141141
R = cryst.latvecs * symop.R * inv(cryst.latvecs)
142142
J′ = transform_coupling_by_symmetry(J, R*det(R), parity)
143-
# For non-conventional unit cells, the rotation matrices R produced by
143+
# For nonconventional unit cells, the rotation matrices R produced by
144144
# spglib might only be accurate to about 11 digits. If an ambiguous
145145
# situation is detected, throw an informative error.
146146
if !isapprox(J, J′; rtol=1e-10)

src/Symmetry/Crystal.jl

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ end
9090
# representing fractions (between 0 and 1) of the lattice vectors `latvecs`.
9191
# All symmetry information is automatically inferred.
9292
function Crystal(latvecs, positions; types::Union{Nothing, Vector{String}}=nothing, symprec=1e-5)
93-
print_crystal_warnings(latvecs, positions)
93+
if length(positions) >= 100
94+
@warn """Very large crystal cells are not recommended. To model chemical inhomogeneity:
95+
1. Create the `Crystal` with an idealized chemical cell.
96+
2. Create the `System` with large volume dimensions, `dims`.
97+
3. Use `to_inhomogeneous` and related functions to set inhomogeneities."""
98+
end
9499
latvecs = convert(Mat3, latvecs)
95100
positions = [convert(Vec3, p) for p in positions]
96101
if isnothing(types)
@@ -106,21 +111,23 @@ function Crystal(latvecs, positions, symbol::Union{Int, String}; types::Union{No
106111
setting=nothing, choice::Union{Nothing, String}=nothing, symprec=1e-5)
107112
if !isnothing(setting)
108113
if symbol isa Integer
109-
@warn """`setting` argument is deprecated! Omit to get the ITA standard setting.
110-
Alternatively, use `choice` instead."""
114+
@warn "`setting` argument is deprecated! Omit to get the ITA standard setting or use `choice`."
111115
else
112116
@warn "`setting` argument is deprecated! Use `choice` instead."
113117
end
114118
choice = setting
115119
end
116120

117-
print_crystal_warnings(latvecs, positions)
118121
latvecs = Mat3(latvecs)
119122
positions = Vec3.(positions)
120123
if isnothing(types)
121124
types = fill("", length(positions))
122125
end
123126

127+
# Enforce right-handedness convention for aesthetics
128+
det(latvecs) < 0 && error("Lattice vectors are not right-handed.")
129+
130+
# Will report an informative error if latvecs not consistent with symbol
124131
sgt = unique_spacegroup_type(symbol, latvecs; choice)
125132
sg = Spacegroup(Int(sgt.hall_number))
126133
return crystal_from_spacegroup(latvecs, positions, types, sg; symprec)
@@ -148,17 +155,6 @@ function spg_get_dataset_scaled(cell, symprec)
148155
end
149156

150157

151-
function print_crystal_warnings(latvecs, positions)
152-
det(latvecs) < 0 && @warn "Lattice vectors are not right-handed."
153-
if length(positions) >= 100
154-
@info """This a very large crystallographic cell, which Sunny does not handle well. If
155-
the intention is to model chemical inhomogeneity, the recommended steps are:
156-
(1) Create a `Crystal` with idealized chemical cell. (2) Create a `System`
157-
with many cells, as set by the `dims` parameter. (3) Use `to_inhomogeneous`
158-
and related functions to introduce model inhomogeneities."""
159-
end
160-
end
161-
162158
"""
163159
natoms(cryst::Crystal)
164160
@@ -302,29 +298,47 @@ function conventionalize_setting(latvecs::Mat3, setting::SymOp, sgnum::Int)
302298
end
303299

304300
function crystal_from_inferred_symmetry(latvecs::Mat3, positions::Vector{Vec3}, types::Vector{String}; symprec, suppress_warnings=false)
305-
# Print a warning if non-conventional lattice vectors are detected.
306-
try cell_type(latvecs) catch e @warn e.msg end
307-
308301
validate_positions(positions; symprec)
309302

310-
cell = Spglib.Cell(latvecs, positions, types)
311-
d = spg_get_dataset_scaled(cell, symprec)
303+
d = spg_get_dataset_scaled(Spglib.Cell(latvecs, positions, types), symprec)
312304
classes = d.crystallographic_orbits
313305
# classes = d.equivalent_atoms
314306
symops = SymOp.(d.rotations, d.translations)
315307
label = spacegroup_label(Int(d.hall_number))
316308
sgnum = Int(d.spacegroup_number)
309+
cell_std = cell_type(standard_setting[sgnum])
317310
setting = mapping_to_standard_setting_from_spglib_dataset(d)
318311
setting = conventionalize_setting(latvecs, setting, sgnum)
319312

320-
# Spglib-inferred symops are unreliable for large cells. Empty the list to
321-
# prevent faulty symmetry analysis.
322-
if length(positions) > d.n_std_atoms
323-
if !suppress_warnings
324-
types_str = allunique(types) ? "" : " Alternatively, break site symmetry with `types=[...]`."
325-
@warn "This cell is non-standard and missing symmetry information. Consider `standardize`.$types_str"
313+
# Spglib-inferred symops are unreliable for large cells. Empty the list
314+
# to prevent faulty symmetry analysis.
315+
volume_ratio = length(positions) // d.n_std_atoms
316+
volume_ratio > 1 && empty!(symops)
317+
volume_ratio_str = number_to_math_string(volume_ratio)
318+
319+
if !suppress_warnings
320+
if volume_ratio > 1
321+
volume_ratio_str = number_to_math_string(volume_ratio)
322+
types_str = allunique(types) ? "" : " or distinct `types=[...]`"
323+
@error "Symmetry analysis disabled! Cell is $volume_ratio_str times too large. Fix with `standardize`$types_str."
324+
elseif volume_ratio < 1
325+
@info "Cell is $volume_ratio_str the standard size for spacegroup $sgnum. Consider `standardize`."
326+
elseif cell_std in (tetragonal, hexagonal, cubic) && setting.R I
327+
# For a tetragonal, hexagonal, trigonal or cubic space group, the
328+
# provided cell should already be Spglib-idealized.
329+
@info "Nonstandard $cell_std cell for spacegroup $sgnum. Consider `standardize`."
330+
elseif !(cell_type(latvecs) in all_compatible_cells(cell_std))
331+
# For triclinic, monoclinic, and orthorhombic spacegroups, check
332+
# that the provided cell is symmetry-consistent (not necessarily
333+
# Spglib-idealized). For orthorhombic, arbitrary axis ordering is
334+
# acceptable. For monoclinic, arbitrary β magnitude is acceptable,
335+
# whereas Spglib finds a cell with 90 < β < 120. For triclinic,
336+
# Spglib's idealization algorithm is even more complex, involving
337+
# Niggli reduction.
338+
@info "Nonstandard $cell_std cell for spacegroup $sgnum. Consider `standardize`."
339+
elseif det(latvecs) < 0
340+
@info "Lattice vectors are not right-handed. Consider `standardize`."
326341
end
327-
symops = SymOp[]
328342
end
329343

330344
sg = Spacegroup(symops, label, sgnum, setting)
@@ -511,7 +525,7 @@ end
511525

512526
# Indices of atoms that wrap into unique positions within the primitive cell.
513527
# The choice is ambiguous because the un-wrapped positions are not restricted to
514-
# `latvecs * primitive_cell()`.
528+
# the parallelpiped spanned by `latvecs * primitive_cell()`.
515529
function primitive_atoms_arbitrary(cryst::Crystal)
516530
P = primitive_cell(cryst)
517531
P I && return collect(1:natoms(cryst))

src/Symmetry/LatticeUtils.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,21 @@ end
6767
# spacegroups are characterized by a 3-fold rotational symmetry. All trigonal
6868
# spacegroups (143-167) admit a hexagonal setting. Some of these (146, 148, 155,
6969
# 160, 161, 166, 167) additionally admit a rhombohedral setting.
70+
#
71+
# The "alt" suffix indicates deviation from the ITA lattice vector conventions.
7072
@enum CellType begin
7173
triclinic
7274
monoclinic
7375
orthorhombic
7476
tetragonal
77+
tetragonal_alt
7578
rhombohedral
7679
hexagonal
80+
hexagonal_alt
7781
cubic
7882
end
7983

80-
# Infer the CellType (lattice system) from lattice vectors. Report an error if
81-
# the unit cell is not in conventional form, which would invalidate the table of
82-
# symops for a given Hall number.
84+
# Infer the CellType (lattice system) from lattice vectors.
8385
function cell_type(latvecs)
8486
a, b, c, α, β, γ = lattice_params(latvecs)
8587

@@ -95,7 +97,7 @@ function cell_type(latvecs)
9597
if a b
9698
return tetragonal
9799
elseif b c || c a
98-
error("Found a nonconventional tetragonal unit cell. Consider using `lattice_vectors(a, a, c, 90, 90, 90)`.")
100+
return tetragonal_alt # nonconventional
99101
else
100102
return orthorhombic
101103
end
@@ -107,7 +109,7 @@ function cell_type(latvecs)
107109
if γ 120
108110
return hexagonal
109111
else
110-
error("Found a nonconventional hexagonal unit cell. Consider using `lattice_vectors(a, a, c, 90, 90, 120)`.")
112+
return hexagonal_alt # nonconventional
111113
end
112114
end
113115

@@ -166,11 +168,11 @@ end
166168

167169
function all_compatible_cells(cell::CellType)
168170
if cell == triclinic
169-
[triclinic, monoclinic, orthorhombic, tetragonal, rhombohedral, hexagonal, cubic]
171+
[triclinic, monoclinic, orthorhombic, tetragonal, tetragonal_alt, rhombohedral, hexagonal, hexagonal_alt, cubic]
170172
elseif cell == monoclinic
171-
[monoclinic, orthorhombic, tetragonal, hexagonal, cubic]
173+
[monoclinic, orthorhombic, tetragonal, tetragonal_alt, hexagonal, hexagonal_alt, cubic]
172174
elseif cell == orthorhombic
173-
[orthorhombic, tetragonal, cubic]
175+
[orthorhombic, tetragonal, tetragonal_alt, cubic]
174176
elseif cell == tetragonal
175177
[tetragonal, cubic]
176178
elseif cell == rhombohedral

src/Symmetry/SpacegroupData.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function suggest_alternative_symbols(sgts)
4040
end
4141

4242
# Get the single SpacegroupType associated with symbol that is valid for
43-
# latvecs. Throw an informative error if the setting is ambiguous.
43+
# latvecs. Throw an informative error if the setting is invalid or ambiguous.
4444
function unique_spacegroup_type(symbol, latvecs; choice=nothing)
4545
# All settings for symbol
4646
sgts = all_spacegroup_types_for_symbol(symbol)
@@ -62,9 +62,15 @@ function unique_spacegroup_type(symbol, latvecs; choice=nothing)
6262
sgts = filter(sgts) do sgt
6363
return cell in all_compatible_cells(cell_type(sgt))
6464
end
65-
if isempty(sgts)
66-
expected = join(string.(allowed_cells), " or ")
67-
error("Expected $expected lattice system but got $cell")
65+
66+
if cell == tetragonal_alt && cell_type(hall_std) == tetragonal
67+
error("Use a conventional tetragonal cell: `lattice_vectors(a, a, c, 90, 90, 90)`")
68+
elseif cell == hexagonal_alt && cell_type(hall_std) == hexagonal
69+
error("Use a conventional hexagonal cell: `lattice_vectors(a, a, c, 90, 90, 120)`")
70+
elseif isempty(sgts)
71+
allowed_str = join(string.(allowed_cells), " or ")
72+
received = only(replace([cell], hexagonal_alt => hexagonal, tetragonal_alt => tetragonal))
73+
error("Expected $allowed_str cell but got $received")
6874
end
6975

7076
# For monoclinic lattice systems, filter by axis setting

test/test_correlation_sampling.jl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
@testitem "Correlation sampling" begin
22
using LinearAlgebra
33

4+
# FCC with nonstandard, primitive lattice vectors
5+
latvecs = [[1, 1, 0] [0, 1, 1] [1, 0, 1]] / 2
6+
positions = [[0, 0, 0]]
7+
msg = "Cell is 1/4 the standard size for spacegroup 225. Consider `standardize`."
8+
cryst = @test_logs (:info, msg) Crystal(latvecs, positions)
9+
410
function simple_model_fcc(; mode, seed=111)
5-
dims = (4, 4, 4)
611
J = 1.0
7-
8-
# FCC with nonstandard, primitive lattice vectors
9-
latvecs = [[1, 1, 0] [0, 1, 1] [1, 0, 1]] / 2
10-
positions = [[0, 0, 0]]
11-
cryst = Crystal(latvecs, positions)
12-
1312
s = mode==:SUN ? 1/2 : 1
1413
κ = mode==:SUN ? 2 : 1
15-
sys = System(cryst, [1 => Moment(; s, g=2)], mode; dims, seed)
14+
sys = System(cryst, [1 => Moment(; s, g=2)], mode; dims=(4, 4, 4), seed)
1615
sys.κs .= κ
1716
set_exchange!(sys, J, Bond(1, 1, [1, 0, 0]))
1817
return sys

test/test_lswt.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
@testitem "Kitchen Sink" begin
1+
@testitem "Kitchen sink" begin
22
using LinearAlgebra
33

44
# Pyrochlore with nonstandard, primitive lattice vectors
55
latvecs = [[1, 1, 0] [1, 0, 1] [0, 1, 1]] / 2
66
positions = [[5, 5, 1], [5, 1, 5], [1, 5, 5], [5, 5, 5]] / 8
77

8-
cryst = @test_logs (:warn, "Lattice vectors are not right-handed.") Crystal(latvecs, positions)
8+
msg = "Cell is 1/4 the standard size for spacegroup 227. Consider `standardize`."
9+
cryst = @test_logs (:info, msg) Crystal(latvecs, positions)
910
natoms = Sunny.natoms(cryst)
1011

1112
moments = [1 => Moment(s=5/2, g=7.2)]
@@ -192,7 +193,8 @@ end
192193
using LinearAlgebra
193194

194195
latvecs = lattice_vectors(1, 1, 1, 90, 90, 90)
195-
cryst = Crystal(latvecs, [[0,0,0], [0.4,0,0]]; types=["A", "B"])
196+
msg = "Nonstandard tetragonal cell for spacegroup 99. Consider `standardize`."
197+
cryst = @test_logs (:info, msg) Crystal(latvecs, [[0,0,0], [0.4,0,0]]; types=["A", "B"])
196198

197199
sys = System(cryst, [1 => Moment(s=1, g=2), 2 => Moment(s=2, g=2)], :dipole)
198200
set_pair_coupling!(sys, (S1, S2) -> +(S1'*diagm([2,-1,-1])*S1)*(S2'*diagm([2,-1,-1])*S2), Bond(1, 2, [0,0,0]))

test/test_rescaling.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ end
110110

111111
@testitem "Anisotropy SU(N) equivalence" begin
112112
latvecs = lattice_vectors(1.0, 1.1, 1.0, 90, 90, 90)
113-
msg = "Found a nonconventional tetragonal unit cell. Consider using `lattice_vectors(a, a, c, 90, 90, 90)`."
114-
cryst = @test_logs (:warn, msg) Crystal(latvecs, [[0, 0, 0]])
113+
msg = "Nonstandard tetragonal cell for spacegroup 123. Consider `standardize`."
114+
cryst = @test_logs (:info, msg) Crystal(latvecs, [[0, 0, 0]])
115115

116116
# Dipole system with renormalized anisotropy
117117
sys0 = System(cryst, [1 => Moment(s=3, g=2)], :dipole)
@@ -169,7 +169,8 @@ end
169169
@testitem "Biquadratic renormalization 2" begin
170170
# Simple dimer model
171171
latvecs = lattice_vectors(1, 1, 1, 90, 90, 90)
172-
cryst = Crystal(latvecs, [[0, 0, 0], [0.3, 0, 0]]; types=["A", "B"])
172+
msg = "Nonstandard tetragonal cell for spacegroup 99. Consider `standardize`."
173+
cryst = @test_logs (:info, msg) Crystal(latvecs, [[0, 0, 0], [0.3, 0, 0]]; types=["A", "B"])
173174
s1 = 3/2
174175
s2 = 2
175176
v1 = randn(3)

0 commit comments

Comments
 (0)