diff --git a/Sources/DiffractoOrientation/Misorientation.swift b/Sources/DiffractoOrientation/Misorientation.swift index 5a952af..0b4cfb9 100644 --- a/Sources/DiffractoOrientation/Misorientation.swift +++ b/Sources/DiffractoOrientation/Misorientation.swift @@ -147,6 +147,75 @@ public struct Orientation: Sendable { self.matrix = matrix; self.symmetry = symmetry } + // MARK: orix from_euler, dot, dot_outer, get_distance_matrix + + /// Which way Bunge Euler angles turn (orix `from_euler(direction=)`): `labToCrystal` is the Bunge convention and orix's default, + /// `crystalToLab` the inverse (MTEX's). + public enum EulerDirection: Sendable, Equatable { case labToCrystal, crystalToLab } + + /// orix `Orientation.from_euler(euler, symmetry, direction)`: Bunge (phi1, Phi, phi2) in radians, through orix's `eu2qu` + /// (Rowenhorst 2015, Eqs. A.5 and A.6), the inverse of `inEulerFundamentalRegion()`'s conversion. Throws `invalidRotation` for a + /// non-finite angle. + public init(euler: SIMD3, symmetry: CrystalOrientationSymmetry, direction: EulerDirection = .labToCrystal) throws { + guard euler.x.isFinite, euler.y.isFinite, euler.z.isFinite else { throw OrientationSymmetryError.invalidRotation } + let sigma = 0.5 * (euler.x + euler.z), delta = 0.5 * (euler.x - euler.z) + let c = cos(euler.y / 2), s = sin(euler.y / 2) + var q = simd_quatd(real: c * cos(sigma), imag: SIMD3(-s * cos(delta), -s * sin(delta), -c * sin(sigma))) + if direction == .crystalToLab { q = q.conjugate } + self.init(unchecked: simd_double3x3(q), symmetry: symmetry) + } + + /// orix `Orientation.dot(other)`: the symmetry-reduced dot product, the largest `||` over the proper + /// operators s, in [0, 1] (the cosine of half the disorientation angle). Throws `differentSymmetry` unless both share a group + /// (as `angle(with:)`; orix takes the product of the two groups, which this kit does not hold). + public func dot(_ other: Orientation) throws -> Double { + guard symmetry.sameGroup(as: other.symmetry) else { throw OrientationSymmetryError.differentSymmetry } + return Orientation.reducedDot(simd_quatd(matrix), simd_quatd(other.matrix), Orientation.operatorQuaternions(symmetry)) + } + + /// orix `Orientation.dot_outer(other)`: `result[i][j] = a[i].dot(b[j])`. Throws `differentSymmetry` unless every orientation of + /// both lists shares one group. + public static func dotOuter(_ a: [Orientation], _ b: [Orientation]) throws -> [[Double]] { + guard let ops = try sharedOperators(a + b) else { return a.map { _ in [] } } + let qa = a.map { simd_quatd($0.matrix) }, qb = b.map { simd_quatd($0.matrix) } + return qa.map { x in qb.map { y in reducedDot(x, y, ops) } } + } + + /// orix `Orientation.get_distance_matrix()`: the disorientation angle (radians) between every pair, computed as orix does, + /// `arccos(2 d^2 - 1)` with the dot product d rounded to 12 decimals and clamped to 1 (so the diagonal is exactly 0). Throws + /// `differentSymmetry` unless every orientation shares one group. + public static func distanceMatrix(_ orientations: [Orientation]) throws -> [[Double]] { + guard let ops = try sharedOperators(orientations) else { return [] } + let q = orientations.map { simd_quatd($0.matrix) } + return q.map { x in + q.map { y in + let d = min((reducedDot(x, y, ops) * 1e12).rounded() / 1e12, 1) + return acos(2 * d * d - 1) + } + } + } + + /// The proper operators as quaternions when every orientation shares a group; nil for an empty list. + private static func sharedOperators(_ orientations: [Orientation]) throws -> [simd_quatd]? { + guard let first = orientations.first else { return nil } + guard orientations.allSatisfy({ $0.symmetry.sameGroup(as: first.symmetry) }) else { + throw OrientationSymmetryError.differentSymmetry + } + return operatorQuaternions(first.symmetry) + } + + private static func operatorQuaternions(_ symmetry: CrystalOrientationSymmetry) -> [simd_quatd] { + symmetry.operators.map { simd_quatd($0) } + } + + /// orix's kernel: M = b * conj(a), the largest |M . s| over the operators. + private static func reducedDot(_ a: simd_quatd, _ b: simd_quatd, _ ops: [simd_quatd]) -> Double { + let m = (b * a.conjugate).vector + var best = 0.0 + for s in ops { best = max(best, abs(simd_dot(m, s.vector))) } + return best + } + // MARK: Euler fundamental region /// The proper subgroup's name in orix's convention for the kit's standard setting ("121" for 2/m with b unique, "312" for -31m, "32" for -3m1). diff --git a/Tests/DiffractoOrientationTests/Fixtures/orix-orientation-api-pins.json b/Tests/DiffractoOrientationTests/Fixtures/orix-orientation-api-pins.json new file mode 100644 index 0000000..cda2e01 --- /dev/null +++ b/Tests/DiffractoOrientationTests/Fixtures/orix-orientation-api-pins.json @@ -0,0 +1 @@ +{"euler":[{"crystal2lab":[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]],"euler":[0.0,0.0,0.0],"lab2crystal":[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]]},{"crystal2lab":[[0.7648421872844884,-0.644217687237691,0.0],[0.644217687237691,0.7648421872844884,-0.0],[0.0,0.0,1.0]],"euler":[0.3,0.0,0.4],"lab2crystal":[[0.7648421872844884,0.644217687237691,0.0],[-0.644217687237691,0.7648421872844884,0.0],[0.0,-0.0,1.0]]},{"crystal2lab":[[0.9950041652780259,-0.09983341664682818,3.619078751711739e-17],[-0.09983341664682818,-0.9950041652780257,-1.1699497735163436e-16],[4.768999264375572e-17,1.1279743981251125e-16,-1.0]],"euler":[0.3,3.141592653589793,0.4],"lab2crystal":[[0.9950041652780259,-0.09983341664682818,4.768999264375572e-17],[-0.09983341664682818,-0.9950041652780257,1.1279743981251125e-16],[3.619078751711739e-17,-1.1699497735163436e-16,-1.0]]},{"crystal2lab":[[1.0,1.2246467991473535e-16,1.2246467991473532e-16],[-1.2246467991473535e-16,1.7938038903913487e-16,1.0],[1.2246467991473532e-16,-1.0,1.793803890391349e-16]],"euler":[3.141592653589793,1.5707963267948966,3.141592653589793],"lab2crystal":[[1.0,-1.2246467991473535e-16,1.2246467991473532e-16],[1.2246467991473535e-16,1.7938038903913487e-16,-1.0],[1.2246467991473532e-16,1.0,1.793803890391349e-16]]},{"crystal2lab":[[0.9999999999995001,-4.999993800710255e-10,-9.9999966666729e-07],[-4.999998699296226e-10,0.9999995000005418,-0.0009999993333334666],[9.999996666670452e-07,0.0009999993333334666,0.9999995000000418]],"euler":[6.282185307179586,0.001,0.001],"lab2crystal":[[0.9999999999995001,-4.999998699296226e-10,9.999996666670452e-07],[-4.999993800710255e-10,0.9999995000005418,0.0009999993333334666],[-9.9999966666729e-07,-0.0009999993333334666,0.9999995000000418]]},{"crystal2lab":[[0.13583298507444885,0.6293638490969854,0.7651474012342926],[0.7549623346639976,0.43435078959577333,-0.491295496433882],[-0.641546002562911,0.6443916022321795,-0.41614683654714213]],"euler":[1.0,2.0,5.5],"lab2crystal":[[0.13583298507444885,0.7549623346639976,-0.641546002562911],[0.6293638490969854,0.43435078959577333,0.6443916022321795],[0.7651474012342926,-0.491295496433882,-0.41614683654714213]]},{"crystal2lab":[[-0.30251052580783167,0.559985814100318,0.7712997275909497],[0.6494280222888573,-0.4712134303686163,0.5968258932940363],[0.6976608242129212,0.6814497714774327,-0.22112345717246318]],"euler":[2.229351519644009,1.793762620464542,0.7971523375753231],"lab2crystal":[[-0.30251052580783167,0.6494280222888573,0.6976608242129212],[0.559985814100318,-0.4712134303686163,0.6814497714774327],[0.7712997275909497,0.5968258932940363,-0.22112345717246318]]},{"crystal2lab":[[0.9451118897565489,-0.012531527748897745,-0.32650647260519006],[0.032791605636186535,-0.9905816979253665,0.13293837043945872],[-0.32509725689492663,-0.1363483059945247,-0.9357996115685332]],"euler":[4.3257307366218205,2.7813167988932523,4.315264545980859],"lab2crystal":[[0.9451118897565489,0.032791605636186535,-0.32509725689492663],[-0.012531527748897745,-0.9905816979253665,-0.1363483059945247],[-0.32650647260519006,0.13293837043945872,-0.9357996115685332]]},{"crystal2lab":[[-0.5456966180765118,-0.7894094326875818,0.28115467025060786],[-0.33804385026619016,0.5143776654140358,0.7881256071340507],[-0.7667734713180077,0.3350348711807124,-0.5475491564926899]],"euler":[2.798927460750888,2.15022882736426,5.124330245966773],"lab2crystal":[[-0.5456966180765118,-0.33804385026619016,-0.7667734713180077],[-0.7894094326875818,0.5143776654140358,0.3350348711807124],[0.28115467025060786,0.7881256071340507,-0.5475491564926899]]},{"crystal2lab":[[0.43223768224706816,-0.7642587471034007,-0.47862214065128966],[-0.8280898896880168,-0.5465187115449087,0.124837624648105],[-0.35698420223053423,0.3423826301190425,-0.8691009227647822]],"euler":[4.457246350219941,2.624178075802511,5.476911902962542],"lab2crystal":[[0.43223768224706816,-0.8280898896880168,-0.35698420223053423],[-0.7642587471034007,-0.5465187115449087,0.3423826301190425],[-0.47862214065128966,0.124837624648105,-0.8691009227647822]]},{"crystal2lab":[[0.49254845153761584,-0.5444630499760941,0.678937412504736],[0.4067105171814396,-0.5456957974137439,-0.7326681731172604],[0.7694040408666325,0.6370055603334301,-0.04734277138476273]],"euler":[0.7473529511420337,1.6181568012329222,0.8792608847299491],"lab2crystal":[[0.49254845153761584,0.4067105171814396,0.7694040408666325],[-0.5444630499760941,-0.5456957974137439,0.6370055603334301],[0.678937412504736,-0.7326681731172604,-0.04734277138476273]]},{"crystal2lab":[[-0.5926441701301035,0.8045730267600344,0.03788313900666476],[0.8054504121659412,0.59225515411438,0.021987859529010473],[-0.0047456456374983,0.04354396669056547,-0.9990402403368618]],"euler":[2.096688969278582,3.0977768293810803,6.1746286101604255],"lab2crystal":[[-0.5926441701301035,0.8054504121659412,-0.0047456456374983],[0.8045730267600344,0.59225515411438,0.04354396669056547],[0.03788313900666476,0.021987859529010473,-0.9990402403368618]]},{"crystal2lab":[[0.9643187414485624,0.2618609344872991,0.03896428981130447],[-0.2610908575513702,0.965021284211479,-0.023779931074716952],[-0.04382840396536222,0.012758213364991014,0.9989576062063809]],"euler":[1.0228372317405054,0.045663441426294145,4.99565582649945],"lab2crystal":[[0.9643187414485624,-0.2610908575513702,-0.04382840396536222],[0.2618609344872991,0.965021284211479,0.012758213364991014],[0.03896428981130447,-0.023779931074716952,0.9989576062063809]]},{"crystal2lab":[[-0.9385770928739685,-0.18275060817744734,-0.29270335827083227],[-0.31121034069271103,0.8147362451407071,0.48923713544655273],[0.14906765101180328,0.5502790801627797,-0.8215666554559213]],"euler":[3.680750348933195,2.5349499055494853,0.26454549484597745],"lab2crystal":[[-0.9385770928739685,-0.31121034069271103,0.14906765101180328],[-0.18275060817744734,0.8147362451407071,0.5502790801627797],[-0.29270335827083227,0.48923713544655273,-0.8215666554559213]]},{"crystal2lab":[[0.7996340834284076,0.5999813160930356,-0.024652646082713087],[0.5912024096437121,-0.779414123631111,0.2073483414831576],[0.1051905102710273,-0.18037750476047879,-0.9779564981763272]],"euler":[3.2599319574076215,2.9312359694737276,2.613641068598193],"lab2crystal":[[0.7996340834284076,0.5912024096437121,0.1051905102710273],[0.5999813160930356,-0.779414123631111,-0.18037750476047879],[-0.024652646082713087,0.2073483414831576,-0.9779564981763272]]},{"crystal2lab":[[0.9353650748048087,-0.2643149512239898,-0.23501017721548698],[-0.11127597095516305,-0.8506459552508189,0.5138279061158587],[-0.33572285463367013,-0.45446569218686017,-0.8250764204010061]],"euler":[3.5705596838993174,2.541133765654227,3.7778353303525187],"lab2crystal":[[0.9353650748048087,-0.11127597095516305,-0.33572285463367013],[-0.2643149512239898,-0.8506459552508189,-0.45446569218686017],[-0.23501017721548698,0.5138279061158587,-0.8250764204010061]]},{"crystal2lab":[[-0.229864375282689,-0.4958623678411843,0.8374263437072141],[-0.8069287145318585,-0.3839430853250454,-0.4488360022269237],[0.5440849369720332,-0.778914770299048,-0.3118707456144608]],"euler":[1.0787879191461032,1.887957672454992,2.531862919181372],"lab2crystal":[[-0.229864375282689,-0.8069287145318589,0.5440849369720334],[-0.4958623678411844,-0.3839430853250455,-0.7789147702990483],[0.8374263437072144,-0.4488360022269239,-0.31187074561446093]]},{"crystal2lab":[[-0.17081051296161842,-0.6727335074924778,-0.7198981848557872],[0.05304634855957091,-0.735854790487132,0.6750583768990912],[-0.9838749176426266,0.07711909759590879,0.16137779035451993]],"euler":[3.9591239852525244,1.4087097433993434,4.790612074150281],"lab2crystal":[[-0.17081051296161842,0.05304634855957091,-0.9838749176426266],[-0.6727335074924778,-0.735854790487132,0.07711909759590879],[-0.7198981848557872,0.6750583768990912,0.16137779035451993]]},{"crystal2lab":[[-0.8366935657906684,0.5476708336599682,0.0007313840932581842],[-0.5476710368320609,-0.8366937468619025,-9.683742054768878e-05],[0.0005589094665425516,-0.00048158113137720265,0.999999727849874]],"euler":[1.4391590256549018,0.0007377670883440672,2.282011758213664],"lab2crystal":[[-0.8366935657906684,-0.5476710368320609,0.0005589094665425516],[0.5476708336599682,-0.8366937468619025,-0.00048158113137720265],[0.0007313840932581842,-9.683742054768878e-05,0.999999727849874]]},{"crystal2lab":[[0.6167777715765095,-0.3030251617466512,0.7264715629930267],[-0.19906915443863338,0.8328761527941688,0.5164191958650184],[-0.7615488508560713,-0.4631339605936932,0.4533765347970678]],"euler":[2.1887734100182574,1.1002463768518715,4.166000265256834],"lab2crystal":[[0.6167777715765095,-0.19906915443863338,-0.7615488508560713],[-0.3030251617466512,0.8328761527941688,-0.4631339605936932],[0.7264715629930267,0.5164191958650184,0.4533765347970678]]},{"crystal2lab":[[0.8486088614291671,0.07862378855794777,0.5231455822012533],[-0.3325047278128089,-0.6898775563504906,0.6430470925414451],[0.41146519450133695,-0.719643840463233,-0.5592934262060535]],"euler":[2.458648987378423,2.164329529759586,2.6221950194948676],"lab2crystal":[[0.8486088614291671,-0.3325047278128089,0.41146519450133695],[0.07862378855794777,-0.6898775563504906,-0.719643840463233],[0.5231455822012533,0.6430470925414451,-0.5592934262060535]]},{"crystal2lab":[[-0.9658314471299152,-0.25550156079487285,0.04345765946663237],[-0.2590564808014104,0.9567205262558599,-0.13257290219158224],[-0.007704251405709191,-0.13930106629920047,-0.9902201055513786]],"euler":[0.31676410061649746,3.00162210519124,3.196842844588791],"lab2crystal":[[-0.9658314471299152,-0.2590564808014104,-0.007704251405709191],[-0.25550156079487285,0.9567205262558599,-0.13930106629920047],[0.04345765946663237,-0.13257290219158224,-0.9902201055513786]]},{"crystal2lab":[[0.7442274554059046,-0.3671746806299536,-0.5579500412441455],[0.16136014240330362,0.9094403276270112,-0.3832508250862091],[0.6481422676107752,0.19519488822703845,0.7360778196266997]],"euler":[5.314262625792775,0.7435387262319101,1.2782751388284348],"lab2crystal":[[0.7442274554059046,0.16136014240330362,0.6481422676107752],[-0.3671746806299536,0.9094403276270112,0.19519488822703845],[-0.5579500412441455,-0.3832508250862091,0.7360778196266997]]},{"crystal2lab":[[0.8007178191870992,-0.5219037860723035,-0.29405341711949484],[-0.011708296416388653,0.477144432504856,-0.8787468954850681],[0.5989272825905348,0.7070711623401245,0.3759474452043842]],"euler":[5.9602699023587435,1.1853773128825789,0.702781128057715],"lab2crystal":[[0.8007178191870992,-0.011708296416388653,0.5989272825905348],[-0.5219037860723035,0.477144432504856,0.7070711623401245],[-0.29405341711949484,-0.8787468954850681,0.3759474452043842]]},{"crystal2lab":[[-0.37202407161084283,0.7251212011857439,0.579480227214902],[-0.8821847136295639,-0.08203815293388328,-0.4637023533491299],[-0.2887009199507597,-0.6837170358080459,0.6702110068966671]],"euler":[0.8959310513001545,0.8363032653245275,3.541133462127542],"lab2crystal":[[-0.37202407161084283,-0.8821847136295639,-0.2887009199507597],[0.7251212011857439,-0.08203815293388328,-0.6837170358080459],[0.579480227214902,-0.4637023533491299,0.6702110068966671]]},{"crystal2lab":[[0.05965013368118159,0.9246490546670193,0.37611964486743443],[-0.6093267000375145,0.33217703387721803,-0.719985687903535],[-0.790672393708804,-0.18623249949437237,0.5832277616496566]],"euler":[0.4814056459189168,0.9480997124317551,4.481068309413854],"lab2crystal":[[0.05965013368118159,-0.6093267000375145,-0.790672393708804],[0.9246490546670193,0.33217703387721803,-0.18623249949437237],[0.37611964486743443,-0.719985687903535,0.5832277616496566]]},{"crystal2lab":[[0.7745262131485335,-0.24219313119856617,0.5843386281481197],[0.20068398494502449,0.9701556623611418,0.13610264132337538],[-0.5998625537007437,0.011852341070366486,0.8000152740284239]],"euler":[1.7996337783672458,0.6434756516471977,4.7321448377751105],"lab2crystal":[[0.7745262131485335,0.20068398494502449,-0.5998625537007437],[-0.24219313119856617,0.9701556623611418,0.011852341070366486],[0.5843386281481197,0.13610264132337538,0.8000152740284239]]},{"crystal2lab":[[0.09435335277507395,0.313872799868396,0.9447652143907904],[0.9902411725524848,0.06818221249864831,-0.1215467238582314],[-0.10256639314113769,0.9470137545999863,-0.30437654902514183]],"euler":[1.4428463258027526,1.8800801814532344,6.175300751674333],"lab2crystal":[[0.09435335277507395,0.9902411725524848,-0.10256639314113769],[0.313872799868396,0.06818221249864831,0.9470137545999863],[0.9447652143907904,-0.1215467238582314,-0.30437654902514183]]},{"crystal2lab":[[-0.6409529407092673,0.7592662492513655,-0.1126680546734766],[0.7594099817320348,0.6486214192616244,0.05085994613432666],[0.11169515406629338,-0.05296241330225714,-0.992330174556487]],"euler":[4.2883597274683,3.0176600112696805,2.013566670482658],"lab2crystal":[[-0.6409529407092673,0.7594099817320348,0.11169515406629338],[0.7592662492513655,0.6486214192616244,-0.05296241330225714],[-0.1126680546734766,0.05085994613432666,-0.992330174556487]]},{"crystal2lab":[[0.9329980066429747,0.35986734213313804,-0.003196195592735914],[0.35975678555744245,-0.9324039815154813,0.034610265810228],[0.009474958871278964,-0.03344116206278175,-0.999395774372835]],"euler":[3.2336796632788434,3.1068281174912724,2.865496659682433],"lab2crystal":[[0.9329980066429747,0.35975678555744245,0.009474958871278964],[0.35986734213313804,-0.9324039815154813,-0.03344116206278175],[-0.003196195592735914,0.034610265810228,-0.999395774372835]]},{"crystal2lab":[[0.8596467323711784,0.43049696582688307,0.27509972362291263],[-0.5047496124416239,0.7988979877517464,0.32709300497907445],[-0.07896406944671736,-0.4200409117931939,0.9040632213271127]],"euler":[2.4423208374446883,0.44161352610491766,3.3274152501263217],"lab2crystal":[[0.8596467323711784,-0.5047496124416239,-0.07896406944671736],[0.43049696582688307,0.7988979877517464,-0.4200409117931939],[0.27509972362291263,0.32709300497907445,0.9040632213271127]]},{"crystal2lab":[[0.13661428499020256,0.988512914325417,-0.06464329314388569],[0.9904841003225877,-0.13740170260544238,-0.007875222490795818],[-0.01666685767533026,-0.06295228616179208,-0.9978773599607489]],"euler":[4.833617421719346,3.0764253090366553,3.400408335295686],"lab2crystal":[[0.13661428499020256,0.9904841003225877,-0.01666685767533026],[0.988512914325417,-0.13740170260544238,-0.06295228616179208],[-0.06464329314388569,-0.007875222490795818,-0.9978773599607489]]},{"crystal2lab":[[-0.8813264810306183,-0.08959207164892662,0.4639363043908515],[0.010298986122881756,-0.9852686391634646,-0.1707033671777308],[0.4723955595886014,-0.14566732433400462,0.8692660501263948]],"euler":[1.2182245477698843,0.5170806428876982,1.869903987141286],"lab2crystal":[[-0.8813264810306183,0.010298986122881756,0.4723955595886014],[-0.08959207164892662,-0.9852686391634646,-0.14566732433400462],[0.4639363043908515,-0.1707033671777308,0.8692660501263948]]},{"crystal2lab":[[0.1280732446944557,0.9571859365610321,-0.2596003213465158],[0.9913125229128147,-0.1314549662034442,0.0043673535100714755],[-0.02994538210917866,-0.2579043906377545,-0.965706269721857]],"euler":[4.695567192443147,2.8789463028412023,3.2571854490302146],"lab2crystal":[[0.1280732446944557,0.9913125229128147,-0.02994538210917866],[0.9571859365610321,-0.1314549662034442,-0.2579043906377545],[-0.2596003213465158,0.0043673535100714755,-0.965706269721857]]},{"crystal2lab":[[0.1764628823257075,0.9812552288904904,-0.0774533855707753],[0.9743707622456844,-0.1629872119012118,0.15503801610390203],[0.13950795261227167,-0.1028267695288592,-0.9848675985259091]],"euler":[3.604901591637344,2.9674047024364287,2.2059690894440505],"lab2crystal":[[0.1764628823257075,0.9743707622456844,0.13950795261227167],[0.9812552288904904,-0.1629872119012118,-0.1028267695288592],[-0.0774533855707753,0.15503801610390203,-0.9848675985259091]]},{"crystal2lab":[[-0.8007033940409115,0.0682137599927451,-0.5951646475715919],[-0.564613382560019,-0.41797151472158234,0.7116962421675977],[-0.20021439260327584,0.9058955214752961,0.37318561221663293]],"euler":[3.83805693875414,1.188355995353749,6.065669283318284],"lab2crystal":[[-0.8007033940409115,-0.564613382560019,-0.20021439260327584],[0.0682137599927451,-0.41797151472158234,0.9058955214752961],[-0.5951646475715919,0.7116962421675977,0.37318561221663293]]}],"orix":"0.15.0","seed":20261031,"sets":[{"A":[[[0.7163224059291963,0.5620992643228095,0.4134327367439159],[0.16471513703888446,0.43954420208953215,-0.8829891381210406],[-0.6780495073326647,0.7006035337173464,0.2222691030006148]],[[0.7163224059291963,0.5620992643228095,0.4134327367439159],[0.16471513703888446,0.43954420208953215,-0.8829891381210406],[-0.6780495073326647,0.7006035337173464,0.2222691030006148]],[[0.2054331911670444,-0.5086497315672309,0.8361056479557523],[-0.1331611049278178,-0.8609099501140087,-0.491021565645642],[0.969569659231005,-0.010464624755754437,-0.24459183863611222]],[[-0.47236276460113763,0.7401804670485553,-0.4785460216302636],[-0.17570995417909097,-0.611116727990814,-0.7717916537331712],[-0.8637125856561035,-0.2804803397202448,0.41872586307710213]],[[-0.7659969011688432,0.6257707867642202,0.1471722454548359],[-0.47319852104372706,-0.39391348068692994,-0.787981807794531],[-0.43512286435185055,-0.6732333118359465,0.5978503163440196]],[[-0.0048049257114897714,-0.9744408332023937,0.22459291012124993],[0.9010881325913431,-0.1016069361884206,-0.4215640020464846],[0.43360937488937357,0.20035242226194072,0.8785452844912549]]],"B":[[[0.7163224059291963,0.5620992643228095,0.4134327367439159],[0.16471513703888446,0.43954420208953215,-0.8829891381210406],[-0.6780495073326647,0.7006035337173464,0.2222691030006148]],[[0.2915940881985653,0.5853185303366164,0.7565547605905542],[-0.7956564209525754,0.5874368447863539,-0.14781344047275013],[-0.5309460872109196,-0.5588561276679684,0.6370055580945726]],[[-0.27452824655325747,0.9593645700022237,-0.06522165030756355],[0.44656720222289503,0.06713059382427368,-0.8922282316042327],[-0.8515937856414934,-0.27406770184983237,-0.44684999615032067]],[[0.5367482613830812,0.1664129046449916,0.8271686944450065],[-0.8273993194304381,0.29583417391892974,0.4773808833078177],[-0.16526242799949237,-0.9406321739709531,0.29647840255639396]],[[-0.6077300760554135,0.6781468704043611,0.41325655084759577],[-0.22373272946097866,0.3530954688918069,-0.9084422136911159],[-0.7619762597519828,-0.6445466717072126,-0.06286308587345138]]],"distanceMatrix":[[0.0,0.0,2.4031011106238047,1.698151711907277,1.947205449317876,2.0604775959888997],[0.0,0.0,2.4031011106238047,1.698151711907277,1.947205449317876,2.0604775959888997],[2.4031011106238047,2.4031011106238047,0.0,2.796483986556562,2.167974260473399,1.5406118437609988],[1.698151711907277,1.698151711907277,2.796483986556562,0.0,0.7215248262260923,2.550071201219305],[1.947205449317876,1.947205449317876,2.167974260473399,0.7215248262260923,0.0,2.364239739838811],[2.0604775959888997,2.0604775959888997,1.5406118437609988,2.550071201219305,2.364239739838811,0.0]],"dot":[1.0,0.7447130778802669,0.04587747609840953,0.38751230603719594,0.9166817177849224],"dotOuter":[[1.0,0.7447130778802669,0.7894020991542061,0.4772768417652543,0.7225503286500532],[1.0,0.7447130778802669,0.7894020991542061,0.4772768417652543,0.7225503286500532],[0.3609121413058482,0.3173299511436828,0.04587747609840953,0.5280626596905034,0.28763886692112867],[0.6606771552725689,0.6539139870053928,0.8754156143668231,0.38751230603719594,0.8556436526566478],[0.5623240328053087,0.7911919334232553,0.7990998596656893,0.6316590849993782,0.9166817177849224],[0.5146141090142011,0.15911703979537128,0.04118980180742016,0.1055047176953052,0.12810412036056817]],"symbol":"-1"},{"A":[[[-0.7377727459794876,0.6717196347654968,0.06696347930322832],[0.24531872601599844,0.35920636120223115,-0.9004385113586267],[-0.628895935712558,-0.6478915976784695,-0.42979806851807334]],[[0.7377727459794876,-0.6717196347654968,-0.06696347930322832],[0.24531872601599844,0.35920636120223115,-0.9004385113586267],[0.628895935712558,0.6478915976784695,0.42979806851807334]],[[-0.6894869814097121,0.6470776010694927,0.3254201601939776],[0.647391398632426,0.3490984040663318,0.6775062222999075],[0.32479544246075104,0.6778059327510277,-0.6596112780147368]],[[0.09597840144551462,0.7796129624383872,-0.6188632928636222],[-0.7800936856774492,0.44508807888404134,0.4397163217365104],[0.6182572183340334,0.44056807741069387,0.6508899915842217]],[[0.2151346080127809,0.8337905200613682,0.5084392481811177],[-0.011730486027901943,0.5227988472282771,-0.8523753639297266],[-0.976513950813165,0.17741120030194757,0.12225289311376608]],[[0.5789828538012067,-0.6415976310908083,-0.5031215904558989],[0.8039668212616375,0.3465453386280179,0.48326357051370034],[-0.13570632009846947,-0.6842943869811116,0.7164670171274294]]],"B":[[[0.7377727459794876,-0.6717196347654968,-0.06696347930322832],[0.24531872601599844,0.35920636120223115,-0.9004385113586267],[0.628895935712558,0.6478915976784695,0.42979806851807334]],[[0.047864982554110966,-0.9969971911262634,0.0608731823665819],[-0.7419336375606491,-0.07629049675954304,-0.6661187863738758],[0.6687626042932706,-0.013280097534379753,-0.7433573959464985]],[[-0.22282203849980356,0.5540239489569172,0.8021270492515356],[0.36935094418493297,-0.7134923771128134,0.5954061704681984],[0.9021808129130715,0.42893599963641443,-0.045647442725954814]],[[0.6372426644192494,0.7298635437387622,0.24742876583968984],[0.7335741120034978,-0.6728635571314748,0.09551782913497558],[0.2362007807822313,0.12063930124745739,-0.9641863669189633]],[[0.8618170195382322,0.13978338660389747,-0.4875777165373578],[-0.39861107765771775,-0.40778854985791196,-0.8214728890068951],[-0.3136568723984725,0.9023131958662286,-0.29571990626753847]]],"distanceMatrix":[[0.0,0.0,1.9393933271897368,2.0290969143689432,1.1403984711607844,1.793828315877938],[0.0,0.0,1.9393933271897368,2.0290969143689432,1.1403984711607844,1.793828315877938],[1.9393933271897368,1.9393933271897368,0.0,1.9523575939340398,2.167284334302043,0.2701729067179692],[2.0290969143689432,2.0290969143689432,1.9523575939340398,0.0,2.119396599012354,2.2193849143609174],[1.1403984711607844,1.1403984711607844,2.167284334302043,2.119396599012354,0.0,1.9138249613487361],[1.793828315877938,1.793828315877938,0.2701729067179692,2.2193849143609174,1.9138249613487361,0.0]],"dot":[1.0,0.7388858162302425,0.8337346407192517,0.2339402194981957,0.70284404688867],"dotOuter":[[1.0,0.7388858162302425,0.36523955863107493,0.5188075982438365,0.7632565669007816],[1.0,0.7388858162302425,0.36523955863107493,0.5188075982438365,0.7632565669007816],[0.5655497230159083,0.14313909162585653,0.8337346407192517,0.7437454556889443,0.1461008690555846],[0.5280034545869069,0.7311798562418654,0.567293911343549,0.2339402194981957,0.5640927392025621],[0.8417934445491787,0.8764888221916253,0.17783856270616447,0.5250069203616978,0.70284404688867],[0.6240242279460015,0.13933724252592963,0.8176367792152693,0.7797402081898247,0.2614275745671642]],"symbol":"2/m"},{"A":[[[-0.9773552037594465,-0.06896349652324951,0.2000520977935959],[0.13652217642725004,0.5168164472648403,0.845140494344056],[-0.16167405799291834,0.853314007844613,-0.49569819748337424]],[[0.9773552037594465,0.06896349652324951,-0.2000520977935959],[-0.13652217642725004,-0.5168164472648403,-0.845140494344056],[-0.16167405799291834,0.853314007844613,-0.49569819748337424]],[[-0.07655479504983698,0.9036336252956977,-0.4214091059645391],[-0.36266709648796447,0.36846276126196964,0.855983510757184],[0.9287690458234852,0.21836085912580444,0.2995105920029768]],[[0.03842888550647364,-0.9805012122488592,0.19271894960602026],[-0.8962084584763517,0.05148243222217366,0.44063585660716587],[-0.44196563182466114,-0.18964949762919678,-0.8767550674702835]],[[-0.6562053326123644,-0.055568780651209584,0.7525335022894553],[-0.23634202903655488,0.9622411362318027,-0.13503496234017665],[-0.716614964092978,-0.2664659572258085,-0.6445610032244838]],[[-0.11491369230699927,-0.7825111498012292,-0.6119404740308738],[-0.2480424466392197,-0.5739055511520785,0.7804533061125813],[-0.9619094489532796,0.24147198345478332,-0.1281463742007971]]],"B":[[[0.9773552037594465,0.06896349652324951,-0.2000520977935959],[0.13652217642725004,0.5168164472648403,0.845140494344056],[0.16167405799291834,-0.853314007844613,0.49569819748337424]],[[0.5350172509514519,0.7593420775046177,0.37034598757826787],[0.3498550351843751,0.1998760607061393,-0.9152327653186021],[-0.7689980465127925,0.6192327265960409,-0.15872250871235033]],[[-0.2654378629529866,-0.9097071002127742,0.3193363943139224],[-0.8029187625843502,0.02521992610198201,-0.5955547128664387],[0.5337267105935088,-0.4144839528255298,-0.7371152225053806]],[[-0.8211544538674752,0.5404230721806529,0.1834346367195514],[0.5219831764261413,0.8411461225198315,-0.14144526891320533],[-0.23073562018397753,-0.020398618205114523,-0.9728026366913475]],[[-0.007079944098665192,-0.5905374902291316,0.8069791490648558],[0.5922522952204772,0.6477567079255956,0.47921651280342187],[-0.8057214737101385,0.48132807935053373,0.34516110272029443]]],"distanceMatrix":[[0.0,0.0,1.546575726061221,1.7266781058922984,1.392542304255805,1.6719331786516047],[0.0,0.0,1.546575726061221,1.7266781058922984,1.392542304255805,1.6719331786516047],[1.546575726061221,1.546575726061221,0.0,0.7913056675166372,1.2877381771177032,1.122965283544026],[1.7266781058922984,1.7266781058922984,0.7913056675166372,0.0,1.4159174152381413,1.2274707680489718],[1.392542304255805,1.392542304255805,1.2877381771177032,1.4159174152381413,0.0,1.2407416574668324],[1.6719331786516047,1.6719331786516047,1.122965283544026,1.2274707680489718,1.2407416574668324,0.0]],"dot":[1.0,0.8448407850846312,0.7632202829457093,0.8619270542637748,0.7566390122245784],"dotOuter":[[1.0,0.8448407850846312,0.717729956608683,0.8201432731821864,0.77453336139274],[1.0,0.8448407850846312,0.7177299566086831,0.8201432731821864,0.77453336139274],[0.7156179960997212,0.859119370528485,0.7632202829457093,0.704357103627237,0.8436657623742502],[0.6499033593246106,0.8247324757902577,0.8501111888398234,0.8619270542637747,0.7173472739701984],[0.7672390541759299,0.6338434789455065,0.7004607843691814,0.88738233911846,0.7566390122245784],[0.6704608399365664,0.9627646418280043,0.76584699522923,0.7913694330162465,0.7110877966243849]],"symbol":"mmm"},{"A":[[[-0.48944537905085744,-0.8715332215300107,0.029547668186910847],[0.8063329526732619,-0.4394067322624741,0.39591525996813187],[-0.33206985764893127,0.2176041530208827,0.9178115504988389]],[[0.8063329526732619,-0.4394067322624741,0.39591525996813187],[0.48944537905085744,0.8715332215300107,-0.029547668186910847],[-0.33206985764893127,0.2176041530208827,0.9178115504988389]],[[0.36402854585934963,0.6191782411355857,-0.6957740462993499],[-0.001788200255967931,0.7474926342170523,0.6642676901152853],[0.931386074696939,-0.24056821796632805,0.27321587136389663]],[[-0.6743672008761197,0.6167450075812626,-0.4060227506016079],[0.34074179490500767,-0.22789832485218905,-0.9121169786460942],[-0.6550754976240684,-0.7534506945734102,-0.0564636454670146]],[[0.4727748949250854,-0.8787796369162142,-0.06504036031558608],[0.06701877265860273,-0.03773745200302803,0.9970377970907893],[-0.878630970794855,-0.47573336487756035,0.04105341279802521]],[[-0.548011749463277,0.36403084605247127,0.7531033565006335],[-0.5997290438678888,0.4566272539204316,-0.6571275560485905],[-0.5831022177899052,-0.8117715775385386,-0.03191722903208061]]],"B":[[[0.48944537905085744,0.8715332215300107,-0.029547668186910847],[-0.8063329526732619,0.4394067322624741,-0.39591525996813187],[-0.33206985764893127,0.2176041530208827,0.9178115504988389]],[[-0.3279522607490987,-0.6004056481768233,0.7293561354420239],[0.37004110490067055,0.6287173364270343,0.6839474333309442],[-0.8692047488097647,0.4941938572194763,0.015985497586557318]],[[0.5203057301120756,-0.8520117251735001,0.057947971314067814],[-0.5495614123769482,-0.38599905678384017,-0.7409365574651041],[0.6536544968397248,0.3536675675388708,-0.6690702881105576]],[[-0.9347029564839682,0.21263538371459298,-0.284809720221549],[0.3464336580348092,0.7241755081104664,-0.5962831156703743],[0.07946133475313692,-0.6560152643387926,-0.7505530422520144]],[[0.3886192750777067,-0.28563108639213614,0.8760079574550255],[0.8365578572729746,-0.28911316564709594,-0.46538642963065013],[0.38619426519942907,0.9136894767567166,0.12659198075356198]]],"distanceMatrix":[[0.0,0.0,1.7242006634139229,1.579713253458096,1.4586338066042046,1.5913599163626468],[0.0,0.0,1.7242006634139229,1.579713253458096,1.4586338066042046,1.5913599163626468],[1.7242006634139229,1.7242006634139229,0.0,2.105546233254149,2.3360739390637413,1.9629369059818889],[1.579713253458096,1.579713253458096,2.105546233254149,0.0,0.6058199725800165,0.30963813295525],[1.4586338066042046,1.4586338066042046,2.3360739390637413,0.6058199725800165,0.0,0.9022525777943591],[1.5913599163626468,1.5913599163626468,1.9629369059818889,0.30963813295525,0.9022525777943591,0.0]],"dot":[1.0,0.7760613538846098,0.7904289210275286,0.8453698884129914,0.3397056906296855],"dotOuter":[[1.0,0.7760613538846097,0.3461154501472641,0.26624658426707526,0.7320664472817244],[1.0,0.7760613538846098,0.3461154501472642,0.26624658426707526,0.7320664472817244],[0.6508443094916331,0.19422929249502507,0.7904289210275286,0.7149687843564638,0.7658519697816786],[0.7039471540897813,0.7553887699525061,0.4081857754825269,0.8453698884129914,0.15768246260676372],[0.7456297648978152,0.818409808171068,0.33908068918840906,0.7778698139887656,0.3397056906296855],[0.6997992067943457,0.7421388543569337,0.41681302290138134,0.8261758717007759,0.11622187824214068]],"symbol":"4/m"},{"A":[[[-0.6050480139811647,-0.6784335167420035,-0.4167071683311022],[0.5355533759935889,-0.7340712926666462,0.41751876454189246],[-0.5891514934542027,0.029449968485490974,0.8074857380269236]],[[0.6050480139811647,0.6784335167420035,0.4167071683311022],[0.5355533759935889,-0.7340712926666462,0.41751876454189246],[0.5891514934542027,-0.029449968485490974,-0.8074857380269236]],[[-0.5461953561913173,-0.05930507494034912,0.8355558275551426],[-0.4274543963018964,0.8775732423965599,-0.2171357716079267],[-0.7203841835834689,-0.47576056195938304,-0.504676644721024]],[[-0.8193914609562327,-0.11542767739748747,0.5614927292517976],[0.22031902680477258,0.8408656926857416,0.49437274731919234],[-0.5292042707119896,0.5287923393495311,-0.6635672548471947]],[[0.9342480451621973,-0.07848028303275056,-0.34788135231097383],[-0.34798893671452874,-0.4139779411066671,-0.8411456260371186],[-0.07800185917594682,0.9068975187154191,-0.41406835246479723]],[[-0.27968575235899845,-0.6135880749419849,-0.738434529404178],[0.7635456246832448,0.324121213969493,-0.5585190396772659],[0.5820429184814931,-0.7200382718605525,0.3778504043951979]]],"B":[[[-0.5355533759935889,0.7340712926666462,-0.41751876454189246],[0.6050480139811647,0.6784335167420035,0.4167071683311022],[0.5891514934542027,-0.029449968485490974,-0.8074857380269236]],[[-0.1791983472023554,0.9836232983940039,-0.01931732943356096],[-0.8132547001579746,-0.13705370453489774,0.5655378632277628],[0.5536287068341985,0.11705335932711025,0.8244960679344273]],[[0.7632324063677683,0.6349425666747335,-0.11968387900869493],[-0.48056850150790914,0.6816629915801027,0.551715036290029],[0.4318914322240127,-0.363570492387057,0.8254006832059957]],[[-0.32587193747468823,0.8513424169284478,-0.4111247614835691],[0.5866810773368305,0.5231026373108906,0.6181981432625681],[0.7413587484216995,-0.039745691298799846,-0.6699309562647421]],[[0.24859064036378248,0.912453200351475,0.32501053935507335],[0.826254384269213,-0.02465556368928963,-0.5627573150613567],[-0.5054763950961788,0.4084375843963365,-0.7600475995969314]]],"distanceMatrix":[[0.0,0.0,1.5790834213375173,1.4515577829637007,1.30968329185828,1.5635854874983925],[0.0,0.0,1.5790834213375173,1.4515577829637007,1.30968329185828,1.5635854874983925],[1.5790834213375173,1.5790834213375173,0.0,1.0902714164135727,1.5471988963537804,1.3546487221196128],[1.4515577829637007,1.4515577829637007,1.0902714164135727,0.0,0.6534367881992531,0.3780310181845137],[1.30968329185828,1.30968329185828,1.5471988963537804,0.6534367881992531,0.0,0.5456984966310598],[1.5635854874983925,1.5635854874983925,1.3546487221196128,0.3780310181845137,0.5456984966310598,0.0]],"dot":[1.0,0.7934901376302987,0.8356256380441287,0.6902236823208447,0.9262570601551449],"dotOuter":[[1.0,0.7934901376302987,0.8323815282427882,0.9896326519646006,0.7801313680558466],[1.0,0.7934901376302987,0.8323815282427882,0.9896326519646006,0.7801313680558466],[0.704170789052913,0.9668862543152503,0.8356256380441287,0.7612177295290354,0.8799656494581549],[0.7479826844242407,0.9428676755298332,0.93199519238186,0.6902236823208447,0.9950730901393213],[0.7931443810035521,0.798871499546421,0.8644462278863453,0.766887686614178,0.926257060155145],[0.7096515964917642,0.8760547080991522,0.9181103417998858,0.7754487623346649,0.9594377016111579]],"symbol":"4/mmm"},{"A":[[[-0.7130442604322078,0.4984373262557673,0.4930802312602767],[0.655594184936799,0.7232888658166035,0.21690892388923466],[-0.24852393716046822,0.4779261955372748,-0.8425095870537775]],[[0.9242833489447253,0.3771678689437291,-0.05869147723451581],[0.28971735108857966,-0.7933038196401886,-0.5354744683199228],[-0.24852393716046822,0.4779261955372748,-0.8425095870537775]],[[0.7963922242743076,-0.42365549497321964,0.43159639328244837],[0.2776422483359161,-0.37788366754603026,-0.8832432936291899],[0.5372841027374615,0.8232374841933268,-0.18331895036968576]],[[-0.48006614345616716,0.4767627478952723,-0.7363652491301256],[0.7373517314678846,0.6740555551234254,-0.044289194038316215],[0.4752356489242316,-0.5642219340176345,-0.6751330884832744]],[[0.7657905715145464,0.632024250497937,-0.11878614128738013],[-0.11383404889889526,-0.04857381572449371,-0.9923116414399494],[-0.6329349175789785,0.7734248064346553,0.03474850933216744]],[[-0.3915328480481092,-0.698196398495992,-0.5993528326675047],[0.8589949807847628,-0.04381492921629104,-0.5101057488054377],[0.3298933947291476,-0.7145642316160228,0.6169021859331361]]],"B":[[[-0.2112390885125175,-0.8756051951994964,-0.43438875402576094],[-0.9453115360253788,0.07001495382358505,0.3185655444306882],[-0.24852393716046822,0.4779261955372748,-0.8425095870537775]],[[-0.39232900391793885,0.4683415089505756,-0.7916654493399696],[-0.5981349674924108,0.5239553541529964,0.6063871267740278],[0.698793612841167,0.7114260452417428,0.07456855103918311]],[[0.4748032918631635,0.6118743637210076,-0.6325911768724857],[-0.2801211162054774,-0.5763313417088771,-0.7677071999270578],[-0.8343224761995218,0.541712052279676,-0.10224508850424366]],[[0.9488942841425233,0.3009378260623183,0.09505820515102271],[0.22453453762610573,-0.8554162638877526,0.4667411026354436],[0.22177438746320316,-0.4215441143239989,-0.8792705389947489]],[[-0.9437489560765698,0.3306186805999562,0.0054033270055281885],[0.3148646366138635,0.9035297261028606,-0.2906790234236195],[-0.1009859817717499,-0.27262670831529623,-0.9568053665185842]]],"distanceMatrix":[[0.0,0.0,1.1491604913729303,1.390199476996264,1.0542652044843746,2.803506751978806],[0.0,0.0,1.1491604913729303,1.390199476996264,1.0542652044843746,2.803506751978806],[1.1491604913729303,1.1491604913729303,0.0,1.8659512291580131,1.3467667878930303,2.1490944740369735],[1.390199476996264,1.390199476996264,1.8659512291580131,0.0,2.443723744734492,1.5376257555418933],[1.0542652044843746,1.0542652044843746,1.3467667878930303,2.443723744734492,0.0,2.4940963394008455],[2.803506751978806,2.803506751978806,2.1490944740369735,1.5376257555418933,2.4940963394008455,0.0]],"dot":[1.0,0.6920049954800397,0.6481511300810588,0.9830933684310466,0.6386379837336249],"dotOuter":[[1.0,0.6920049954800398,0.8726808269679643,0.8606554763650552,0.9135366666556658],[1.0,0.6920049954800397,0.8726808269679642,0.8606554763650551,0.9135366666556659],[0.8394205054825252,0.9433476106631973,0.6481511300810588,0.6276700752547699,0.6033794270736431],[0.767989826889597,0.6633654930087599,0.4180786504027675,0.9830933684310466,0.8961835356314546],[0.8642530867130465,0.7444559422450353,0.9474494198481035,0.4902668814743207,0.6386379837336249],[0.16823901862258162,0.6143090047180423,0.3660455036393057,0.6341879645320407,0.521015571136862]],"symbol":"-3"},{"A":[[[-0.4685308955840931,0.8809777840188808,-0.06600715073649055],[-0.49866112258272444,-0.3253979413365765,-0.8034010608646561],[-0.7292570772421099,-0.34350301867538235,0.5917683596249426]],[[-0.6661186478283521,0.15868600847280662,-0.7287693035044056],[-0.15642909674233663,0.9256481117983595,0.3445366610630986],[0.7292570772421099,0.34350301867538235,-0.5917683596249426]],[[0.29105727894095274,-0.4017185278241864,-0.8682786907314133],[0.3302780071958179,-0.8095827910485478,0.4852753263878178],[-0.8978875755562319,-0.428016271700986,-0.10295616941660231]],[[0.5504842613761024,-0.24468892883773308,-0.7981819379574122],[0.8052168885693044,-0.09683222918654605,0.5850207532673803],[-0.22043783781369258,-0.9647542938463698,0.14372303978491802]],[[0.5138111352102585,-0.025628777923704327,0.8575204272063044],[-0.8054465043684413,-0.358571040716445,0.471892718062256],[0.29538795834394443,-0.9331505636828725,-0.20488040307392313]],[[-0.3751381329077325,0.5794048073939989,0.7235754628283241],[-0.9217935257791078,-0.15080273186504883,-0.3571487531740232],[-0.09781654803877338,-0.8009672934837984,0.590663455530319]]],"B":[[[-0.19758775224425892,-0.7222917755460743,-0.6627621527679151],[0.6550902193250612,-0.6002501704617829,0.4588643998015576],[-0.7292570772421099,-0.34350301867538235,0.5917683596249426]],[[0.5007337950325595,0.4088750343365326,0.7629461794966866],[-0.32572086983593085,-0.7276192518563006,0.6037185927908816],[0.8019797886889767,-0.5508097953235555,-0.23116441748246552]],[[-0.09285042338573629,0.6535351726223244,0.7511794572687664],[0.6266761970712893,0.6246212658335168,-0.4659669712466773],[-0.77372846845735,0.42748105503524236,-0.4675512856196634]],[[0.5336922437567307,-0.704056941336751,0.4684830971437982],[0.14736498599531284,0.6229224146120771,0.7682780917587466],[-0.8327401454574578,-0.34098605357022826,0.436202202440673]],[[-0.8660433840835378,0.14395444876115346,0.47879638006882086],[-0.19971395983957982,-0.9775378530898784,-0.06733557768094342],[0.4583483294726065,-0.15393785258442483,0.8753399033583281]]],"distanceMatrix":[[0.0,0.0,0.7421826680885174,1.0308186365088499,1.614903520903289,0.9702923936479678],[0.0,0.0,0.7421826680885174,1.0308186365088499,1.614903520903289,0.9702923936479678],[0.7421826680885174,0.7421826680885174,0.0,0.9422989120003707,1.430621717642346,1.4045819483060242],[1.0308186365088499,1.0308186365088499,0.9422989120003707,0.0,0.642370444892709,1.0312939036207243],[1.614903520903289,1.614903520903289,1.430621717642346,0.642370444892709,0.0,1.245917447501579],[0.9702923936479678,0.9702923936479678,1.4045819483060242,1.0312939036207243,1.245917447501579,0.0]],"dot":[1.0,0.8562651725652206,0.8296558534542715,0.7963414162336696,0.661662018386126],"dotOuter":[[1.0,0.8562651725652206,0.6786402791978696,0.9499262130263834,0.7431997457596707],[1.0,0.8562651725652206,0.6786402791978697,0.9499262130263834,0.7431997457596706],[0.9319321472472357,0.8538079443168889,0.8296558534542715,0.932053306198968,0.8447137832394251],[0.8700910458935243,0.8026882022525323,0.7568326891174865,0.7963414162336696,0.7424660083284436],[0.6913418495404541,0.9364073104837239,0.7719409952851023,0.7095821709693798,0.661662018386126],[0.8846067814728077,0.7109253362028732,0.8215605127562342,0.8977761017006884,0.8224650095109481]],"symbol":"-31m"},{"A":[[[0.6088499874224766,0.7821985094603148,0.13216347685238755],[0.019679615526936922,-0.1814445110996042,0.9832042524951452],[0.7930412382394509,-0.5960229703542368,-0.12586585423279367]],[[-0.2873819467282003,-0.5482348107196622,0.7853981213434917],[0.5371193639651692,0.5866815244451531,0.6060590546542168],[-0.7930412382394509,0.5960229703542368,0.12586585423279367]],[[-0.8640853211165995,-0.40195241213284577,0.30296999226228727],[0.5012542535313017,-0.6323498147802047,0.590658856755927],[-0.045833733729656545,0.6622446452241257,0.7478845490611309]],[[-0.9795827189658297,-0.15937160237377157,-0.1225495371689674],[-0.14741322667790796,0.9838857758214323,-0.10118359914860418],[0.1367005387843054,-0.08105228246677663,-0.9872909855777101]],[[-0.06243620974210465,0.8488835788722533,0.5248794044674214],[-0.25581239111490905,-0.5219484516348832,0.8137136071051171],[0.9647081114332525,-0.08346546206056094,0.24974342109033495]],[[-0.6984389981483087,-0.7103908894321928,-0.08676260759869704],[0.7151917634674682,-0.6883951242570133,-0.12088380523240014],[0.026147797878225142,-0.1464818661504209,0.9888677138804817]]],"B":[[[-0.2873819467282003,-0.5482348107196622,0.7853981213434917],[-0.5371193639651692,-0.5866815244451531,-0.6060590546542168],[0.7930412382394509,-0.5960229703542368,-0.12586585423279367]],[[-0.5054689042721593,-0.2901073390143219,-0.8126124037103618],[-0.858524498088858,0.07497198611834364,0.5072621486753626],[-0.08623730628432968,0.9540528985058954,-0.2869602652935116]],[[-0.8123924953518672,-0.5683341867512702,0.13044035290407252],[-0.5608263750477652,0.7002941310604666,-0.4416581336883193],[0.15966280263965665,-0.4319541435986445,-0.8876504983839145]],[[0.7539655009197037,0.6505377345434317,-0.09130541801010322],[-0.06296770709682631,-0.0667824587330274,-0.9957786757450371],[-0.6538892041361963,0.7565320608808797,-0.009388800427132582]],[[-0.8077093074868211,-0.4134360338461592,-0.4203288242753732],[-0.018964981349985977,-0.694340000755163,0.7193971732177679],[-0.5892758302200717,0.5890353209009439,0.552984074499047]]],"distanceMatrix":[[0.0,0.0,1.3242807951061164,1.337046187970089,0.8172664073864355,1.772216404473324],[0.0,0.0,1.3242807951061164,1.337046187970089,0.8172664073864355,1.772216404473324],[1.3242807951061164,1.3242807951061164,0.0,0.7759498953242442,1.7308840082180748,0.9106152988143518],[1.337046187970089,1.337046187970089,0.7759498953242442,0.0,1.6779747219248398,0.6924407872487721],[0.8172664073864355,0.8172664073864355,1.7308840082180748,1.6779747219248398,0.0,1.285801082639325],[1.772216404473324,1.772216404473324,0.9106152988143518,0.6924407872487721,1.285801082639325,0.0]],"dot":[1.0,0.8880956526694277,0.9889379849409693,0.7438920107449022,0.7630912407781703],"dotOuter":[[1.0,0.8880956526694277,0.7585093581133822,0.9864456206015362,0.9279723628766161],[1.0,0.8880956526694277,0.7585093581133819,0.9864456206015363,0.9279723628766161],[0.7886781090941306,0.8034771511135274,0.9889379849409693,0.7743169986136891,0.9249195364666452],[0.7847379486307706,0.7043011266854406,0.951311461873771,0.7438920107449022,0.9132386154069514],[0.9176647837530224,0.6808676908605295,0.6700193675982535,0.8533532042113257,0.7630912407781703],[0.6324314584685402,0.8340181444669328,0.9486750598693992,0.6947972619765888,0.8241504323028819]],"symbol":"-3m1"},{"A":[[[0.21090673656781794,0.8914881258201418,-0.40095794042767663],[-0.4087707610811565,-0.2921621713665979,-0.8646084261140918],[-0.8879328878667831,0.3462516240309148,0.30279530957481643]],[[-0.24855249513667196,0.19272420048177455,-0.9492518315547228],[-0.38703597223758107,-0.9181324498157197,-0.0850644507975905],[-0.8879328878667831,0.3462516240309148,0.30279530957481643]],[[-0.669509523193665,-0.7276754820028786,-0.1491488895193981],[0.6992256224547988,-0.6851591622843407,0.20405992071085305],[-0.25068012935948963,0.03233133510541719,0.9675299259013184]],[[-0.06581188403517199,0.8622542664514712,-0.5021816164556173],[0.5827147335442004,0.4417513151490338,0.6821285178576034],[0.8100076143038736,-0.2477364639094026,-0.5315207514475919]],[[0.7855470727242999,-0.6100555745461135,0.1036725252875351],[-0.4319399192368639,-0.6605510737059492,-0.6140848355036695],[0.4431068750357352,0.4376122427345112,-0.7824013179340488]],[[-0.37998582833767813,0.7853043091600512,-0.4887820703311272],[0.7362715252403121,-0.06308256756432773,-0.6737394383512041],[-0.5599241121641687,-0.615887758998461,-0.554226719793462]]],"B":[[[-0.21090673656781794,-0.8914881258201418,0.40095794042767663],[0.4087707610811565,0.2921621713665979,0.8646084261140918],[-0.8879328878667831,0.3462516240309148,0.30279530957481643]],[[0.8915016166787415,0.19774969384407548,0.40757812262652754],[0.002798143591882102,0.8972753221341107,-0.4414625314582962],[-0.4530088717140422,0.39470502261017965,0.7993690682498718]],[[-0.8603749261251334,-0.49634597325813046,-0.1157396272916501],[0.44606859260656617,-0.8431954352378532,0.3000737720695435],[-0.24653153385554583,0.20654813682162879,0.9468685600390084]],[[-0.12001372873321847,0.29686310620376055,0.9473484053349137],[0.100891864967509,0.95295206283302,-0.28583771186753776],[-0.987632288100651,0.06127529777444059,-0.144318402789548]],[[0.31209615903254084,0.8181733377333583,0.4828958241063914],[0.9417594577879457,-0.1994238089841519,-0.2707753092089583],[-0.1252402139254379,0.5392796434434629,-0.8327618837239141]]],"distanceMatrix":[[0.0,0.0,1.0310894917467153,2.8880159414151887,2.093290262694123,1.4545247945775686],[0.0,0.0,1.0310894917467153,2.8880159414151887,2.093290262694123,1.4545247945775686],[1.0310894917467153,1.0310894917467153,0.0,2.3897177289769616,2.5942831029112843,2.007132847900268],[2.8880159414151887,2.8880159414151887,2.3897177289769616,0.0,0.8901855887877429,1.6294261193919053],[2.093290262694123,2.093290262694123,2.5942831029112843,0.8901855887877429,0.0,1.6798536479870154],[1.4545247945775686,1.4545247945775686,2.007132847900268,1.6294261193919053,1.6798536479870154,0.0]],"dot":[1.0,0.9297797928993878,0.9861454428529799,0.35899622958420685,0.9423004406924904],"dotOuter":[[1.0,0.9297797928993878,0.8879970194693607,0.9429168949435389,0.7230885510247699],[1.0,0.9297797928993878,0.8879970194693609,0.9429168949435389,0.7230885510247699],[0.870024286856333,0.9561056064554204,0.9861454428529799,0.7262011669047612,0.3484184774104984],[0.12644893580926161,0.22800854273389992,0.34166946230106376,0.35899622958420685,0.7757887550672475],[0.5004783333074398,0.4024023001767634,0.33592765198026986,0.5866840343814448,0.9423004406924904],[0.7469972318436724,0.527676201036471,0.49141160097843445,0.8642697742718877,0.7706852672031262]],"symbol":"6/m"},{"A":[[[0.7118825854623548,0.6952108340699085,0.09952427195038968],[-0.591114446809163,0.5166141603513703,0.619429996123812],[0.3792187960649235,-0.4997916621115951,0.7787177917543678]],[[-0.7118825854623548,-0.6952108340699085,-0.09952427195038968],[-0.591114446809163,0.5166141603513703,0.619429996123812],[-0.3792187960649235,0.4997916621115951,-0.7787177917543678]],[[-0.05947685957900828,0.22928796875814633,0.9715397730187798],[0.9376587523201276,0.34669834135684907,-0.024419752207405998],[-0.342430383249034,0.9095203612251751,-0.23561439927293631]],[[-0.8420354973423032,-0.5163783479897022,0.15597955937534175],[-0.5393022120440469,0.8119816691433706,-0.2232462610202993],[-0.011373007513297465,-0.27210139783281495,-0.9622013739324686]],[[0.931805877738764,-0.0015597098994214464,-0.3629536795742967],[0.3161606272611262,-0.4876681615771318,0.8137703742173414],[-0.17827019936417532,-0.8730276808263358,-0.45391894048348214]],[[-0.8882196922584668,-0.004277292166149971,-0.45939904555408034],[-0.35674448697444117,-0.6236533558106082,0.6955500433466377],[-0.28948082716756457,0.7816893222744521,0.5524152913746057]]],"B":[[[-0.7118825854623548,-0.6952108340699085,-0.09952427195038968],[0.591114446809163,-0.5166141603513703,-0.619429996123812],[0.3792187960649235,-0.4997916621115951,0.7787177917543678]],[[-0.36000460319859134,-0.40315469904153906,0.8413459302311688],[-0.9270230150125263,0.256075459070995,-0.2739592832861562],[-0.10500007291859387,-0.87857364398342,-0.46592202865370586]],[[0.6159066703422489,0.6267971449957882,-0.4772675480828897],[0.6237154452353452,-0.7580451453468988,-0.1906478454923483],[-0.4812878731100541,-0.1802578615237853,-0.8578281218030057]],[[0.3066884326610481,-0.6064717114647147,0.7335763548976818],[0.7681482080241084,-0.29739905971730474,-0.5670115781786244],[0.5620414004157176,0.7373912546757814,0.37465130687936504]],[[-0.1332790030737651,0.8341299977083317,0.5352231817314737],[-0.8717362381964319,0.158256432189326,-0.4637141713230677],[-0.4715004118288737,-0.5283768055032355,0.706049086856855]]],"distanceMatrix":[[0.0,0.0,0.7160308600540858,0.9517694392193087,1.5683964214020076,1.5353793765341912],[0.0,0.0,0.7160308600540858,0.9517694392193087,1.5683964214020076,1.5353793765341912],[0.7160308600540858,0.7160308600540858,0.0,1.5664090871456355,0.9150949538247892,0.8273767384909123],[0.9517694392193087,0.9517694392193087,1.5664090871456355,0.0,0.8860729978565509,0.7608746546125184],[1.5683964214020076,1.5683964214020076,0.9150949538247892,0.8860729978565509,0.0,0.5903111068563118],[1.5353793765341912,1.5353793765341912,0.8273767384909123,0.7608746546125184,0.5903111068563118,0.0]],"dot":[1.0,0.700276198445068,0.7515690018695079,0.8848824951101397,0.757651373143307],"dotOuter":[[1.0,0.700276198445068,0.9307973472705022,0.7459157536843979,0.9041285109461057],[1.0,0.700276198445068,0.9307973472705021,0.745915753684398,0.9041285109461056],[0.9365940930619241,0.8907135072672552,0.7515690018695079,0.8078159420839882,0.8596281770898843],[0.8888877573461708,0.8939886781627803,0.9625458044559465,0.8848824951101397,0.8726996637414959],[0.7079547665948325,0.995580315622917,0.8955715269166521,0.95086875394835,0.757651373143307],[0.7195170416518022,0.9478125878115118,0.8539537449301375,0.8950454700773736,0.7374875575764474]],"symbol":"6/mmm"},{"A":[[[-0.42557876876077505,0.9044599884600195,-0.028893612701842097],[0.31658001993783286,0.17872140599909997,0.9315770231246937],[0.8477380506714465,0.3873122620220101,-0.36239399709593667]],[[0.42557876876077505,-0.9044599884600195,0.028893612701842097],[0.31658001993783286,0.17872140599909997,0.9315770231246937],[-0.8477380506714465,-0.3873122620220101,0.36239399709593667]],[[-0.6887783406650669,0.6848769836079867,0.237755998356934],[-0.4296148388159167,-0.6497582353535627,0.6270927569820895],[0.5839653137814358,0.32978440368557416,0.7417727141017655]],[[-0.5838421526835367,-0.7688313700970377,-0.260819219200897],[-0.6177004020132186,0.21218550717067344,0.757247333372282],[-0.5268534465129384,0.6032210497831088,-0.5987902896640881]],[[0.3900773943702221,-0.8834147477743001,0.25964978300820424],[-0.10927870785080662,0.23557958731807127,0.9656916806359599],[-0.9142744612050562,-0.4050686873283549,-0.004644150545276338]],[[0.43971586980661376,0.09556165143439749,0.8930385907760892],[0.8253719316330124,0.3490653161687112,-0.4437505825578456],[-0.35413433653431964,0.9322131600782988,0.07461565428399625]]],"B":[[[-0.8477380506714465,-0.3873122620220101,0.36239399709593667],[0.42557876876077505,-0.9044599884600195,0.028893612701842097],[0.31658001993783286,0.17872140599909997,0.9315770231246937]],[[0.960263133017638,-0.2544109283739925,-0.11475972678179962],[0.2790894431986555,0.8781802723350526,0.38846942218995484],[0.0019488617740821695,-0.40506109268283985,0.9142875440102908]],[[0.10219864247737766,-0.23485479525598335,0.966642986123135],[-0.845842120441169,-0.5319454340520275,-0.03981409899504257],[0.5235518548944013,-0.8135584062232228,-0.25301417924725417]],[[0.253232651663755,0.3289752558899295,0.9097518920801969],[-0.7196393341352336,0.6925372205632131,-0.050114138723286517],[-0.6465233583535405,-0.6420027096101188,0.4121165708346663]],[[0.18157263995017614,0.698685490519375,-0.6920043076160902],[0.8895597004021553,-0.41666522997313526,-0.1872795385296064],[-0.4191836302170586,-0.5815743043404986,-0.6971774614034175]]],"distanceMatrix":[[0.0,0.0,1.179595416149386,1.0073483453750531,0.43951259970733336,0.15625804571064045],[0.0,0.0,1.179595416149386,1.0073483453750531,0.43951259970733336,0.15625804571064045],[1.179595416149386,1.179595416149386,0.0,1.000429728024037,1.0573495214379327,1.1633990354962145],[1.0073483453750531,1.0073483453750531,1.000429728024037,0.0,1.1926722665634746,1.0244484979120798],[0.43951259970733336,0.43951259970733336,1.0573495214379327,1.1926722665634746,0.0,0.5798328257676334],[0.15625804571064045,0.15625804571064045,1.1633990354962145,1.0244484979120798,0.5798328257676334,0.0]],"dot":[1.0,0.8946139675898135,0.9208317737786043,0.8934156237835227,0.8480455443256398],"dotOuter":[[1.0,0.8946139675898136,0.968358030444268,0.8104891098143582,0.9098704183567541],[1.0,0.8946139675898135,0.968358030444268,0.8104891098143582,0.9098704183567542],[0.8310532094404154,0.844923326258326,0.9208317737786043,0.9801271433240992,0.7881017824385629],[0.8758151501580183,0.8853380528994454,0.9185785496030359,0.8934156237835227,0.8616117977204659],[0.9759506029419716,0.8842659239346915,0.963485250234147,0.8258312030698385,0.8480455443256398],[0.9969494801017375,0.9058335321548228,0.9636637924321114,0.8099381911349305,0.9056422928467562]],"symbol":"m-3"},{"A":[[[0.013368621681777602,0.8989900712034365,-0.4377649275946725],[0.7607789742001642,-0.29324964549212,-0.5789818631301773],[-0.6488733561869179,-0.3253021630674861,-0.6878530877551826]],[[-0.7607789742001642,0.29324964549212,0.5789818631301773],[-0.013368621681777602,-0.8989900712034365,0.4377649275946725],[0.6488733561869179,0.3253021630674861,0.6878530877551826]],[[0.33321603055822857,-0.4968675981735633,0.8013049774363392],[0.9342176251607237,0.05924784520452547,-0.3517486626522951],[0.12729691990804481,0.8658015261732074,0.48392479938319943]],[[0.4719465342527275,-0.6925214948748447,-0.5456009970144385],[0.6716530685752212,-0.11845502112687856,0.7313347820549218],[-0.5710942341508346,-0.7116054996317419,0.4092297504013249]],[[-0.9740324638270189,0.1983400240155307,-0.10918788524642371],[0.2189913945631417,0.7028991820898959,-0.6767388779467648],[-0.05747633007036797,-0.6830768439135341,-0.7280813806098657]],[[-0.48652196101090295,-0.8409259251682653,-0.236938747008595],[0.8567636793546516,-0.5123189701478016,0.059036180138759456],[-0.17103326925714055,-0.1742781145370626,0.9697292197312719]]],"B":[[[-0.6488733561869179,-0.3253021630674861,-0.6878530877551826],[0.7607789742001642,-0.29324964549212,-0.5789818631301773],[-0.013368621681777602,-0.8989900712034365,0.4377649275946725]],[[0.09131306672963396,-0.5380117211769101,-0.837976915983184],[0.9937166717972842,0.1039219204277409,0.04156212995912084],[0.06472325730800316,-0.836506797540145,0.5441206462105375]],[[-0.5014688390924232,0.07278747511516537,-0.8621084542478729],[0.5810515166397014,0.7666216332129471,-0.27325886353532336],[0.6410211684726178,-0.6379602299174086,-0.4267301332384753]],[[-0.23324102628148957,0.9503492820347855,0.20599724705715147],[-0.9300589600077415,-0.1561744895582845,-0.3325655720614022],[-0.2838819377284788,-0.2691575206959242,0.9203071631169384]],[[0.962860478477972,-0.08383667876207508,0.2566536777046373],[-0.007339310902099314,-0.958346825037454,-0.28551269228549864],[0.26989967302805973,0.27302522637070764,-0.9233695859538424]]],"distanceMatrix":[[0.0,0.0,0.8043645573055292,0.6394365734275991,0.7760566506900676,0.9862970572837038],[0.0,0.0,0.8043645573055292,0.6394365734275991,0.7760566506900676,0.9862970572837038],[0.8043645573055292,0.8043645573055292,0.0,0.8010847317309344,0.6185733104174598,0.7520329496092768],[0.6394365734275991,0.6394365734275991,0.8010847317309344,0.0,0.7205286648463355,0.9756926816039027],[0.7760566506900676,0.7760566506900676,0.6185733104174598,0.7205286648463355,0.0,0.6968974304466603],[0.9862970572837038,0.9862970572837038,0.7520329496092768,0.9756926816039027,0.6968974304466603,0.0]],"dot":[1.0,0.9231668539207399,0.9021772125719139,0.930090769150625,0.9645370461318225],"dotOuter":[[1.0,0.9231668539207399,0.9530370001698241,0.9102185093800966,0.8903062569437799],[1.0,0.9231668539207399,0.9530370001698241,0.9102185093800966,0.89030625694378],[0.9202089821397823,0.9556146184406318,0.9021772125719139,0.9260871313782869,0.9432309795114356],[0.9493239979939373,0.913697068546865,0.9927576869262131,0.9300907691506248,0.920138788440241],[0.9256568694422348,0.9831381648202211,0.8891190690137717,0.9448741383596914,0.9645370461318225],[0.8808467085882791,0.8772236256205075,0.9322553499676551,0.9299030123638263,0.9681590673964181]],"symbol":"m-3m"}]} diff --git a/Tests/DiffractoOrientationTests/OrixOrientationAPITests.swift b/Tests/DiffractoOrientationTests/OrixOrientationAPITests.swift new file mode 100644 index 0000000..2bccdd6 --- /dev/null +++ b/Tests/DiffractoOrientationTests/OrixOrientationAPITests.swift @@ -0,0 +1,103 @@ +import DiffractoKitTestSupport +import Foundation +import Testing +import simd +import DiffractoCore +import DiffractoOrientation + +/// Pins: tools/pins/orix/orientation-api-pins.py (orix 0.15.0). Tolerances of orix test_orientation.py: `from_euler` atol 1e-4 +/// (`test_from_euler_symmetry`), the rest np.allclose defaults (rtol 1e-5, atol 1e-8) element by element; the measured worst gaps +/// are printed (below 1e-15 when written). +private struct Pins: Decodable { + struct EulerItem: Decodable { let euler: [Double]; let lab2crystal, crystal2lab: [[Double]] } + struct Set: Decodable { + let symbol: String + let A, B: [[[Double]]] + let dot: [Double] + let dotOuter, distanceMatrix: [[Double]] + } + let euler: [EulerItem] + let sets: [Set] +} +private func mat(_ r: [[Double]]) -> simd_double3x3 { .init(rows: r.map { SIMD3($0[0], $0[1], $0[2]) }) } +private func gap(_ a: simd_double3x3, _ b: simd_double3x3) -> Double { + var g = 0.0 + for c in 0 ..< 3 { for r in 0 ..< 3 { g = max(g, abs(a[c][r] - b[c][r])) } } + return g +} +/// np.allclose defaults: |x - want| <= 1e-8 + 1e-5 |want|. +private func close(_ x: Double, _ want: Double) -> Bool { abs(x - want) <= 1e-8 + 1e-5 * abs(want) } +private func symmetry(_ symbol: String) -> CrystalOrientationSymmetry { + if symbol == "-31m" { return CrystalOrientationSymmetry(laueClass: .threeBarM, setting: .threeBarOneM) } + return pinnedSymmetry(symbol)! +} + +struct OrixOrientationAPITests { + private func pins() throws -> Pins { + let file = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent("Fixtures/orix-orientation-api-pins.json") + return try JSONDecoder().decode(Pins.self, from: Data(contentsOf: file)) + } + + @Test func fromEulerMatchesOrixInBothDirections() throws { + let cubic = CrystalOrientationSymmetry(laueClass: .mThreeBarM) + var worst = 0.0 + for item in try pins().euler { + let e = SIMD3(item.euler[0], item.euler[1], item.euler[2]) + let lab = try Orientation(euler: e, symmetry: cubic) + let crystal = try Orientation(euler: e, symmetry: cubic, direction: .crystalToLab) + #expect(lab.symmetry.laueClass == .mThreeBarM && crystal.symmetry.laueClass == .mThreeBarM) + let g = max(gap(lab.matrix, mat(item.lab2crystal)), gap(crystal.matrix, mat(item.crystal2lab))) + worst = max(worst, g) + #expect(g < 1e-4, Comment(rawValue: "\(item.euler)")) // orix test_from_euler_symmetry: atol 1e-4 + } + print("ORIX from_euler worst: \(worst)") + } + + @Test func fromEulerRefusesNonFiniteAngles() { + let cubic = CrystalOrientationSymmetry(laueClass: .mThreeBarM) + #expect(throws: OrientationSymmetryError.invalidRotation) { try Orientation(euler: SIMD3(.nan, 0, 0), symmetry: cubic) } + #expect(throws: OrientationSymmetryError.invalidRotation) { try Orientation(euler: SIMD3(0, .infinity, 0), symmetry: cubic) } + } + + @Test func dotDotOuterAndDistanceMatrixMatchOrixForEveryClass() throws { + var worstDot = 0.0, worstDistance = 0.0 + for set in try pins().sets { + let s = symmetry(set.symbol) + let a = try set.A.map { try Orientation(mat($0), symmetry: s) } + let b = try set.B.map { try Orientation(mat($0), symmetry: s) } + for (i, want) in set.dot.enumerated() { + let d = try a[i].dot(b[i]) + worstDot = max(worstDot, abs(d - want)) + #expect(close(d, want), Comment(rawValue: "\(set.symbol) dot \(i)")) + } + let outer = try Orientation.dotOuter(a, b) + #expect(outer.count == a.count && outer.allSatisfy { $0.count == b.count }) + for i in a.indices { for j in b.indices { + worstDot = max(worstDot, abs(outer[i][j] - set.dotOuter[i][j])) + #expect(close(outer[i][j], set.dotOuter[i][j]), Comment(rawValue: "\(set.symbol) dotOuter \(i) \(j)")) + } } + let distance = try Orientation.distanceMatrix(a) + #expect(distance.count == a.count && distance.allSatisfy { $0.count == a.count }) + for i in a.indices { for j in a.indices { + worstDistance = max(worstDistance, abs(distance[i][j] - set.distanceMatrix[i][j])) + #expect(close(distance[i][j], set.distanceMatrix[i][j]), Comment(rawValue: "\(set.symbol) distance \(i) \(j)")) + } } + // A[1] and B[0] are symmetry equivalents of A[0]: the dot is 1 and the rounded, clamped distance exactly 0. + #expect(distance[0][0] == 0 && distance[0][1] == 0 && distance[1][0] == 0, Comment(rawValue: "\(set.symbol) equivalent distance")) + let selfDot = try a[0].dot(a[1]) + #expect(abs(outer[0][0] - 1) < 1e-12 && abs(selfDot - 1) < 1e-12, Comment(rawValue: "\(set.symbol) equivalent dot")) + // The distance is the disorientation angle of `angle(with:)`. + #expect(close(distance[1][2], try a[1].angle(with: a[2]))) + } + print("ORIX dot / dot_outer worst: \(worstDot), distance matrix worst: \(worstDistance)") + } + + @Test func dotRefusesDifferentSymmetries() throws { + let cubic = try Orientation(matrix_identity_double3x3, symmetry: CrystalOrientationSymmetry(laueClass: .mThreeBarM)) + let hexagonal = try Orientation(matrix_identity_double3x3, symmetry: CrystalOrientationSymmetry(laueClass: .sixOverMmm)) + #expect(throws: OrientationSymmetryError.differentSymmetry) { try cubic.dot(hexagonal) } + #expect(throws: OrientationSymmetryError.differentSymmetry) { try Orientation.dotOuter([cubic], [hexagonal]) } + #expect(throws: OrientationSymmetryError.differentSymmetry) { try Orientation.distanceMatrix([cubic, hexagonal]) } + #expect(try Orientation.distanceMatrix([]).isEmpty) + } +} diff --git a/docs/open-items.md b/docs/open-items.md index 7e31ee7..bae25e7 100644 --- a/docs/open-items.md +++ b/docs/open-items.md @@ -31,9 +31,10 @@ A plain list, worst first. Each line: the item, the cause where it is known, wha find it. - **Ring fit.** `fitRing` at R 600 px on 2048² takes tens of seconds (a coarse-then-fine annulus, or Metal). A one-ring profile model that tolerates a neighbouring ring is missing (the {111} bias of `RingFitNeighbourTailTests` is the model's, not a bug). -- **orix API gaps.** `Orientation.from_euler(symmetry:)`, `dot`, `dot_outer`, `get_distance_matrix` and the orientation - (Rodrigues) fundamental zone. orix's `Misorientation.reduce()` misses the smallest angle when the two groups' product set is - not symmetric (432 with 622, 23 with 3): it builds its zone from start . end while its equivalents need end . start (cause and - pins in `Misorientation.swift`); the kit returns the smallest angle. Not reported upstream yet. +- **orix API gaps.** The orientation (Rodrigues) fundamental zone, and `dot` / `dot_outer` / `get_distance_matrix` across two + different groups (`from_euler`, `dot`, `dot_outer` and `get_distance_matrix` for one group are held). orix's + `Misorientation.reduce()` misses the smallest angle when the two groups' product set is not a group (432 with 622, 23 with + 3): it builds its zone from start . end while its equivalents need end . start (cause and pins in `Misorientation.swift`); + the kit returns the smallest angle. Not reported upstream yet. - **Not yet used by a known consumer** (0.10.0): `PatternCentre.find`, `PatternRadialProfile.about`, `LinearScale.fit` and `Misorientation`. Revisit at 0.12. diff --git a/docs/parity.md b/docs/parity.md index 12286bd..64676ba 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -296,8 +296,8 @@ Single-pattern and calibration helpers the apps asked for that no Python package | `Rotation.random_vonmises`, `von_mises`, `Rotation.get_random_sample` | none | not held | not planned (random orientation spreads are for plotting and tests) | `test_quaternion/test_rotation.py` (no value test) | | | `Orientation.reduce`, `Orientation.angle_with`, `Misorientation.reduce` (symmetry reduction, disorientation) with the same group on both sides | `CrystalOrientationSymmetry.reduce(_:)`, `disorientation(from:to:)` | **held** | study | np.allclose defaults (rtol 1e-5, atol 1e-8); `atol=1e-3` (`test_set_symmetry`) in `test_quaternion/test_orientation.py` | P27, orix 0.15.0 executed, all 11 classes; `reduce` Float angle and disorientation within orix's allclose (atol 1e-8 + rtol 1e-5 of the angle); orix's `Quaternion.from_matrix` is imprecise near a half-turn (1.3e-5 rad), so the pins build quaternions with SciPy | | `Misorientation((s1, s2))` with two different symmetries (phase boundaries, orientation relationships), `equivalent`, `map_into_symmetry_reduced_zone` / `reduce` | `Misorientation` (`equivalent(grainExchange:)`, `reduce()`, 0.10.0), `OrientationVariantSet`, `OrientationRelationship.variants(of:matrix:precipitate:)`, `.rotation(_:_:precipitate:matrix:)` | **held** | study | np.allclose defaults (rtol 1e-5, atol 1e-8); `test_quaternion/test_misorientation.py` (`atol=1e-4`) | P36, orix 0.15.0 executed: 12 group pairs (m-3m with 2/m, 6/mmm and m-3m; 2/m with m-3m; 6/mmm with 2/m; 4/mmm with mmm; m-3 with -3; -31m with -3m1; -1 with m-3m; mmm; 6/m with 4/m; -3m1 with 6/mmm), 41 draws each: the equivalents as sets (6e-16), the distinct count, the smallest angle (4e-15), the reduced matrix where it is unique, the grain-exchange count. **DEVIATION**: orix's `reduce()` is the equivalent inside its misorientation fundamental zone, which is documented as the smallest angle and is not for every pair: it returns a larger angle on 1 of 41 draws of (m-3m, 6/mmm) and 3 of 41 of (m-3, -3) (2.18 against 0.92 rad on one); the kit returns the smallest angle (compared with orix where orix is minimal, with brute force where it is not). beta'' in Al: 24 distinct equivalents, reduced angle arctan(2/3) = 33.690068 degrees, 12 variants (planted truth, `OrientationVariantsTests`: four needle variants per <001> axis, variant-to-variant angles 0/90/120/180, nearest variant of 48 noisy orientations exact); no Python reference computes the variant set | -| `Orientation` (`from_euler(symmetry=)`, `from_matrix`, `from_axes_angles`, `symmetry`, `unit`, `in_euler_fundamental_region`, `dot`, `dot_outer`) | `Orientation(_:symmetry:)` with `reduce()`, `angle(with:)`, `inEulerFundamentalRegion()` (0.10.0; `OrientationResult` still holds Euler angles and a score) | **partly** | study | np.allclose defaults (rtol 1e-5, atol 1e-8); `atol=1e-4` (`test_from_euler_symmetry`) in `test_quaternion/test_orientation.py` | P36, orix 0.15.0 executed: `in_euler_fundamental_region` for the 11 classes and -3m in both settings (kit settings, 61 draws each; orix's proper-subgroup names 1, 121, 222, 4, 422, 3, 312, 32, 6, 622, 23, 432), Euler angles within 2e-15. Not held: `from_euler(symmetry=)`, `from_matrix`, `from_axes_angles`, `dot`, `dot_outer` | -| `Orientation.get_distance_matrix`, `Misorientation.get_distance_matrix`, `angle_with_outer` | none | not held | useful | `atol=1e-4` in `test_quaternion/test_misorientation.py`; `atol=1e-6` in `test_quaternion/test_orientation.py` | | +| `Orientation` (`from_euler(symmetry=, direction=)`, `from_matrix`, `from_axes_angles`, `symmetry`, `unit`, `in_euler_fundamental_region`, `dot`, `dot_outer`, `get_distance_matrix`) | `Orientation(_:symmetry:)` with `reduce()`, `angle(with:)`, `inEulerFundamentalRegion()` (0.10.0; `OrientationResult` still holds Euler angles and a score); `Orientation(euler:symmetry:direction:)`, `dot(_:)`, `Orientation.dotOuter(_:_:)`, `Orientation.distanceMatrix(_:)` | **partly** | study | np.allclose defaults (rtol 1e-5, atol 1e-8); `atol=1e-4` (`test_from_euler_symmetry`) in `test_quaternion/test_orientation.py` | P36, orix 0.15.0 executed: `in_euler_fundamental_region` for the 11 classes and -3m in both settings (kit settings, 61 draws each; orix's proper-subgroup names 1, 121, 222, 4, 422, 3, 312, 32, 6, 622, 23, 432), Euler angles within 2e-15. `from_euler` (36 Bunge triplets with the gimbal-lock and half-turn edges, both directions; matrix within 3.3e-16, held at orix's atol 1e-4), `dot`, `dot_outer` and `get_distance_matrix` (the 12 class/setting entries, six and five orientations each, two of them symmetry equivalents of the first: dot 1, distance exactly 0; within 8.9e-16; the distance matrix rounds the dot product to 12 decimals as orix's kernel does) held at np.allclose defaults (`OrixOrientationAPITests`, `tools/pins/orix/orientation-api-pins.py`). `dot`, `dotOuter` and `distanceMatrix` refuse two different groups (`differentSymmetry`, as `angle(with:)`): orix's two-group `dot` searches `sym1.outer(sym2)`, which with equivalents `S * M` is the inverse of the set it needs (the cause of the `Misorientation.reduce` DEVIATION in the row above), and for 432 with 622 or 23 with 3 it differs from the largest dot over equivalent pairs (by up to 0.08 in a 200-draw check); not held. Not held: `from_matrix` beyond the matrix initialiser, `from_axes_angles` | +| `Misorientation.get_distance_matrix`, `angle_with_outer` (`Orientation.get_distance_matrix` is held, row above) | none | not held | useful | `atol=1e-4` in `test_quaternion/test_misorientation.py`; `atol=1e-6` in `test_quaternion/test_orientation.py` | | | `Symmetry` as a set of 32 point groups (`order`, `is_proper`, `subgroups`, `proper_subgroup`, `contains_inversion`, `diads`, `from_generators`, `system`) and the 11 Laue groups (`laue`, `laue_proper_subgroup`) | `PointSymmetry`, `LaueClass` (the 11 Laue classes as integer rotations acting on hkl), `CrystalOrientationSymmetry.operators` (proper operators as matrices) | **partly** | study | equality of operator sets, no numeric tolerance (`test_quaternion/test_symmetry.py`) | operation counts and rotation sets per class vs pymatgen/spglib (P2, P3, P17), and vs orix as asserted operator sets inside the P26/P27/P32 generators (the Swift tests compare the kit with the resulting numbers, not with the operator sets); the 21 non-centrosymmetric point groups, `subgroups` and `from_generators` are not held | | `get_point_group(space_group_number, proper=False)` | none (`PhaseModel.orientationClass(of:)` gives the Laue class of a detected structure) | not held | study | exact equality for all 230 numbers (`test_quaternion/test_symmetry.py::test_get_point_group`) | needs the space group number (ROADMAP item 2); `PhaseModel.detectedOrientationClassIsSpglibsLaueClassOrNil` (P29) checks 15 groups against spglib's Laue class | | `Symmetry.fundamental_sector` (`FundamentalSector`: `vertices`, `center`, `edges`), `fundamental_zone`, `euler_fundamental_region` | internal sector tables generated from orix (`OrixIPFSectors.swift`, P26); `CrystalOrientationSymmetry.sampleFundamentalZone(count:)` for the directions | **partly** | study | `atol=1e-3`, `1e-4` for the 32 sectors in `test_quaternion/test_symmetry.py`; `atol=1e-3` in `test_vector3d/test_fundamental_sector.py` | the sectors enter only through the colour key (P26) and the sector-corner pins; they are not a public type and `euler_fundamental_region` is held as a private table behind `Orientation.inEulerFundamentalRegion()` (P36) | diff --git a/tools/consumer-check/Sources/consumer-check/OrientationAndScaleChecks.swift b/tools/consumer-check/Sources/consumer-check/OrientationAndScaleChecks.swift index b3aacc3..3aed032 100644 --- a/tools/consumer-check/Sources/consumer-check/OrientationAndScaleChecks.swift +++ b/tools/consumer-check/Sources/consumer-check/OrientationAndScaleChecks.swift @@ -28,6 +28,11 @@ func runOrientationAndScaleChecks(_ r: inout Report) throws { let o = try Orientation(matrix_identity_double3x3, symmetry: cubic) r.check("the Euler region of the identity is the origin", o.inEulerFundamentalRegion() == SIMD3(0, 0, 0)) r.check("two groups cannot be compared", (try? o.angle(with: Orientation(matrix_identity_double3x3, symmetry: monoclinic))) == nil) + // a quarter turn about z is a cubic symmetry operator: the reduced dot is 1, the distance 0 + let quarter = try Orientation(euler: SIMD3(Double.pi / 2, 0, 0), symmetry: cubic, direction: .labToCrystal) + r.near("a cubic quarter turn has dot 1 with the identity", try o.dot(quarter), 1, 1e-12) + r.equal("the distance matrix is n x n", try Orientation.distanceMatrix([o, quarter]).count, 2) + r.equal("dot_outer is |a| x |b|", try Orientation.dotOuter([o], [o, quarter])[0].count, 2) // ring-sequence assignment on Al [001] let al = [3.0, 4.0, 8.0, 11.0].map { $0.squareRoot() / 4.0495 } diff --git a/tools/pins/orix/orientation-api-pins.py b/tools/pins/orix/orientation-api-pins.py new file mode 100644 index 0000000..f6d2777 --- /dev/null +++ b/tools/pins/orix/orientation-api-pins.py @@ -0,0 +1,71 @@ +"""orix Orientation API pins: `from_euler(symmetry=, direction=)`, `dot`, `dot_outer`, `get_distance_matrix`. + + ../References/python/orix/bin/python -I -B tools/pins/orix/orientation-api-pins.py + +Writes Tests/DiffractoOrientationTests/Fixtures/orix-orientation-api-pins.json. The oracle is orix 0.15.0. The symmetry groups +and the quaternion helper come from misorientation-pins.py next to this file (the kit's own generators and settings). + * `Orientation.from_euler(euler, symmetry, direction)`: the rotation matrix, Bunge angles in radians, both directions, + random triplets plus the gimbal-lock and half-turn edges. + * For each of the twelve class/setting entries: six orientations A and five B (A[1] and B[0] symmetry equivalents of A[0], + the last operator, so the dot is 1 and the distance 0); `A[:5].dot(B)`, `A.dot_outer(B)` and `A.get_distance_matrix()`. +Tolerance: atol 1e-4 for from_euler (test_from_euler_symmetry), np.allclose defaults (rtol 1e-5, atol 1e-8) for the rest +(orix test_orientation.py). +""" +import importlib.metadata as md +import importlib.util +import json +from pathlib import Path +import numpy as np +from orix.quaternion import Orientation +from scipy.spatial.transform import Rotation as SR + +HERE = Path(__file__).resolve().parent +KIT = Path(__file__).resolve().parents[3] +FIX = KIT / 'Tests/DiffractoOrientationTests/Fixtures' +SEED = 20261031 +assert md.version('orix') == '0.15.0' + +spec = importlib.util.spec_from_file_location('misorientation_pins', HERE / 'misorientation-pins.py') +P = importlib.util.module_from_spec(spec) +spec.loader.exec_module(P) + +SYMBOLS = ['-1', '2/m', 'mmm', '4/m', '4/mmm', '-3', '-31m', '-3m1', '6/m', '6/mmm', 'm-3', 'm-3m'] + + +def main(): + rng = np.random.default_rng(SEED) + out = {'orix': md.version('orix'), 'seed': SEED, 'euler': [], 'sets': []} + + edges = [[0, 0, 0], [0.3, 0, 0.4], [0.3, np.pi, 0.4], [np.pi, np.pi / 2, np.pi], [2 * np.pi - 1e-3, 1e-3, 1e-3], [1.0, 2.0, 5.5]] + triplets = edges + (rng.random((30, 3)) * np.array([2 * np.pi, np.pi, 2 * np.pi])).tolist() + s_cubic, _ = P.symmetry('m-3m') + for eu in triplets: + rec = {'euler': [float(v) for v in eu]} + for direction in ('lab2crystal', 'crystal2lab'): + o = Orientation.from_euler(np.array(eu), symmetry=s_cubic, direction=direction) + assert o.symmetry.name == s_cubic.name + rec[direction] = P.mats(o)[0].tolist() + out['euler'].append(rec) + + for symbol in SYMBOLS: + s, ops = P.symmetry(symbol) + A = list(SR.random(6, random_state=rng).as_matrix()) + A[1] = ops[-1] @ A[0] # an equivalent of A[0]: distance 0 inside the distance matrix + B = [ops[len(ops) // 2] @ A[0]] + list(SR.random(4, random_state=rng).as_matrix()) + oa = Orientation(np.concatenate([P.rot(m).data for m in A]), symmetry=s) + ob = Orientation(np.concatenate([P.rot(m).data for m in B]), symmetry=s) + dot = oa[:5].dot(ob) + outer = oa.dot_outer(ob) + dist = oa.get_distance_matrix() + assert outer.shape == (6, 5) and dist.shape == (6, 6) + assert np.allclose(dot, np.diag(outer[:5])) + assert float(dist[0, 0]) == 0 and float(dist[0, 1]) == 0 and abs(float(outer[0, 0]) - 1) < 1e-12 and abs(float(oa[0].angle_with(ob[0])[0])) < 1e-6 + out['sets'].append({'symbol': symbol, 'A': [m.tolist() for m in A], 'B': [m.tolist() for m in B], + 'dot': [float(v) for v in dot], 'dotOuter': outer.tolist(), 'distanceMatrix': dist.tolist()}) + + (FIX / 'orix-orientation-api-pins.json').write_text(json.dumps(out, sort_keys=True, separators=(',', ':'), allow_nan=False) + '\n') + print('ok', len(out['euler']), 'Euler triplets,', len(out['sets']), 'symmetry sets') + + +if __name__ == '__main__': + main()