Skip to content

Commit f2f0eb1

Browse files
committed
Finalize overhaul over group loading and add magnetic space group functionality
- Bump Crystalline to v0.5.0 Among many updates, this includes: - Now, load all group operation data by reference to a fixed set of operators (in `tables/rotation_translation.jl` via a set of codes (stored in `tables/spacegroup.jl` and similar for point/subperiodic/magnetic groups): this is much faster computationally as it avoids having to load anything from disk and solves #39 - Introduce a new `AbstractOperation` abstract type to fold both `SymOperation` and `MSymOperation` (for magnetic operations) under one umbrella - All assembly of groups is now in `group-assembly/...` - Various minor fixes/code-improvement to subperiodic groups - Add an element type parameter to `AbstractGroup` - Fix various minor things
1 parent 410780e commit f2f0eb1

File tree

492 files changed

+8251
-322
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

492 files changed

+8251
-322
lines changed

Project.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Crystalline"
22
uuid = "ae5e2be0-a263-11e9-351e-f94dad1eb351"
33
authors = ["Thomas Christensen <[email protected]>"]
4-
version = "0.4.22"
4+
version = "0.5.0"
55

66
[deps]
77
Bravais = "ada6cbde-b013-4edf-aa94-f6abe8bd6e6b"
@@ -29,7 +29,7 @@ CrystallineGraphMakieExt = "GraphMakie"
2929
CrystallinePyPlotExt = "PyPlot"
3030

3131
[compat]
32-
Bravais = "0.1.8"
32+
Bravais = "0.1.9"
3333
Combinatorics = "1.0"
3434
DelimitedFiles = "1"
3535
DocStringExtensions = "0.8, 0.9"

build/crawl_and_write_pgs.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ function __crawl_and_write_3d_pgirreps()
180180

181181
# ==== save irreps ====
182182
# we do not save the point group operations anew; they are already stored in
183-
# "data/operations/pgs/..."; note that we explicitly checked the sorting and
184-
# equivalence of operations when pgirs was crawled above (cf. flag
183+
# "test/data/xyzt-operations/pgs/..."; note that we explicitly checked the
184+
# sorting and # equivalence of operations when pgirs was crawled above (cf. flag
185185
# `consistency_checks=true`)
186186
unmangled_pgiuc = Crystalline.unmangle_pgiuclab(pgiuc) # replace '/'s by '_slash_'s
187187
irreps_file[unmangled_pgiuc*"/matrices"] = matrices

build/crawl_and_write_subperiodic.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,16 @@ end
5353

5454
# ---------------------------------------------------------------------------------------- #
5555
## Use crawling functions & write information to:
56-
# `/data/generators/subperiodic/{layer|rod|frieze}/`
57-
# `/data/operations/subperiodic/{layer|rod|frieze}/`
56+
# `/test/data/xyzt-operations/generators/subperiodic/{layer|rod|frieze}/`
57+
# `/test/data/xyzt-operations/subperiodic/{layer|rod|frieze}/`
5858
for (D, P, sub, maxnum) in [(3,2,"layer",80), (3,1,"rod",75), (2,1,"frieze",7)]
5959
println(sub)
6060
for kind in ["operations", "generators"]
6161
println(" ", kind)
6262
for num in 1:maxnum
6363
println(" ", num)
6464
gens_str = crawl_subperiodic_xyzt(num, D, P, kind)
65-
filename = (@__DIR__)*"/../data/$kind/subperiodic/$sub/"*string(num)*".csv"
65+
filename = (@__DIR__)*"/../test/data/xyzt-$kind/subperiodic/$sub/"*string(num)*".csv"
6666
open(filename; write=true, create=true, truncate=true) do io
6767
first = true
6868
for str in gens_str

build/parse_and_write_magnetic_space_groups.jl

+3-30
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,8 @@
1-
#module ParseMSG
2-
31
using Crystalline
2+
using StaticArrays
43
# --- definitions ---
54
# to be transferred to Crystalline's definitions
6-
using Crystalline: AbstractGroup
7-
using StaticArrays
85

9-
struct MSymOperation{D}
10-
op :: SymOperation{D}
11-
tr :: Bool
12-
end
13-
Base.show(io::IO, mop::MSymOperation) = (show(io, mop.op); print(io, mop.tr ? "" : ""))
14-
function Crystalline.compose(
15-
mop₁::MSymOperation{D},
16-
mop₂::MSymOperation{D},
17-
modτ::Bool=true) where D
18-
return MSymOperation{D}(compose(mop₁.op, mop₂.op, modτ), xor(mop₁.tr, mop₂.tr))
19-
end
20-
Base.:*(mop₁::MSymOperation{D}, mop₂::MSymOperation{D}) where D = compose(mop₁, mop₂)
21-
function Base.isapprox(mop₁::MSymOperation{D}, mop₂::MSymOperation{D}, vs...; kws...) where D
22-
isapprox(mop₁.op, mop₂.op, vs...; kws...) && mop₁.tr == mop₂.tr
23-
end
24-
struct MSpaceGroup{D} <: AbstractGroup{D}
25-
# Note: we use BNS numbers, not OG numbers (see Sec. 4, Acta Cryst. A78, 99 (2022))
26-
# BNS number: `num[1]`: F space-group associated number
27-
# `num[2]`: crystal-system sequential number
28-
num :: Tuple{Int, Int}
29-
operations :: Vector{MSymOperation{D}}
30-
end
31-
Crystalline.label(msg::MSpaceGroup) = MSG_BNS_LABELs_D[msg.num]::String
326
# --- parsing ---
337
#cd(@__DIR__)
348
io = open((@__DIR__)*"/../data/operations/msgs/iso-magnetic_table_bns.txt")
@@ -69,6 +43,7 @@ function read_group!(io)
6943
readuntil(io, "BNS: ")
7044
num_str = readuntil(io, " ") # numbering in format `sgnum.cnum`
7145
sgnum, cnum = parse.(Int, split(num_str, "."))::Vector{Int}
46+
# `sgnum`: F space-group associated number, `cnum`: crystal-system sequential number
7247
bns_label = replace(readuntil(io, " "), '\''=>'') # BNS label in IUC-style
7348

7449
# OG: number and label
@@ -109,6 +84,4 @@ for i in 1:1651
10984
MSG_BNS_LABELs_D[msg.num] = bns_label
11085
MSG_OG_LABELs_D[(N₁, N₂, N₃)] = og_label
11186
MSG_BNS2OG_NUMs_D[msg.num] = (N₁, N₂, N₃)
112-
end
113-
114-
#end # module ParseMSG
87+
end

src/Crystalline.jl

+26-11
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ export smith, Smith # export, so that loading Crystalline effectively also provi
3838

3939
@reexport using Bravais
4040
import Bravais: primitivize, conventionalize, cartesianize, transform, centering
41-
using Bravais: stack, all_centeringtranslations, centeringtranslation
41+
using Bravais: stack, all_centeringtranslations, centeringtranslation,
42+
centering_volume_fraction
4243

4344
# included files and exports
4445
include("constants.jl")
45-
export MAX_SGNUM, MAX_SUBGNUM, ENANTIOMORPHIC_PAIRS
46+
export MAX_SGNUM, MAX_SUBGNUM, MAX_MSGNUM, MAX_MSUBGNUM, ENANTIOMORPHIC_PAIRS
4647

4748
include("utils.jl") # useful utility methods (seldom needs exporting)
4849
export splice_kvpath, interpolate_kvpath
@@ -71,17 +72,35 @@ export SymOperation, # types
7172
irreplabels, klabels, # ::BandRep & ::BandRepSet
7273
isspinful
7374

74-
include("show.jl") # custom printing for structs defined in src/types.jl
75-
7675
include("notation.jl")
7776
export schoenflies, iuc, centering, seitz, mulliken
7877

78+
include("subperiodic.jl")
79+
export SubperiodicGroup
80+
81+
include("magnetic/notation-data.jl")
82+
include("magnetic/types.jl")
83+
export MSymOperation, MSpaceGroup
84+
85+
include("tables/rotation_translation.jl")
86+
include("tables/pointgroup.jl")
87+
include("tables/spacegroup.jl")
88+
include("tables/subperiodicgroup.jl")
89+
include("tables/mspacegroup.jl")
90+
export pointgroup, spacegroup, subperiodicgroup, mspacegroup
91+
92+
include("group-assembly/assemble_pointgroup.jl")
93+
include("group-assembly/assemble_spacegroup.jl")
94+
include("group-assembly/assemble_subperiodicgroup.jl")
95+
include("group-assembly/assemble_mspacegroup.jl")
96+
97+
include("show.jl") # custom printing for structs defined in src/types.jl
98+
7999
include("orders.jl")
80100

81101
include("symops.jl") # symmetry operations for space, plane, and line groups
82-
export @S_str, spacegroup, compose,
102+
export @S_str, compose,
83103
issymmorph, littlegroup, orbit,
84-
pointgroup,
85104
reduce_ops,
86105
issubgroup, isnormal,
87106
generate, generators
@@ -99,8 +118,7 @@ include("symeigs2irrep.jl") # find irrep multiplicities from symmetry eigenvalue
99118
export find_representation
100119

101120
include("pointgroup.jl") # symmetry operations for crystallographic point groups
102-
export pointgroup, pgirreps,
103-
PG_IUCs, find_isomorphic_parent_pointgroup
121+
export pgirreps, PG_IUCs, find_isomorphic_parent_pointgroup
104122

105123
include("irreps_reality.jl")
106124
export calc_reality, realify
@@ -133,9 +151,6 @@ export bandreps, matrix, classification, nontrivial_factors, basisdim
133151
include("deprecations.jl")
134152
export get_littlegroups, get_lgirreps, get_pgirreps, WyckPos, kvec, wyck, kstar
135153

136-
include("subperiodic.jl")
137-
export SubperiodicGroup, subperiodicgroup
138-
139154
include("grouprelations/grouprelations.jl")
140155
export maximal_subgroups, minimal_supergroups
141156

src/bandrep.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ end
253253
function matching_lgirreps(BRS::BandRepSet)
254254
lgirsd = lgirreps(num(BRS), Val(3))
255255
# create "physical/real" irreps if `BRS` assumes time-reversal symmetry
256-
if BRS.timeinvar
256+
if BRS.timereversal
257257
for (klab, lgirs) in lgirsd
258258
lgirsd[klab] = realify(lgirs)
259259
end

src/conjugacy.jl

+1-5
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,7 @@ function classes(
3434
cntr_ops = if cntr === nothing
3535
nothing
3636
else
37-
if cntr == 'P' || cntr == 'p'
38-
SymOperation{D}[]
39-
else
40-
SymOperation{D}.(all_centeringtranslations(cntr, Val(D)))
41-
end
37+
SymOperation{D}.(all_centeringtranslations(cntr, Val(D)))
4238
end
4339

4440
conj_classes = Vector{Vector{SymOperation{D}}}()

src/constants.jl

+16-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const NULL_ATOL = 1e-11 # absolute tolerance for nullspace
88
Return the number of distinct space group types across dimensions 1, 2, and 3 (indexable by
99
dimensionality).
1010
"""
11-
const MAX_SGNUM = (2, 17, 230) # number of space groups in dimensions 1, 2, and 3
11+
const MAX_SGNUM = (2, 17, 230)
1212

1313
@doc raw"""
1414
MAX_SUBGNUM :: ImmutableDict
@@ -23,6 +23,21 @@ subperiodic group of dimensionality `D` and periodicity `P`:
2323
"""
2424
const MAX_SUBGNUM = ImmutableDict((3,2)=>80, (3,1)=>75, (2,1)=>7)
2525

26+
@doc raw"""
27+
MAX_MSGNUM :: Tuple{Int,Int,Int}
28+
29+
Analogous to `MAX_SUBGNUM`, but for the number of magnetic space groups.
30+
"""
31+
const MAX_MSGNUM = (7, 80, 1651) # Figure 1.2.1 of Litvin's book
32+
33+
34+
@doc raw"""
35+
MAX_MSUBGNUM :: Tuple{Int,Int,Int}
36+
37+
Analogous to `MAX_SUBGNUM`, but for the number of magnetic subperiodic groups.
38+
"""
39+
const MAX_MSUBGNUM = ImmutableDict((3,2)=>528, (3,1)=>394, (2,1)=>31) # Litvin, Fig. 1.2.1
40+
2641
# --- VECTORS ---
2742
# arbitrary test vector for e.g. evaluating KVecs lines/planes/volumes; 3D by default
2843
const TEST_αβγ = [0.123123123, 0.456456456, 0.789789789]
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
function mspacegroup(BNS₁::Integer, BNS₂::Integer, Dᵛ::Val{D}=Val(3)) where D
2+
D == 3 || throw(DomainError("only 3D magnetic space groups are supported"))
3+
msgnum = (BNS₁, BNS₂)
4+
codes = MSG_CODES_D[msgnum]
5+
6+
label = MSG_BNS_LABELs_D[msgnum]
7+
cntr = first(label)
8+
Ncntr = centering_volume_fraction(cntr, Dᵛ)
9+
Nop = (length(codes)+1) # number of operations ÷ by equiv. centering translations
10+
operations = Vector{MSymOperation{D}}(undef, Nop * Ncntr)
11+
12+
operations[1] = MSymOperation{D}(one(SymOperation{D}), false)
13+
for (n, code) in enumerate(codes)
14+
op = SymOperation{D}(ROTATIONS_3D[code[1]], TRANSLATIONS_3D[code[2]])
15+
operations[n+1] = MSymOperation{D}(op, code[3])
16+
end
17+
18+
# add the centering-translation related operations (not included in `codes`)
19+
if Ncntr > 1
20+
cntr_translations = all_centeringtranslations(cntr, Dᵛ)
21+
for (i, t) in enumerate(cntr_translations)
22+
for n in 1:Nop
23+
mop = operations[n]
24+
t′ = reduce_translation_to_unitrange(translation(mop) + t)
25+
op′ = SymOperation{D}(mop.op.rotation, t′)
26+
mop′ = MSymOperation{D}(op′, mop.tr)
27+
operations[n+i*Nop] = mop′
28+
end
29+
end
30+
end
31+
32+
return MSpaceGroup{D}(msgnum, operations)
33+
end
34+
35+
function mspacegroup(OG₃::Integer, ::Val{D}=Val(3)) where D
36+
D == 3 || throw(DomainError("only 3-dimensional magnetic space groups are supported"))
37+
OG₃ 1:MAX_MSGNUM[D] || _throw_invalid_msgnum(OG₃, D)
38+
39+
BNS₁, BNS₂ = MSG_OG₃2BNS_NUMs_V[OG₃]
40+
return mspacegroup(BNS₁, BNS₂, Val(3))
41+
end
42+
@noinline function _throw_invalid_msgnum(OG₃, D)
43+
throw(DomainError(OG₃, "the sequential magnetic space group number must be between 1 and $(MAX_MSGNUM[D]) in dimension $D"))
44+
end
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
pointgroup(iuclab::String, ::Union{Val{D}, Integer}=Val(3)) --> PointGroup{D}
3+
4+
Return the symmetry operations associated with the point group identified with label
5+
`iuclab` in dimension `D` as a `PointGroup{D}`.
6+
"""
7+
function pointgroup(iuclab::AbstractString, Dᵛ::Val{D}=Val(3)) where D
8+
@boundscheck _check_valid_pointgroup_label(iuclab, D)
9+
pgnum = pointgroup_iuc2num(iuclab, D) # this is not generally a particularly well-established numbering
10+
return _pointgroup(iuclab, pgnum, Dᵛ)
11+
end
12+
@inline pointgroup(iuclab::String, D::Integer) = pointgroup(iuclab, Val(D))
13+
14+
"""
15+
pointgroup(pgnum::Integer, ::Union{Val{D}, Integer}=Val(3), setting::Integer=1)
16+
--> PointGroup{D}
17+
18+
Return the symmetry operations associated with the point group identfied with canonical
19+
number `pgnum` in dimension `D` as a `PointGroup{D}`. The connection between a point group's
20+
numbering and its IUC label is enumerated in `Crystalline.PG_NUM2IUC[D]` and
21+
`Crystalline.IUC2NUM[D]`.
22+
23+
Certain point groups feature in multiple setting variants: e.g., IUC labels 321 and 312 both
24+
correspond to `pgnum = 18` and correspond to the same group structure expressed in two
25+
different settings. The `setting` argument allows choosing between these setting variations.
26+
"""
27+
function pointgroup(pgnum::Integer, Dᵛ::Val{D}=Val(3), setting::Integer=1) where D
28+
iuclab = pointgroup_num2iuc(pgnum, Dᵛ, setting) # also checks validity of `(pgnum, D)`
29+
return _pointgroup(iuclab, pgnum, Dᵛ)
30+
end
31+
@inline function pointgroup(pgnum::Integer, D::Integer, setting::Integer=1)
32+
return pointgroup(pgnum, Val(D), setting)
33+
end
34+
35+
function _pointgroup(iuclab::String, pgnum::Integer, Dᵛ::Val{D}) where D
36+
codes = D == 3 ? PG_CODES_3D_D[iuclab] :
37+
D == 2 ? PG_CODES_2D_D[iuclab] :
38+
PG_CODES_1D_D[iuclab]
39+
40+
Nop = (length(codes)+1) # number of operations
41+
operations = Vector{SymOperation{D}}(undef, Nop)
42+
operations[1] = one(SymOperation{D})
43+
for (n, code) in enumerate(codes)
44+
op = SymOperation{D}(get_indexed_rotation(code[1], Dᵛ),
45+
zero(SVector{D,Float64}))
46+
operations[n+1] = op
47+
end
48+
49+
return PointGroup{D}(pgnum, iuclab, operations)
50+
end
51+
52+
function _check_valid_pointgroup_label(iuclab::AbstractString, D)
53+
D (1,2,3) && _throw_invalid_dim(D)
54+
if iuclab PG_IUCs[D]
55+
throw(DomainError(iuclab,
56+
"iuc label not found in database (see possible labels in `PG_IUCs[D]`)"))
57+
end
58+
end

0 commit comments

Comments
 (0)