Conditions | 10 |
Total Lines | 96 |
Code Lines | 51 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like amd.reconstruct.reconstruct() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | """Functions for resconstructing a periodic set up to isometry from its |
||
18 | def reconstruct(pdd: np.ndarray, cell: np.ndarray) -> np.ndarray: |
||
19 | """Reconstruct a motif from a PDD and unit cell. This function will |
||
20 | only work if ``pdd`` has enough columns, such that the last column |
||
21 | has all values larger than 2 times the diameter of the unit cell. It |
||
22 | also expects an uncollapsed PDD with no weights column. Do not use |
||
23 | ``amd.PDD`` to compute the PDD for this function, instead use |
||
24 | ``amd.PDD_reconstructable`` which returns a version of the PDD which |
||
25 | is passable to this function. This function is quite slow and run |
||
26 | time may vary a lot arbitrarily depending on input. |
||
27 | |||
28 | Parameters |
||
29 | ---------- |
||
30 | pdd : :class:`numpy.ndarray` |
||
31 | The PDD of the periodic set to reconstruct. Needs `k` at least |
||
32 | large enough so all values in the last column of pdd are greater |
||
33 | than :code:`2 * diameter(cell)`, and needs to be uncollapsed |
||
34 | without weights. Use amd.PDD_reconstructable to get a PDD which |
||
35 | is acceptable for this argument. |
||
36 | cell : :class:`numpy.ndarray` |
||
37 | Unit cell of the periodic set to reconstruct. |
||
38 | |||
39 | Returns |
||
40 | ------- |
||
41 | :class:`numpy.ndarray` |
||
42 | The reconstructed motif of the periodic set. |
||
43 | """ |
||
44 | |||
45 | # TODO: get a more reduced neighbour set |
||
46 | # TODO: find all shared distances in a big operation at the start |
||
47 | # TODO: move PREC variable to its proper place |
||
48 | PREC = 1e-10 |
||
49 | |||
50 | dims = cell.shape[0] |
||
51 | if dims not in (2, 3): |
||
52 | raise ValueError( |
||
53 | 'Reconstructing from PDD only implemented for 2 and 3 dimensions' |
||
54 | ) |
||
55 | diam = diameter(cell) |
||
56 | motif = [np.zeros((dims, ))] |
||
57 | if pdd.shape[0] == 1: |
||
58 | return np.array(motif) |
||
59 | |||
60 | # finding lattice distances so we can ignore them |
||
61 | cloud_generator = generate_concentric_cloud(np.array(motif), cell) |
||
62 | next(cloud_generator) # is just the origin |
||
63 | cloud = [] |
||
64 | layer = next(cloud_generator) |
||
65 | while np.any(np.linalg.norm(layer, axis=-1) <= diam): |
||
66 | cloud.append(layer) |
||
67 | layer = next(cloud_generator) |
||
68 | cloud = np.concatenate(cloud) |
||
69 | |||
70 | # is (a superset of) lattice points close enough to Voronoi domain |
||
71 | nn_set = _neighbour_set(cell, PREC) |
||
72 | lattice_dists = np.linalg.norm(cloud, axis=-1) |
||
73 | lattice_dists = lattice_dists[lattice_dists <= diam] |
||
74 | lattice_dists = _unique_within_tol(lattice_dists, PREC) |
||
75 | |||
76 | # remove lattice distances from first and second rows |
||
77 | row1_reduced = _remove_vals(pdd[0], lattice_dists, PREC) |
||
78 | row2_reduced = _remove_vals(pdd[1], lattice_dists, PREC) |
||
79 | # get shared dists between first and second rows |
||
80 | shared_dists = _shared_vals(row1_reduced, row2_reduced, PREC) |
||
81 | shared_dists = _unique_within_tol(shared_dists, PREC) |
||
82 | |||
83 | # all combinations of vecs in neighbour set forming a basis |
||
84 | bases = [] |
||
85 | for vecs in combinations(nn_set, dims): |
||
86 | vecs = np.asarray(vecs) |
||
87 | if np.abs(np.linalg.det(vecs)) > PREC: |
||
88 | bases.extend(basis for basis in permutations(vecs, dims)) |
||
89 | |||
90 | q = _find_second_point(shared_dists, bases, cloud, PREC) |
||
91 | if q is None: |
||
92 | raise RuntimeError('Second point of motif could not be found.') |
||
93 | motif.append(q) |
||
94 | |||
95 | if pdd.shape[0] == 2: |
||
96 | return np.array(motif) |
||
97 | |||
98 | for row in pdd[2:, :]: |
||
99 | row_reduced = _remove_vals(row, lattice_dists, PREC) |
||
100 | shared_dists1 = _shared_vals(row1_reduced, row_reduced, PREC) |
||
101 | shared_dists2 = _shared_vals(row2_reduced, row_reduced, PREC) |
||
102 | shared_dists1 = _unique_within_tol(shared_dists1, PREC) |
||
103 | shared_dists2 = _unique_within_tol(shared_dists2, PREC) |
||
104 | q_ = _find_further_point( |
||
105 | shared_dists1, shared_dists2, bases, cloud, q, PREC |
||
106 | ) |
||
107 | if q_ is None: |
||
108 | raise RuntimeError('Further point of motif could not be found.') |
||
109 | motif.append(q_) |
||
110 | |||
111 | motif = np.array(motif) |
||
112 | motif = np.mod(motif @ np.linalg.inv(cell), 1) @ cell |
||
113 | return motif |
||
114 | |||
297 |