kim-tools 0.3.6__py3-none-any.whl → 0.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kim_tools/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.6"
1
+ __version__ = "0.3.7"
2
2
 
3
3
  from .aflow_util import *
4
4
  from .aflow_util import __all__ as aflow_all
@@ -398,9 +398,7 @@ def get_atom_indices_for_each_wyckoff_orb(prototype_label: str) -> List[Dict]:
398
398
  orbit.
399
399
 
400
400
  Returns:
401
- The information is in this format:
402
-
403
- [{"letter":"a", "indices":[0,1]}, ... ]
401
+ The information is in this format -- ``[{"letter":"a", "indices":[0,1]}, ... ]``
404
402
  """
405
403
  return_list = []
406
404
  wyck_lists = get_wyckoff_lists_from_prototype(prototype_label)
@@ -416,6 +414,45 @@ def get_atom_indices_for_each_wyckoff_orb(prototype_label: str) -> List[Dict]:
416
414
  return return_list
417
415
 
418
416
 
417
+ def get_all_equivalent_labels(prototype_label: str) -> List[str]:
418
+ """
419
+ Return all possible permutations of the Wyckoff letters in a prototype
420
+ label under the operations of the affine normalizer.
421
+
422
+ NOTE: For now this function will not completely enumerate the possibilities
423
+ for triclinic and monoclinic space groups
424
+ """
425
+ sgnum = get_space_group_number_from_prototype(prototype_label)
426
+ prototype_label_split = prototype_label.split("_")
427
+ equivalent_labels = []
428
+ for wyck_pos_xform in get_wyck_pos_xform_under_normalizer(sgnum):
429
+ prototype_label_split_permuted = prototype_label_split[:3]
430
+ for wycksec in prototype_label_split[3:]:
431
+ # list of letters joined with their nums, e.g. ["a", "2i"]
432
+ wycksec_permuted_list = []
433
+ prev_lett_ind = -1
434
+ for i, num_or_lett in enumerate(wycksec):
435
+ if isalpha(num_or_lett):
436
+ if num_or_lett == "A":
437
+ # Wyckoff position A comes after z in sg 47
438
+ lett_index = ord("z") + 1 - ord("a")
439
+ else:
440
+ lett_index = ord(num_or_lett) - ord("a")
441
+ # The start position of the (optional) numbers +
442
+ # letter describing this position
443
+ this_pos_start_ind = prev_lett_ind + 1
444
+ permuted_lett_and_num = wycksec[this_pos_start_ind:i]
445
+ permuted_lett_and_num += wyck_pos_xform[lett_index]
446
+ wycksec_permuted_list.append(permuted_lett_and_num)
447
+ prev_lett_ind = i
448
+ wycksec_permuted_list_sorted = sorted(
449
+ wycksec_permuted_list, key=lambda x: x[-1]
450
+ )
451
+ prototype_label_split_permuted.append("".join(wycksec_permuted_list_sorted))
452
+ equivalent_labels.append("_".join(prototype_label_split_permuted))
453
+ return list(set(equivalent_labels))
454
+
455
+
419
456
  def prototype_labels_are_equivalent(
420
457
  prototype_label_1: str,
421
458
  prototype_label_2: str,
@@ -1603,6 +1640,8 @@ class AFLOW:
1603
1640
  self.get_library_prototype_label_and_shortname_from_atoms(atoms)
1604
1641
  )
1605
1642
 
1643
+ # NOTE: Because of below, this only works if the provided prototype label is
1644
+ # correctly alphabetized. Change this?
1606
1645
  if not prototype_labels_are_equivalent(
1607
1646
  prototype_label, prototype_label_detected
1608
1647
  ):
@@ -1665,7 +1704,7 @@ class AFLOW:
1665
1704
  )
1666
1705
 
1667
1706
  position_set_list = get_equivalent_atom_sets_from_prototype_and_atom_map(
1668
- atoms, prototype_label, atom_map, sort_atoms=True
1707
+ atoms, prototype_label_detected, atom_map, sort_atoms=True
1669
1708
  )
1670
1709
 
1671
1710
  # get equation sets
@@ -12,6 +12,7 @@ from typing import Dict, List, Optional, Tuple, Union
12
12
  import matplotlib.pyplot as plt
13
13
  import numpy as np
14
14
  import numpy.typing as npt
15
+ import sympy as sp
15
16
  from ase import Atoms
16
17
  from ase.cell import Cell
17
18
  from ase.constraints import FixSymmetry
@@ -22,6 +23,7 @@ from pymatgen.core.tensors import Tensor
22
23
  from scipy.stats import kstest
23
24
  from sklearn.decomposition import PCA
24
25
  from sympy import Matrix, cos, matrix2numpy, sin, sqrt, symbols
26
+ from sympy.tensor.array.expressions import ArrayContraction, ArrayTensorProduct
25
27
 
26
28
  logger = logging.getLogger(__name__)
27
29
  logging.basicConfig(filename="kim-tools.log", level=logging.INFO, force=True)
@@ -805,6 +807,224 @@ def kstest_reduced_distances(
805
807
  )
806
808
 
807
809
 
810
+ def voigt_to_full_symb(voigt_input: sp.Array) -> sp.MutableDenseNDimArray:
811
+ """
812
+ Convert a 3-dimensional symbolic Voigt matrix to a full tensor. Order is
813
+ automatically detected. For now, only works with tensors that don't have special
814
+ scaling for the Voigt matrix (e.g. this doesn't work with the
815
+ compliance tensor)
816
+ """
817
+ order = sum(voigt_input.shape) // 3
818
+ this_voigt_map = Tensor.get_voigt_dict(order)
819
+ t = sp.MutableDenseNDimArray(np.zeros([3] * order))
820
+ for ind, v in this_voigt_map.items():
821
+ t[ind] = voigt_input[v]
822
+ return t
823
+
824
+
825
+ def full_to_voigt_symb(full: sp.Array) -> sp.MutableDenseNDimArray:
826
+ """
827
+ Convert a 3-dimensional symbolic full tensor to a Voigt matrix. Order is
828
+ automatically detected. For now, only works with tensors that don't have special
829
+ scaling for the Voigt matrix (e.g. this doesn't work with the
830
+ compliance tensor). No error checking is done to see if the
831
+ full tensor has the required symmetries to be converted to Voigt.
832
+ """
833
+ order = len(full.shape)
834
+ vshape = tuple([3] * (order % 2) + [6] * (order // 2))
835
+ v_matrix = sp.MutableDenseNDimArray(np.zeros(vshape))
836
+ this_voigt_map = Tensor.get_voigt_dict(order)
837
+ for ind, v in this_voigt_map.items():
838
+ v_matrix[v] = full[ind]
839
+ return v_matrix
840
+
841
+
842
+ def rotate_tensor_symb(t: sp.Array, r: sp.Array) -> sp.Array:
843
+ """
844
+ Rotate a 3-dimensional symbolic Cartesian tensor by a rotation matrix.
845
+
846
+ Args:
847
+ t: The tensor to rotate
848
+ r:
849
+ The rotation matrix, or a precomputed tensor product of rotation matrices
850
+ with the correct rank
851
+ """
852
+ order = len(t.shape)
853
+ if r.shape == (3, 3):
854
+ r_tenprod = [sp.Array(r)] * order
855
+ elif r.shape == tuple([3] * 2 * order):
856
+ r_tenprod = [sp.Array(r)]
857
+ else:
858
+ raise RuntimeError(
859
+ "r must be a 3x3 rotation matrix or a tensor product of n 3x3 rotation "
860
+ f"matrices, where n is the rank of t. Instead got shape f{r.shape}"
861
+ )
862
+ args = r_tenprod + [t]
863
+ fullproduct = ArrayTensorProduct(*args)
864
+ for i in range(order):
865
+ current_order = len(fullproduct.shape)
866
+ # Count back from end: one component of tensor,
867
+ # plus two components for each rotation matrix.
868
+ # Then, step forward by 2*i + 1 to land on the second
869
+ # component of the correct rotation matrix.
870
+ # but, step forward by i more, because we've knocked out
871
+ # that many components of the tensor already
872
+ # (the knocked out components of the rotation matrices
873
+ # are lower than the current component we are summing)
874
+ rotation_component = current_order - order * 3 + 3 * i + 1
875
+ tensor_component = current_order - order + i # Count back from end
876
+ fullproduct = ArrayContraction(
877
+ fullproduct, (rotation_component, tensor_component)
878
+ )
879
+ return fullproduct.as_explicit()
880
+
881
+
882
+ def fit_voigt_tensor_to_cell_and_space_group_symb(
883
+ symb_voigt_inp: sp.Array,
884
+ cell: npt.ArrayLike,
885
+ sgnum: Union[int, str],
886
+ ):
887
+ """
888
+ Given a Cartesian symbolic tensor in Voigt form, average it over all the operations
889
+ in the crystal's space group in order to remove violations of the material symmetry
890
+ due to numerical errors. Similar to
891
+ :meth:`pymatgen.core.tensors.Tensor.fit_to_structure`,
892
+ except the input in output are Voigt, and the symmetry operations are tabulated
893
+ instead of being detected on the fly from a structure.
894
+
895
+ The provided tensor and cell must be in the standard primitive
896
+ setting and orientation w.r.t. Cartesian coordinates as defined in
897
+ https://doi.org/10.1016/j.commatsci.2017.01.017
898
+
899
+ Args:
900
+ symb_voigt_inp:
901
+ Tensor in Voigt form as understood by
902
+ :meth:`pymatgen.core.tensors.Tensor.from_voigt`
903
+ cell:
904
+ The cell of the crystal, with each row being a cartesian vector
905
+ representing a lattice vector
906
+ sgnum:
907
+ Space group number
908
+
909
+ Returns:
910
+ Tensor symmetrized w.r.t. operations of the space group,
911
+ additionally the symmetrized error if `voigt_error`
912
+ is provided
913
+ """
914
+ t = voigt_to_full_symb(symb_voigt_inp)
915
+ order = len(t.shape)
916
+
917
+ # Precompute the average Q (x) Q (x) Q (x) Q for each
918
+ # Q in G, where (x) is tensor product. Better
919
+ # to do this with numpy, sympy is SLOW
920
+ r_tensprod_ave = np.zeros([3] * 2 * order, dtype=float)
921
+ space_group_ops = get_primitive_genpos_ops(sgnum)
922
+ for op in space_group_ops:
923
+ frac_rot = op["W"]
924
+ cart_rot = fractional_to_cartesian_itc_rotation_from_ase_cell(frac_rot, cell)
925
+ r_tensprod = 1
926
+ for _ in range(order):
927
+ # tensordot with axes=0 is tensor product
928
+ r_tensprod = np.tensordot(r_tensprod, cart_rot, axes=0)
929
+ r_tensprod_ave += r_tensprod
930
+ r_tensprod_ave /= len(space_group_ops)
931
+ t_symmetrized = rotate_tensor_symb(t, r_tensprod_ave)
932
+ return full_to_voigt_symb(t_symmetrized)
933
+
934
+
935
+ def fit_voigt_tensor_and_error_to_cell_and_space_group(
936
+ voigt_input: npt.ArrayLike,
937
+ voigt_error: npt.ArrayLike,
938
+ cell: npt.ArrayLike,
939
+ sgnum: Union[int, str],
940
+ symmetric: bool = False,
941
+ ) -> Tuple[npt.ArrayLike, npt.ArrayLike]:
942
+ """
943
+ Given a Cartesian Tensor and its errors in Voigt form, average them over
944
+ all the operations in the
945
+ crystal's space group in order to remove violations of the material symmetry due to
946
+ numerical errors. Similar to :meth:`pymatgen.core.tensors.Tensor.fit_to_structure`,
947
+ except the input in output are Voigt, and the symmetry operations are tabulated
948
+ instead of being detected on the fly from a structure.
949
+
950
+ Only use this function if you need the errors. If you do not,
951
+ use
952
+ :func:`fit_voigt_tensor_to_cell_and_space_group`, which is significantly faster.
953
+
954
+ The provided tensor and cell must be in the standard primitive
955
+ setting and orientation w.r.t. Cartesian coordinates as defined in
956
+ https://doi.org/10.1016/j.commatsci.2017.01.017
957
+
958
+ Args:
959
+ voigt_input:
960
+ Tensor in Voigt form as understood by
961
+ :meth:`pymatgen.core.tensors.Tensor.from_voigt`
962
+ voigt_error:
963
+ The error corresponding to voigt_input
964
+ cell:
965
+ The cell of the crystal, with each row being a cartesian vector
966
+ representing a lattice vector
967
+ sgnum:
968
+ Space group number
969
+ symmetric:
970
+ Whether the provided matrix is symmetric. Currently
971
+ only supported for 6x6 Voigt matrices
972
+
973
+ Returns:
974
+ Tensor symmetrized w.r.t. operations of the space group,
975
+ and its symmetrized error
976
+ """
977
+ # First, get the symmetrized tensor as a symbolic
978
+ voigt_shape = voigt_input.shape
979
+ symb_voigt_inp = sp.symarray("t", voigt_shape)
980
+ if symmetric:
981
+ if voigt_shape != (6, 6):
982
+ raise NotImplementedError(
983
+ "Symmetric input only supported for 6x6 Voigt matrices"
984
+ )
985
+ for i in range(5):
986
+ for j in range(i + 1, 6):
987
+ symb_voigt_inp[j, i] = symb_voigt_inp[i, j]
988
+
989
+ sym_voigt_out = fit_voigt_tensor_to_cell_and_space_group_symb(
990
+ symb_voigt_inp=symb_voigt_inp, cell=cell, sgnum=sgnum
991
+ )
992
+
993
+ # OK, got the symbolic voigt output. Set up machinery for
994
+ # substitution
995
+ voigt_ranges = [range(n) for n in voigt_shape]
996
+ # Convert to list so can be reused
997
+ voigt_ranges_product = list(product(*voigt_ranges))
998
+
999
+ # Substitute result. Symmetry not an issue, keys will get overwritten
1000
+ sub_dict = {}
1001
+ for symb, num in zip(symb_voigt_inp.flatten(), voigt_input.flatten()):
1002
+ sub_dict[symb] = num
1003
+
1004
+ sub_dict_err = {}
1005
+ for symb, num in zip(symb_voigt_inp.flatten(), voigt_error.flatten()):
1006
+ sub_dict_err[symb] = num
1007
+
1008
+ voigt_out = np.zeros(voigt_shape, dtype=float)
1009
+ voigt_err_out = np.zeros(voigt_shape, dtype=float)
1010
+ for indices in voigt_ranges_product:
1011
+ compon_expr = sym_voigt_out[indices]
1012
+ voigt_out[indices] = compon_expr.subs(sub_dict)
1013
+ # For the error, consider the current component (indicated by ``indices``)
1014
+ # as a random variable that is a linear combination of all the components
1015
+ # of voigt_inp. The variance of the
1016
+ # current component will be the sum of a_i^2 var_i, where a_i is the
1017
+ # coefficient of the ith component of voigt_inp
1018
+ voigt_out_var_compon = 0
1019
+ for symb in sub_dict_err:
1020
+ inp_compon_coeff = float(compon_expr.coeff(symb))
1021
+ inp_compon_var = sub_dict_err[symb] ** 2
1022
+ voigt_out_var_compon += inp_compon_coeff**2 * inp_compon_var
1023
+ voigt_err_out[indices] = voigt_out_var_compon**0.5
1024
+
1025
+ return voigt_out, voigt_err_out
1026
+
1027
+
808
1028
  def fit_voigt_tensor_to_cell_and_space_group(
809
1029
  voigt_input: npt.ArrayLike, cell: npt.ArrayLike, sgnum: Union[int, str]
810
1030
  ) -> npt.ArrayLike:
@@ -815,6 +1035,10 @@ def fit_voigt_tensor_to_cell_and_space_group(
815
1035
  except the input in output are Voigt, and the symmetry operations are tabulated
816
1036
  instead of being detected on the fly from a structure.
817
1037
 
1038
+ If you need to symmetrize the errors as well, use
1039
+ :func:`fit_voigt_tensor_and_error_to_cell_and_space_group`, which properly
1040
+ handles errors, but is much slower.
1041
+
818
1042
  The provided tensor and cell must be in the standard primitive
819
1043
  setting and orientation w.r.t. Cartesian coordinates as defined in
820
1044
  https://doi.org/10.1016/j.commatsci.2017.01.017
@@ -0,0 +1,105 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <!-- This file was generated by dvisvgm 2.13.1 -->
3
+ <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='350pt' height='350pt' viewBox='66.550958 63.999777 350 350'>
4
+ <g id='page1'>
5
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
6
+ <path d='M 10.1795 10.1795L 76.3702 76.3702L 142.561 142.561' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/>
7
+ </g>
8
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
9
+ <path d='M 76.3702 10.1795L 142.561 10.1795L 142.561 76.3702' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/>
10
+ </g>
11
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
12
+ <path d='M 208.752 208.752L 274.942 274.942L 341.133 341.133' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/>
13
+ </g>
14
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
15
+ <path d='M 20.1081 10.1795C 20.1081 4.69612 15.663 0.250938 10.1795 0.250937C 4.69612 0.250937 0.250937 4.69612 0.250937 10.1795C 0.250937 15.663 4.69612 20.1081 10.1795 20.1081C 15.663 20.1081 20.1081 15.663 20.1081 10.1795Z' fill='#000000'/>
16
+ </g>
17
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
18
+ <path d='M 20.1081 10.1795C 20.1081 4.69612 15.663 0.250938 10.1795 0.250937C 4.69612 0.250937 0.250937 4.69612 0.250937 10.1795C 0.250937 15.663 4.69612 20.1081 10.1795 20.1081C 15.663 20.1081 20.1081 15.663 20.1081 10.1795Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
19
+ </g>
20
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
21
+ <path d='M 86.2988 10.1795C 86.2988 4.69612 81.8536 0.250938 76.3702 0.250937C 70.8868 0.250937 66.4416 4.69612 66.4416 10.1795C 66.4416 15.663 70.8868 20.1081 76.3702 20.1081C 81.8536 20.1081 86.2988 15.663 86.2988 10.1795Z' fill='#000000'/>
22
+ </g>
23
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
24
+ <path d='M 86.2988 10.1795C 86.2988 4.69612 81.8536 0.250938 76.3702 0.250937C 70.8868 0.250937 66.4416 4.69612 66.4416 10.1795C 66.4416 15.663 70.8868 20.1081 76.3702 20.1081C 81.8536 20.1081 86.2988 15.663 86.2988 10.1795Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
25
+ </g>
26
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
27
+ <path d='M 152.49 10.1795C 152.49 4.69612 148.044 0.250938 142.561 0.250937C 137.077 0.250937 132.632 4.69612 132.632 10.1795C 132.632 15.663 137.077 20.1081 142.561 20.1081C 148.044 20.1081 152.49 15.663 152.49 10.1795Z' fill='#000000'/>
28
+ </g>
29
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
30
+ <path d='M 152.49 10.1795C 152.49 4.69612 148.044 0.250938 142.561 0.250937C 137.077 0.250937 132.632 4.69612 132.632 10.1795C 132.632 15.663 137.077 20.1081 142.561 20.1081C 148.044 20.1081 152.49 15.663 152.49 10.1795Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
31
+ </g>
32
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
33
+ <circle cx='208.752' cy='10.1795' fill='#000000' r='2.50937'/>
34
+ </g>
35
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
36
+ <circle cx='274.942' cy='10.1795' fill='#000000' r='2.50937'/>
37
+ </g>
38
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
39
+ <circle cx='341.133' cy='10.1795' fill='#000000' r='2.50937'/>
40
+ </g>
41
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
42
+ <path d='M 86.2988 76.3702C 86.2988 70.8868 81.8536 66.4416 76.3702 66.4416C 70.8868 66.4416 66.4416 70.8868 66.4416 76.3702C 66.4416 81.8536 70.8868 86.2988 76.3702 86.2988C 81.8536 86.2988 86.2988 81.8536 86.2988 76.3702Z' fill='#000000'/>
43
+ </g>
44
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
45
+ <path d='M 86.2988 76.3702C 86.2988 70.8868 81.8536 66.4416 76.3702 66.4416C 70.8868 66.4416 66.4416 70.8868 66.4416 76.3702C 66.4416 81.8536 70.8868 86.2988 76.3702 86.2988C 81.8536 86.2988 86.2988 81.8536 86.2988 76.3702Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
46
+ </g>
47
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
48
+ <path d='M 152.49 76.3702C 152.49 70.8868 148.044 66.4416 142.561 66.4416C 137.077 66.4416 132.632 70.8868 132.632 76.3702C 132.632 81.8536 137.077 86.2988 142.561 86.2988C 148.044 86.2988 152.49 81.8536 152.49 76.3702Z' fill='#000000'/>
49
+ </g>
50
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
51
+ <path d='M 152.49 76.3702C 152.49 70.8868 148.044 66.4416 142.561 66.4416C 137.077 66.4416 132.632 70.8868 132.632 76.3702C 132.632 81.8536 137.077 86.2988 142.561 86.2988C 148.044 86.2988 152.49 81.8536 152.49 76.3702Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
52
+ </g>
53
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
54
+ <circle cx='208.752' cy='76.3702' fill='#000000' r='2.50937'/>
55
+ </g>
56
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
57
+ <circle cx='274.942' cy='76.3702' fill='#000000' r='2.50937'/>
58
+ </g>
59
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
60
+ <circle cx='341.133' cy='76.3702' fill='#000000' r='2.50937'/>
61
+ </g>
62
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
63
+ <path d='M 152.49 142.561C 152.49 137.077 148.044 132.632 142.561 132.632C 137.077 132.632 132.632 137.077 132.632 142.561C 132.632 148.044 137.077 152.49 142.561 152.49C 148.044 152.49 152.49 148.044 152.49 142.561Z' fill='#000000'/>
64
+ </g>
65
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
66
+ <path d='M 152.49 142.561C 152.49 137.077 148.044 132.632 142.561 132.632C 137.077 132.632 132.632 137.077 132.632 142.561C 132.632 148.044 137.077 152.49 142.561 152.49C 148.044 152.49 152.49 148.044 152.49 142.561Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
67
+ </g>
68
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
69
+ <circle cx='208.752' cy='142.561' fill='#000000' r='2.50937'/>
70
+ </g>
71
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
72
+ <circle cx='274.942' cy='142.561' fill='#000000' r='2.50937'/>
73
+ </g>
74
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
75
+ <circle cx='341.133' cy='142.561' fill='#000000' r='2.50937'/>
76
+ </g>
77
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
78
+ <path d='M 218.68 208.752C 218.68 203.268 214.235 198.823 208.752 198.823C 203.268 198.823 198.823 203.268 198.823 208.752C 198.823 214.235 203.268 218.68 208.752 218.68C 214.235 218.68 218.68 214.235 218.68 208.752Z' fill='#000000'/>
79
+ </g>
80
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
81
+ <path d='M 218.68 208.752C 218.68 203.268 214.235 198.823 208.752 198.823C 203.268 198.823 198.823 203.268 198.823 208.752C 198.823 214.235 203.268 218.68 208.752 218.68C 214.235 218.68 218.68 214.235 218.68 208.752Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
82
+ </g>
83
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
84
+ <circle cx='274.942' cy='208.752' fill='#000000' r='2.50937'/>
85
+ </g>
86
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
87
+ <circle cx='341.133' cy='208.752' fill='#000000' r='2.50937'/>
88
+ </g>
89
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
90
+ <path d='M 284.871 274.942C 284.871 269.459 280.426 265.014 274.942 265.014C 269.459 265.014 265.014 269.459 265.014 274.942C 265.014 280.426 269.459 284.871 274.942 284.871C 280.426 284.871 284.871 280.426 284.871 274.942Z' fill='#000000'/>
91
+ </g>
92
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
93
+ <path d='M 284.871 274.942C 284.871 269.459 280.426 265.014 274.942 265.014C 269.459 265.014 265.014 269.459 265.014 274.942C 265.014 280.426 269.459 284.871 274.942 284.871C 280.426 284.871 284.871 280.426 284.871 274.942Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
94
+ </g>
95
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
96
+ <circle cx='341.133' cy='274.942' fill='#000000' r='2.50937'/>
97
+ </g>
98
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
99
+ <path d='M 351.062 341.133C 351.062 335.65 346.616 331.204 341.133 331.204C 335.65 331.204 331.204 335.65 331.204 341.133C 331.204 346.616 335.65 351.062 341.133 351.062C 346.616 351.062 351.062 346.616 351.062 341.133Z' fill='#000000'/>
100
+ </g>
101
+ <g transform='translate(66.551 63.9998)scale(.996264)'>
102
+ <path d='M 351.062 341.133C 351.062 335.65 346.616 331.204 341.133 331.204C 335.65 331.204 331.204 335.65 331.204 341.133C 331.204 346.616 335.65 351.062 341.133 351.062C 346.616 351.062 351.062 346.616 351.062 341.133Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
103
+ </g>
104
+ </g>
105
+ </svg>
@@ -0,0 +1,105 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <!-- This file was generated by dvisvgm 2.13.1 -->
3
+ <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='350pt' height='350pt' viewBox='66.50143 63.950249 350 350'>
4
+ <g id='page1'>
5
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
6
+ <path d='M 10.1298 10.1298L 75.9891 75.9891' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/>
7
+ </g>
8
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
9
+ <path d='M 141.848 10.1298L 141.848 75.9891' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/>
10
+ </g>
11
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
12
+ <path d='M 207.708 207.708L 273.567 273.567' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/>
13
+ </g>
14
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
15
+ <path d='M 20.0087 10.1298C 20.0087 4.67387 15.5858 0.250938 10.1298 0.250937C 4.67387 0.250937 0.250937 4.67387 0.250937 10.1298C 0.250937 15.5858 4.67387 20.0087 10.1298 20.0087C 15.5858 20.0087 20.0087 15.5858 20.0087 10.1298Z' fill='#000000'/>
16
+ </g>
17
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
18
+ <path d='M 20.0087 10.1298C 20.0087 4.67387 15.5858 0.250938 10.1298 0.250937C 4.67387 0.250937 0.250937 4.67387 0.250937 10.1298C 0.250937 15.5858 4.67387 20.0087 10.1298 20.0087C 15.5858 20.0087 20.0087 15.5858 20.0087 10.1298Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
19
+ </g>
20
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
21
+ <path d='M 85.868 10.1298C 85.868 4.67387 81.445 0.250938 75.9891 0.250937C 70.5331 0.250937 66.1102 4.67387 66.1102 10.1298C 66.1102 15.5858 70.5331 20.0087 75.9891 20.0087C 81.445 20.0087 85.868 15.5858 85.868 10.1298Z' fill='#000000'/>
22
+ </g>
23
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
24
+ <path d='M 85.868 10.1298C 85.868 4.67387 81.445 0.250938 75.9891 0.250937C 70.5331 0.250937 66.1102 4.67387 66.1102 10.1298C 66.1102 15.5858 70.5331 20.0087 75.9891 20.0087C 81.445 20.0087 85.868 15.5858 85.868 10.1298Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
25
+ </g>
26
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
27
+ <path d='M 151.727 10.1298C 151.727 4.67387 147.304 0.250938 141.848 0.250937C 136.392 0.250937 131.969 4.67387 131.969 10.1298C 131.969 15.5858 136.392 20.0087 141.848 20.0087C 147.304 20.0087 151.727 15.5858 151.727 10.1298Z' fill='#000000'/>
28
+ </g>
29
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
30
+ <path d='M 151.727 10.1298C 151.727 4.67387 147.304 0.250938 141.848 0.250937C 136.392 0.250937 131.969 4.67387 131.969 10.1298C 131.969 15.5858 136.392 20.0087 141.848 20.0087C 147.304 20.0087 151.727 15.5858 151.727 10.1298Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
31
+ </g>
32
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
33
+ <circle cx='207.708' cy='10.1298' fill='#000000' r='2.50937'/>
34
+ </g>
35
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
36
+ <circle cx='273.567' cy='10.1298' fill='#000000' r='2.50937'/>
37
+ </g>
38
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
39
+ <circle cx='339.426' cy='10.1298' fill='#000000' r='2.50937'/>
40
+ </g>
41
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
42
+ <path d='M 85.868 75.9891C 85.868 70.5331 81.445 66.1102 75.9891 66.1102C 70.5331 66.1102 66.1102 70.5331 66.1102 75.9891C 66.1102 81.445 70.5331 85.868 75.9891 85.868C 81.445 85.868 85.868 81.445 85.868 75.9891Z' fill='#000000'/>
43
+ </g>
44
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
45
+ <path d='M 85.868 75.9891C 85.868 70.5331 81.445 66.1102 75.9891 66.1102C 70.5331 66.1102 66.1102 70.5331 66.1102 75.9891C 66.1102 81.445 70.5331 85.868 75.9891 85.868C 81.445 85.868 85.868 81.445 85.868 75.9891Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
46
+ </g>
47
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
48
+ <path d='M 151.727 75.9891C 151.727 70.5331 147.304 66.1102 141.848 66.1102C 136.392 66.1102 131.969 70.5331 131.969 75.9891C 131.969 81.445 136.392 85.868 141.848 85.868C 147.304 85.868 151.727 81.445 151.727 75.9891Z' fill='#000000'/>
49
+ </g>
50
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
51
+ <path d='M 151.727 75.9891C 151.727 70.5331 147.304 66.1102 141.848 66.1102C 136.392 66.1102 131.969 70.5331 131.969 75.9891C 131.969 81.445 136.392 85.868 141.848 85.868C 147.304 85.868 151.727 81.445 151.727 75.9891Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
52
+ </g>
53
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
54
+ <circle cx='207.708' cy='75.9891' fill='#000000' r='2.50937'/>
55
+ </g>
56
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
57
+ <circle cx='273.567' cy='75.9891' fill='#000000' r='2.50937'/>
58
+ </g>
59
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
60
+ <circle cx='339.426' cy='75.9891' fill='#000000' r='2.50937'/>
61
+ </g>
62
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
63
+ <path d='M 151.727 141.848C 151.727 136.392 147.304 131.969 141.848 131.969C 136.392 131.969 131.969 136.392 131.969 141.848C 131.969 147.304 136.392 151.727 141.848 151.727C 147.304 151.727 151.727 147.304 151.727 141.848Z' fill='#000000'/>
64
+ </g>
65
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
66
+ <path d='M 151.727 141.848C 151.727 136.392 147.304 131.969 141.848 131.969C 136.392 131.969 131.969 136.392 131.969 141.848C 131.969 147.304 136.392 151.727 141.848 151.727C 147.304 151.727 151.727 147.304 151.727 141.848Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
67
+ </g>
68
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
69
+ <circle cx='207.708' cy='141.848' fill='#000000' r='2.50937'/>
70
+ </g>
71
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
72
+ <circle cx='273.567' cy='141.848' fill='#000000' r='2.50937'/>
73
+ </g>
74
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
75
+ <circle cx='339.426' cy='141.848' fill='#000000' r='2.50937'/>
76
+ </g>
77
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
78
+ <path d='M 217.586 207.708C 217.586 202.252 213.164 197.829 207.708 197.829C 202.252 197.829 197.829 202.252 197.829 207.708C 197.829 213.164 202.252 217.586 207.708 217.586C 213.164 217.586 217.586 213.164 217.586 207.708Z' fill='#000000'/>
79
+ </g>
80
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
81
+ <path d='M 217.586 207.708C 217.586 202.252 213.164 197.829 207.708 197.829C 202.252 197.829 197.829 202.252 197.829 207.708C 197.829 213.164 202.252 217.586 207.708 217.586C 213.164 217.586 217.586 213.164 217.586 207.708Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
82
+ </g>
83
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
84
+ <circle cx='273.567' cy='207.708' fill='#000000' r='2.50937'/>
85
+ </g>
86
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
87
+ <circle cx='339.426' cy='207.708' fill='#000000' r='2.50937'/>
88
+ </g>
89
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
90
+ <path d='M 283.446 273.567C 283.446 268.111 279.023 263.688 273.567 263.688C 268.111 263.688 263.688 268.111 263.688 273.567C 263.688 279.023 268.111 283.446 273.567 283.446C 279.023 283.446 283.446 279.023 283.446 273.567Z' fill='#000000'/>
91
+ </g>
92
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
93
+ <path d='M 283.446 273.567C 283.446 268.111 279.023 263.688 273.567 263.688C 268.111 263.688 263.688 268.111 263.688 273.567C 263.688 279.023 268.111 283.446 273.567 283.446C 279.023 283.446 283.446 279.023 283.446 273.567Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='0.501875'/>
94
+ </g>
95
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
96
+ <circle cx='339.426' cy='273.567' fill='#000000' r='2.50937'/>
97
+ </g>
98
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
99
+ <path d='M 329.547 349.305L 349.305 329.547' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='4.015'/>
100
+ </g>
101
+ <g transform='translate(66.5014 63.9502)scale(.996264)'>
102
+ <path d='M 329.547 329.547L 349.305 349.305' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='4.015'/>
103
+ </g>
104
+ </g>
105
+ </svg>