text: # Copyright (C) 2013 Atsushi Togo
  # All rights reserved.
  #
  # This file is part of phonopy.
  #
  # Redistribution and use in source and binary forms, with or without
  # modification, are permitted provided that the following conditions
  # are met:
  #
  # * Redistributions of source code must retain the above copyright
  #   notice, this list of conditions and the following disclaimer.
  #
  # * Redistributions in binary form must reproduce the above copyright
  #   notice, this list of conditions and the following disclaimer in
  #   the documentation and/or other materials provided with the
  #   distribution.
  #
  # * Neither the name of the phonopy project nor the names of its
  #   contributors may be used to endorse or promote products derived
  #   from this software without specific prior written permission.
  #
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  # POSSIBILITY OF SUCH DAMAGE.
  
  import numpy as np
  from phonopy.structure.cells import get_reduced_bases
  
  search_space = np.array([
          [0, 0, 0],
          [0, 0, 1],
          [0, 1, -1],
          [0, 1, 0],
          [0, 1, 1],
          [1, -1, -1],
          [1, -1, 0],
          [1, -1, 1],
          [1, 0, -1],
          [1, 0, 0],
          [1, 0, 1],
          [1, 1, -1],
          [1, 1, 0],
          [1, 1, 1],
          [-1, -1, -1],
          [-1, -1, 0],
          [-1, -1, 1],
          [-1, 0, -1],
          [-1, 0, 0],
          [-1, 0, 1],
          [-1, 1, -1],
          [-1, 1, 0],
          [-1, 1, 1],
          [0, -1, -1],
          [0, -1, 0],
          [0, -1, 1],
          [0, 0, -1]], dtype='intc')
  
  
  def get_qpoints_in_Brillouin_zone(reciprocal_lattice,
                                    qpoints,
                                    only_unique=False,
                                    tolerance=0.01):
      bz = BrillouinZone(reciprocal_lattice)
      bz.run(qpoints)
      if only_unique:
          return np.array([pts[0] for pts in bz.shortest_qpoints],
                          dtype='double', order='C')
      else:
          return bz.shortest_qpoints
  
  
  class BrillouinZone(object):
      """Move qpoints to first Brillouin zone by lattice translation.
  
      Attributes
      ----------
      shortest_qpoints : list
          Each element of the list contains a set of q-points that are in first
          Brillouin zone (BZ). When inside BZ, there is only one q-point for
          each element, but on the surface, multiple q-points that are
          distinguished by non-zero lattice translation are stored.
  
      """
  
      def __init__(self, reciprocal_lattice, tolerance=0.01):
          """
  
          Parameters
          ----------
          reciprocal_lattice : array_like
              Primitive cell basis vectors given in column vectors.
              shape=(3,3)
              dtype=float
          tolerance : float, optional
              Default = 0.01
  
          """
  
          self._reciprocal_lattice = np.array(reciprocal_lattice)
          self._tolerance = min(
              np.sum(reciprocal_lattice ** 2, axis=0)) * tolerance
          self._reduced_bases = get_reduced_bases(reciprocal_lattice.T)
          self._tmat = np.dot(np.linalg.inv(self._reciprocal_lattice),
                              self._reduced_bases.T)
          self._tmat_inv = np.linalg.inv(self._tmat)
          self.shortest_qpoints = None
  
      def run(self, qpoints):
          reduced_qpoints = np.dot(qpoints, self._tmat_inv.T)
          self.shortest_qpoints = []
          for q in reduced_qpoints:
              distances = (np.dot(q + search_space,
                                  self._reduced_bases) ** 2).sum(axis=1)
              min_dist = min(distances)
              shortest_indices = np.where(
                  distances < min_dist + self._tolerance)[0]
              self.shortest_qpoints.append(
                  np.dot(search_space[shortest_indices] + q, self._tmat.T))
  