Conditions | 10 |
Total Lines | 94 |
Code Lines | 50 |
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 |
||
20 | def reconstruct(pdd: FloatArray, cell: FloatArray) -> FloatArray: |
||
21 | """Reconstruct a motif from a PDD and unit cell. This function will |
||
22 | only work if ``pdd`` has enough columns, such that the last column |
||
23 | has all values larger than 2 times the diameter of the unit cell. It |
||
24 | also expects an uncollapsed PDD with no weights column. Do not use |
||
25 | ``amd.PDD`` to compute the PDD for this function, instead use |
||
26 | ``amd.PDD_reconstructable`` which returns a version of the PDD which |
||
27 | is passable to this function. This function is quite slow and run |
||
28 | time may vary a lot arbitrarily depending on input. |
||
29 | |||
30 | Parameters |
||
31 | ---------- |
||
32 | pdd : :class:`numpy.ndarray` |
||
33 | The PDD of the periodic set to reconstruct. Needs `k` at least |
||
34 | large enough so all values in the last column of pdd are greater |
||
35 | than :code:`2 * diameter(cell)`, and needs to be uncollapsed |
||
36 | without weights. Use amd.PDD_reconstructable to get a PDD which |
||
37 | is acceptable for this argument. |
||
38 | cell : :class:`numpy.ndarray` |
||
39 | Unit cell of the periodic set to reconstruct. |
||
40 | |||
41 | Returns |
||
42 | ------- |
||
43 | :class:`numpy.ndarray` |
||
44 | The reconstructed motif of the periodic set. |
||
45 | """ |
||
46 | |||
47 | # TODO: get a more reduced neighbor set |
||
48 | # TODO: find all shared distances in a big operation at the start |
||
49 | # TODO: move PREC variable to its proper place |
||
50 | PREC = 1e-10 |
||
51 | |||
52 | dims = cell.shape[0] |
||
53 | if dims not in (2, 3): |
||
54 | raise ValueError( |
||
55 | "Reconstructing from PDD only implemented for 2 and 3 dimensions" |
||
56 | ) |
||
57 | diam = diameter(cell) |
||
58 | motif = [np.zeros((dims,))] |
||
59 | if pdd.shape[0] == 1: |
||
60 | return np.array(motif) |
||
61 | |||
62 | # finding lattice distances so we can ignore them |
||
63 | cloud_generator = iter(generate_concentric_cloud(np.array(motif), cell)) |
||
64 | next(cloud_generator) # the origin |
||
65 | cloud = [] |
||
66 | layer = next(cloud_generator) |
||
67 | while np.any(np.linalg.norm(layer, axis=-1) <= diam): |
||
68 | cloud.append(layer) |
||
69 | layer = next(cloud_generator) |
||
70 | cloud = np.concatenate(cloud) |
||
71 | |||
72 | # is (a superset of) lattice points close enough to Voronoi domain |
||
73 | nn_set = _neighbor_set(cell, PREC) |
||
74 | lattice_dists = np.linalg.norm(cloud, axis=-1) |
||
75 | lattice_dists = lattice_dists[lattice_dists <= diam] |
||
76 | lattice_dists = _unique_within_tol(lattice_dists, PREC) |
||
77 | |||
78 | # remove lattice distances from first and second rows |
||
79 | row1_reduced = _remove_vals(pdd[0], lattice_dists, PREC) |
||
80 | row2_reduced = _remove_vals(pdd[1], lattice_dists, PREC) |
||
81 | # get shared dists between first and second rows |
||
82 | shared_dists = _shared_vals(row1_reduced, row2_reduced, PREC) |
||
83 | shared_dists = _unique_within_tol(shared_dists, PREC) |
||
84 | |||
85 | # all combinations of vecs in neighbor set forming a basis |
||
86 | bases = [] |
||
87 | for vecs in combinations(nn_set, dims): |
||
88 | vecs = np.asarray(vecs) |
||
89 | if np.abs(np.linalg.det(vecs)) > PREC: |
||
90 | bases.extend(basis for basis in permutations(vecs, dims)) |
||
91 | |||
92 | q = _find_second_point(shared_dists, bases, cloud, PREC) |
||
93 | if q is None: |
||
94 | raise RuntimeError("Second point of motif could not be found.") |
||
95 | motif.append(q) |
||
96 | |||
97 | if pdd.shape[0] == 2: |
||
98 | return np.array(motif) |
||
99 | |||
100 | for row in pdd[2:, :]: |
||
101 | row_reduced = _remove_vals(row, lattice_dists, PREC) |
||
102 | shared_dists1 = _shared_vals(row1_reduced, row_reduced, PREC) |
||
103 | shared_dists2 = _shared_vals(row2_reduced, row_reduced, PREC) |
||
104 | shared_dists1 = _unique_within_tol(shared_dists1, PREC) |
||
105 | shared_dists2 = _unique_within_tol(shared_dists2, PREC) |
||
106 | q_ = _find_further_point(shared_dists1, shared_dists2, bases, cloud, q, PREC) |
||
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 | |||
295 |