| 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 |