def check_and_plot(self, A_nn, A0_nn, digits, keywords=''): # Construct fingerprint of input matrices for comparison fingerprint = np.array( [md5_array(A_nn, numeric=True), md5_array(A0_nn, numeric=True)]) # Compare fingerprints across all processors fingerprints = np.empty((world.size, 2), np.int64) world.all_gather(fingerprint, fingerprints) if fingerprints.ptp(0).any(): raise RuntimeError('Distributed matrices are not identical!') # If assertion fails, catch temporarily while plotting, then re-raise try: self.assertAlmostEqual(np.abs(A_nn - A0_nn).max(), 0, digits) except AssertionError: if world.rank == 0 and mpl is not None: from matplotlib.figure import Figure fig = Figure() ax = fig.add_axes([0.0, 0.1, 1.0, 0.83]) ax.set_title(self.__class__.__name__) im = ax.imshow(np.abs(A_nn - A0_nn), interpolation='nearest') fig.colorbar(im) fig.text(0.5, 0.05, 'Keywords: ' + keywords, \ horizontalalignment='center', verticalalignment='top') from matplotlib.backends.backend_agg import FigureCanvasAgg img = 'ut_hsops_%s_%s.png' % (self.__class__.__name__, \ '_'.join(keywords.split(','))) FigureCanvasAgg(fig).print_figure(img.lower(), dpi=90) raise
def check_and_plot(self, A_nn, A0_nn, digits, keywords=''): # Construct fingerprint of input matrices for comparison fingerprint = np.array([md5_array(A_nn, numeric=True), md5_array(A0_nn, numeric=True)]) # Compare fingerprints across all processors fingerprints = np.empty((world.size, 2), np.int64) world.all_gather(fingerprint, fingerprints) if fingerprints.ptp(0).any(): raise RuntimeError('Distributed matrices are not identical!') # If assertion fails, catch temporarily while plotting, then re-raise try: self.assertAlmostEqual(np.abs(A_nn-A0_nn).max(), 0, digits) except AssertionError: if world.rank == 0 and mpl is not None: from matplotlib.figure import Figure fig = Figure() ax = fig.add_axes([0.0, 0.1, 1.0, 0.83]) ax.set_title(self.__class__.__name__) im = ax.imshow(np.abs(A_nn-A0_nn), interpolation='nearest') fig.colorbar(im) fig.text(0.5, 0.05, 'Keywords: ' + keywords, \ horizontalalignment='center', verticalalignment='top') from matplotlib.backends.backend_agg import FigureCanvasAgg img = 'ut_hsops_%s_%s.png' % (self.__class__.__name__, \ '_'.join(keywords.split(','))) FigureCanvasAgg(fig).print_figure(img.lower(), dpi=90) raise
def test_overlap_inverse_before(self): kpt = self.wfs.kpt_u[0] kpt.P_ani = self.pt.dict(self.bd.mynbands) ppo = ProjectorPairOverlap(self.wfs, self.atoms) # Compare fingerprints across all processors fingerprint = np.array([md5_array(ppo.B_aa, numeric=True)]) fingerprints = np.empty(world.size, np.int64) world.all_gather(fingerprint, fingerprints) if fingerprints.ptp(0).any(): raise RuntimeError('Distributed matrices are not identical!') work_nG = np.empty_like(self.psit_nG) self.pt.integrate(self.psit_nG, kpt.P_ani, kpt.q) P0_ani = dict([(a,P_ni.copy()) for a,P_ni in kpt.P_ani.items()]) ppo.apply_inverse(self.psit_nG, work_nG, self.wfs, kpt, calculate_P_ani=False) res_nG = np.empty_like(self.psit_nG) ppo.apply(work_nG, res_nG, self.wfs, kpt, calculate_P_ani=True) del work_nG P_ani = self.pt.dict(self.bd.mynbands) self.pt.integrate(res_nG, P_ani, kpt.q) abserr = np.empty(1, dtype=float) for n in range(self.nbands): band_rank, myn = self.bd.who_has(n) if band_rank == self.bd.comm.rank: abserr[:] = np.abs(self.psit_nG[myn] - res_nG[myn]).max() self.gd.comm.max(abserr) self.bd.comm.broadcast(abserr, band_rank) self.assertAlmostEqual(abserr.item(), 0, 10) self.check_and_plot(P_ani, P0_ani, 10, 'overlap,inverse,before')
def diagonalize(self, H_sS): if self.coupling: # Non-Hermitian matrix can only use linalg.eig self.printtxt('Use numpy.linalg.eig') H_SS = np.zeros((self.nS, self.nS), dtype=complex) if self.nS % world.size == 0: world.all_gather(H_sS, H_SS) else: H_SS = gatherv(H_sS) self.w_S, self.v_SS = np.linalg.eig(H_SS) self.par_save('v_SS', 'v_SS', self.v_SS[self.nS_start:self.nS_end, :].copy()) else: if world.size == 1: self.printtxt('Use lapack.') from gpaw.utilities.lapack import diagonalize self.w_S = np.zeros(self.nS) H_SS = H_sS diagonalize(H_SS, self.w_S) self.v_SS = H_SS.conj() # eigenvectors in the rows, transposed later else: self.printtxt('Use scalapack') self.w_S, self.v_sS = self.scalapack_diagonalize(H_sS) self.v_SS = self.v_sS # just use the same name self.par_save('v_SS', 'v_SS', self.v_SS) return
def diagonalize(self, H_sS): if self.coupling: # Non-Hermitian matrix can only use linalg.eig self.printtxt('Use numpy.linalg.eig') H_SS = np.zeros((self.nS, self.nS), dtype=complex) if self.nS % world.size == 0: world.all_gather(H_sS, H_SS) else: H_SS = gatherv(H_sS) self.w_S, self.v_SS = np.linalg.eig(H_SS) self.par_save('v_SS', 'v_SS', self.v_SS[self.nS_start:self.nS_end, :].copy()) else: if world.size == 1: self.printtxt('Use lapack.') from gpaw.utilities.lapack import diagonalize self.w_S = np.zeros(self.nS) H_SS = H_sS diagonalize(H_SS, self.w_S) self.v_SS = H_SS.conj( ) # eigenvectors in the rows, transposed later else: self.printtxt('Use scalapack') self.w_S, self.v_sS = self.scalapack_diagonalize(H_sS) self.v_SS = self.v_sS # just use the same name self.par_save('v_SS', 'v_SS', self.v_SS) return
def setUp(self): UTDomainParallelSetup.setUp(self) for virtvar in ['dtype']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Create randomized atoms self.atoms = create_random_atoms(self.gd, 5) # also tested: 10xNH3/BDA # XXX DEBUG START if False: from ase import view view(self.atoms*(1+2*self.gd.pbc_c)) # XXX DEBUG END # Do we agree on the atomic positions? pos_ac = self.atoms.get_positions() pos_rac = np.empty((world.size,)+pos_ac.shape, pos_ac.dtype) world.all_gather(pos_ac, pos_rac) if (pos_rac-pos_rac[world.rank,...][np.newaxis,...]).any(): raise RuntimeError('Discrepancy in atomic positions detected.') # Create setups for atoms self.Z_a = self.atoms.get_atomic_numbers() self.setups = Setups(self.Z_a, p.setups, p.basis, p.lmax, xc) # K-point descriptor bzk_kc = np.array([[0, 0, 0]], dtype=float) self.kd = KPointDescriptor(bzk_kc, 1) self.kd.set_symmetry(self.atoms, self.setups, usesymm=True) self.kd.set_communicator(self.kpt_comm) # Create gamma-point dummy wavefunctions self.wfs = FDWFS(self.gd, self.bd, self.kd, self.setups, self.dtype) spos_ac = self.atoms.get_scaled_positions() % 1.0 self.wfs.set_positions(spos_ac) self.pt = self.wfs.pt # XXX shortcut ## Also create pseudo partial waveves #from gpaw.lfc import LFC #self.phit = LFC(self.gd, [setup.phit_j for setup in self.setups], \ # self.kpt_comm, dtype=self.dtype) #self.phit.set_positions(spos_ac) self.r_cG = None self.buf_G = None self.psit_nG = None self.allocate()
def setUp(self): UTDomainParallelSetup.setUp(self) for virtvar in ['dtype']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Create randomized atoms self.atoms = create_random_atoms(self.gd, 5) # also tested: 10xNH3/BDA # XXX DEBUG START if False: from ase import view view(self.atoms*(1+2*self.gd.pbc_c)) # XXX DEBUG END # Do we agree on the atomic positions? pos_ac = self.atoms.get_positions() pos_rac = np.empty((world.size,)+pos_ac.shape, pos_ac.dtype) world.all_gather(pos_ac, pos_rac) if (pos_rac-pos_rac[world.rank,...][np.newaxis,...]).any(): raise RuntimeError('Discrepancy in atomic positions detected.') # Create setups for atoms self.Z_a = self.atoms.get_atomic_numbers() self.setups = Setups(self.Z_a, p.setups, p.basis, p.lmax, xc) # K-point descriptor bzk_kc = np.array([[0, 0, 0]], dtype=float) self.kd = KPointDescriptor(bzk_kc, 1) self.kd.set_symmetry(self.atoms, self.setups) self.kd.set_communicator(self.kpt_comm) # Create gamma-point dummy wavefunctions self.wfs = FDWFS(self.gd, self.bd, self.kd, self.setups, self.block_comm, self.dtype) spos_ac = self.atoms.get_scaled_positions() % 1.0 self.wfs.set_positions(spos_ac) self.pt = self.wfs.pt # XXX shortcut ## Also create pseudo partial waveves #from gpaw.lfc import LFC #self.phit = LFC(self.gd, [setup.phit_j for setup in self.setups], \ # self.kpt_comm, dtype=self.dtype) #self.phit.set_positions(spos_ac) self.r_cG = None self.buf_G = None self.psit_nG = None self.allocate()
def setUp(self): UTBandParallelSetup.setUp(self) for virtvar in ['dtype', 'blocking', 'async']: assert getattr(self, virtvar) is not None, 'Virtual "%s"!' % virtvar # Create randomized atoms self.atoms = create_random_atoms(self.gd) # Do we agree on the atomic positions? pos_ac = self.atoms.get_positions() pos_rac = np.empty((world.size, ) + pos_ac.shape, pos_ac.dtype) world.all_gather(pos_ac, pos_rac) if (pos_rac - pos_rac[world.rank, ...][np.newaxis, ...]).any(): raise RuntimeError('Discrepancy in atomic positions detected.') # Create setups for atoms self.Z_a = self.atoms.get_atomic_numbers() self.setups = Setups(self.Z_a, p.setups, p.basis, p.lmax, xc) # Create atomic projector overlaps spos_ac = self.atoms.get_scaled_positions() % 1.0 self.rank_a = self.gd.get_ranks_from_positions(spos_ac) self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups], self.kpt_comm, dtype=self.dtype) self.pt.set_positions(spos_ac) if memstats: # Hack to scramble heap usage into steady-state level HEAPSIZE = 25 * 1024**2 for i in range(100): data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float) del data self.mem_pre = record_memory() self.mem_alloc = None self.mem_test = None # Stuff for pseudo wave functions and projections if self.dtype == complex: self.gamma = 1j**(1.0 / self.nbands) else: self.gamma = 1.0 self.psit_nG = None self.P_ani = None self.Qeff_a = None self.Qtotal = None self.allocate()
def test_projection_linearity(self): kpt = self.wfs.kpt_u[0] Q_ani = self.pt.dict(self.bd.mynbands) self.pt.integrate(self.psit_nG, Q_ani, q=kpt.q) for Q_ni in Q_ani.values(): self.assertTrue(Q_ni.dtype == self.dtype) P0_ani = dict([(a, Q_ni.copy()) for a, Q_ni in Q_ani.items()]) self.pt.add(self.psit_nG, Q_ani, q=kpt.q) self.pt.integrate(self.psit_nG, P0_ani, q=kpt.q) #rank_a = self.gd.get_ranks_from_positions(spos_ac) #my_atom_indices = np.argwhere(self.gd.comm.rank == rank_a).ravel() # ~ a ~ a' #TODO XXX should fix PairOverlap-ish stuff for < p | phi > overlaps # i i' #spos_ac = self.pt.spos_ac # NewLFC doesn't have this spos_ac = self.atoms.get_scaled_positions() % 1.0 gpo = GridPairOverlap(self.gd, self.setups) B_aa = gpo.calculate_overlaps(spos_ac, self.pt) # Compare fingerprints across all processors fingerprint = np.array([md5_array(B_aa, numeric=True)]) fingerprints = np.empty(world.size, np.int64) world.all_gather(fingerprint, fingerprints) if fingerprints.ptp(0).any(): raise RuntimeError('Distributed matrices are not identical!') P_ani = dict([(a, Q_ni.copy()) for a, Q_ni in Q_ani.items()]) for a1 in range(len(self.atoms)): if a1 in P_ani.keys(): P_ni = P_ani[a1] else: # Atom a1 is not in domain so allocate a temporary buffer P_ni = np.zeros(( self.bd.mynbands, self.setups[a1].ni, ), dtype=self.dtype) for a2, Q_ni in Q_ani.items(): # B_aa are the projector overlaps across atomic pairs B_ii = gpo.extract_atomic_pair_matrix(B_aa, a1, a2) P_ni += np.dot(Q_ni, B_ii.T) #sum over a2 and last i in B_ii self.gd.comm.sum(P_ni) self.check_and_plot(P_ani, P0_ani, 8, 'projection,linearity')
def setUp(self): UTBandParallelSetup.setUp(self) for virtvar in ['dtype','blocking','async']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Create randomized atoms self.atoms = create_random_atoms(self.gd) # Do we agree on the atomic positions? pos_ac = self.atoms.get_positions() pos_rac = np.empty((world.size,)+pos_ac.shape, pos_ac.dtype) world.all_gather(pos_ac, pos_rac) if (pos_rac-pos_rac[world.rank,...][np.newaxis,...]).any(): raise RuntimeError('Discrepancy in atomic positions detected.') # Create setups for atoms self.Z_a = self.atoms.get_atomic_numbers() self.setups = Setups(self.Z_a, p.setups, p.basis, p.lmax, xc) # Create atomic projector overlaps spos_ac = self.atoms.get_scaled_positions() % 1.0 self.rank_a = self.gd.get_ranks_from_positions(spos_ac) self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups], dtype=self.dtype) self.pt.set_positions(spos_ac) if memstats: # Hack to scramble heap usage into steady-state level HEAPSIZE = 25 * 1024**2 for i in range(100): data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float) del data self.mem_pre = record_memory() self.mem_alloc = None self.mem_test = None # Stuff for pseudo wave functions and projections if self.dtype == complex: self.gamma = 1j**(1.0/self.nbands) else: self.gamma = 1.0 self.psit_nG = None self.P_ani = None self.Qeff_a = None self.Qtotal = None self.allocate()
def gatherv(m, N=None): from gpaw.mpi import world, size, rank if world.size == 1: return m ndim = m.ndim if ndim == 2: n, N = m.shape assert n < N M = np.zeros((N, N), dtype=complex) elif ndim == 1: n = m.shape[0] M = np.zeros(N, dtype=complex) else: print 'Not Implemented' XX n_index = np.zeros(size, dtype=int) world.all_gather(np.array([n]), n_index) root = 0 if rank != root: world.ssend(m, root, 112+rank) else: for irank, n in enumerate(n_index): if irank == root: if ndim == 2: M[:n_index[0] :] = m else: M[:n_index[0]] = m else: n_start = n_index[0:irank].sum() n_end = n_index[0:irank+1].sum() if ndim == 2: tmp_nN = np.zeros((n, N), dtype=complex) world.receive(tmp_nN, irank, 112+irank) M[n_start:n_end, :] = tmp_nN else: tmp_n = np.zeros(n, dtype=complex) world.receive(tmp_n, irank, 112+irank) M[n_start:n_end] = tmp_n world.broadcast(M, root) return M
def test_projection_linearity(self): kpt = self.wfs.kpt_u[0] Q_ani = self.pt.dict(self.bd.mynbands) self.pt.integrate(self.psit_nG, Q_ani, q=kpt.q) for Q_ni in Q_ani.values(): self.assertTrue(Q_ni.dtype == self.dtype) P0_ani = dict([(a,Q_ni.copy()) for a,Q_ni in Q_ani.items()]) self.pt.add(self.psit_nG, Q_ani, q=kpt.q) self.pt.integrate(self.psit_nG, P0_ani, q=kpt.q) #rank_a = self.gd.get_ranks_from_positions(spos_ac) #my_atom_indices = np.argwhere(self.gd.comm.rank == rank_a).ravel() # ~ a ~ a' #TODO XXX should fix PairOverlap-ish stuff for < p | phi > overlaps # i i' #spos_ac = self.pt.spos_ac # NewLFC doesn't have this spos_ac = self.atoms.get_scaled_positions() % 1.0 gpo = GridPairOverlap(self.gd, self.setups) B_aa = gpo.calculate_overlaps(spos_ac, self.pt) # Compare fingerprints across all processors fingerprint = np.array([md5_array(B_aa, numeric=True)]) fingerprints = np.empty(world.size, np.int64) world.all_gather(fingerprint, fingerprints) if fingerprints.ptp(0).any(): raise RuntimeError('Distributed matrices are not identical!') P_ani = dict([(a,Q_ni.copy()) for a,Q_ni in Q_ani.items()]) for a1 in range(len(self.atoms)): if a1 in P_ani.keys(): P_ni = P_ani[a1] else: # Atom a1 is not in domain so allocate a temporary buffer P_ni = np.zeros((self.bd.mynbands,self.setups[a1].ni,), dtype=self.dtype) for a2, Q_ni in Q_ani.items(): # B_aa are the projector overlaps across atomic pairs B_ii = gpo.extract_atomic_pair_matrix(B_aa, a1, a2) P_ni += np.dot(Q_ni, B_ii.T) #sum over a2 and last i in B_ii self.gd.comm.sum(P_ni) self.check_and_plot(P_ani, P0_ani, 8, 'projection,linearity')
def gatherv(m, N=None): if world.size == 1: return m ndim = m.ndim if ndim == 2: n, N = m.shape assert n < N M = np.zeros((N, N), dtype=complex) elif ndim == 1: n = m.shape[0] M = np.zeros(N, dtype=complex) else: print('Not Implemented') XX n_index = np.zeros(size, dtype=int) world.all_gather(np.array([n]), n_index) root = 0 if rank != root: world.ssend(m, root, 112 + rank) else: for irank, n in enumerate(n_index): if irank == root: if ndim == 2: M[:n_index[0]:] = m else: M[:n_index[0]] = m else: n_start = n_index[0:irank].sum() n_end = n_index[0:irank + 1].sum() if ndim == 2: tmp_nN = np.zeros((n, N), dtype=complex) world.receive(tmp_nN, irank, 112 + irank) M[n_start:n_end, :] = tmp_nN else: tmp_n = np.zeros(n, dtype=complex) world.receive(tmp_n, irank, 112 + irank) M[n_start:n_end] = tmp_n world.broadcast(M, root) return M
def test_extrapolate_inverse(self): kpt = self.wfs.kpt_u[0] ppo = ProjectorPairOverlap(self.wfs, self.atoms) # Compare fingerprints across all processors fingerprint = np.array([md5_array(ppo.B_aa, numeric=True)]) fingerprints = np.empty(world.size, np.int64) world.all_gather(fingerprint, fingerprints) if fingerprints.ptp(0).any(): raise RuntimeError('Distributed matrices are not identical!') work_nG = np.empty_like(self.psit_nG) P_ani = ppo.apply_inverse(self.psit_nG, work_nG, self.wfs, kpt, \ calculate_P_ani=True, extrapolate_P_ani=True) P0_ani = self.pt.dict(self.bd.mynbands) self.pt.integrate(work_nG, P0_ani, kpt.q) del work_nG self.check_and_plot(P_ani, P0_ani, 11, 'extrapolate,inverse')
def test_contents_projection(self): # Distribute inverse effective charges to everybody in domain all_Qeff_a = np.empty(len(self.atoms), dtype=float) for a,rank in enumerate(self.rank_a): if rank == self.gd.comm.rank: Qeff = np.array([self.Qeff_a[a]]) else: Qeff = np.empty(1, dtype=float) self.gd.comm.broadcast(Qeff, rank) all_Qeff_a[a] = Qeff # Check absolute values consistency of inverse effective charges self.assertAlmostEqual(np.abs(1./self.Z_a-np.abs(all_Qeff_a)).max(), 0, 9) # Check sum of inverse effective charges against total self.assertAlmostEqual(all_Qeff_a.sum(), self.Qtotal, 9) # Make sure that we all agree on inverse effective charges fingerprint = np.array([md5_array(all_Qeff_a, numeric=True)]) all_fingerprints = np.empty(world.size, fingerprint.dtype) world.all_gather(fingerprint, all_fingerprints) if all_fingerprints.ptp(0).any(): raise RuntimeError('Distributed eff. charges are not identical!')
def test_contents_projection(self): # Distribute inverse effective charges to everybody in domain all_Qeff_a = np.empty(len(self.atoms), dtype=float) for a, rank in enumerate(self.rank_a): if rank == self.gd.comm.rank: Qeff = np.array([self.Qeff_a[a]]) else: Qeff = np.empty(1, dtype=float) self.gd.comm.broadcast(Qeff, rank) all_Qeff_a[a] = Qeff # Check absolute values consistency of inverse effective charges self.assertAlmostEqual( np.abs(1. / self.Z_a - np.abs(all_Qeff_a)).max(), 0, 9) # Check sum of inverse effective charges against total self.assertAlmostEqual(all_Qeff_a.sum(), self.Qtotal, 9) # Make sure that we all agree on inverse effective charges fingerprint = np.array([md5_array(all_Qeff_a, numeric=True)]) all_fingerprints = np.empty(world.size, fingerprint.dtype) world.all_gather(fingerprint, all_fingerprints) if all_fingerprints.ptp(0).any(): raise RuntimeError('Distributed eff. charges are not identical!')
mixer = Mixer(beta=0.10, nmaxold=5, weight=100.0), communicator=comm) return calc rank = world.rank # process_type can be one of three values: # 0: server # 1: client # 2: potential # Each independent program identifies itself with one of these three. process_type = numpy.array((2,), dtype='i') process_types = numpy.empty(world.size, dtype='i') world.all_gather(process_type, process_types) servers = 0 clients = 0 potentials = 0 client_ranks = [] potential_ranks = [] for i,t in enumerate(process_types): if t == 0: servers += 1 elif t == 1: clients += 1 client_ranks.append(i) elif t == 2: potentials += 1 potential_ranks.append(i)
def create_memory_info(mem1, mem2, vmkey='VmData'): dm = np.array([(mem2 - mem1)[vmkey]], dtype=float) dm_r = np.empty(world.size, dtype=float) world.all_gather(dm, dm_r) return dm_r, ','.join(map(lambda v: '%8.4f MB' % v, dm_r / 1024**2.))
calc = EMT() return calc rank = world.rank # process_type can be one of three values: # 0: server # 1: client # 2: potential # Each independent program identifies itself with one of these three. process_type = numpy.array((2, ), dtype='i') process_types = numpy.empty(world.size, dtype='i') world.all_gather(process_type, process_types) servers = 0 clients = 0 potentials = 0 for i, t in enumerate(process_types): if t == 0: server_rank = i servers += 1 elif t == 1: clients += 1 elif t == 2: potentials += 1 potential_group_size = potentials / clients potential_ranks = numpy.empty(potentials, dtype='i')
def create_memory_info(mem1, mem2, vmkey='VmData'): dm = np.array([(mem2-mem1)[vmkey]], dtype=float) dm_r = np.empty(world.size, dtype=float) world.all_gather(dm, dm_r) return dm_r, ','.join(map(lambda v: '%8.4f MB' % v, dm_r/1024**2.))
def expand_ibz(lU_scc, cU_scc, ibzk_kc, pbc_c=np.ones(3, bool)): """Expand IBZ from lattice group to crystal group. Parameters ---------- lU_scc : ndarray Lattice symmetry operators. cU_scc : ndarray Crystal symmetry operators. ibzk_kc : ndarray Vertices of lattice IBZ. Returns ------- ibzk_kc : ndarray Vertices of crystal IBZ. """ # Find right cosets. The lattice group is partioned into right cosets of # the crystal group. This can in practice be done by testing whether # U1 U2^{-1} is in the crystal group as done below. cosets = [] Utmp_scc = lU_scc.copy() while len(Utmp_scc): U1_cc = Utmp_scc[0].copy() Utmp_scc = np.delete(Utmp_scc, 0, axis=0) j = 0 new_coset = [U1_cc] while j < len(Utmp_scc): U2_cc = Utmp_scc[j] U3_cc = np.dot(U1_cc, np.linalg.inv(U2_cc)) if (U3_cc == cU_scc).all(2).all(1).any(): new_coset.append(U2_cc) Utmp_scc = np.delete(Utmp_scc, j, axis=0) j -= 1 j += 1 cosets.append(new_coset) volume = np.inf nibzk_kc = ibzk_kc U0_cc = cosets[0][0] # Origin if np.any(~pbc_c): nonpbcind = np.argwhere(~pbc_c) # Once the coests are known the irreducible zone is given by picking one # operation from each coset. To make sure that the IBZ produced is simply # connected we compute the volume of the convex hull of the produced IBZ # and pick (one of) the ones that have the smallest volume. This is done by # brute force and can sometimes take a while, however, in most cases this # is not a problem. combs = list(product(*cosets[1:]))[world.rank::world.size] for U_scc in combs: if not len(U_scc): continue U_scc = np.concatenate([np.array(U_scc), [U0_cc]]) tmpk_kc = unfold_points(ibzk_kc, U_scc) volumenew = convex_hull_volume(tmpk_kc) if np.any(~pbc_c): # Compute the area instead volumenew /= (tmpk_kc[:, nonpbcind].max() - tmpk_kc[:, nonpbcind].min()) if volumenew < volume: nibzk_kc = tmpk_kc volume = volumenew ibzk_kc = unique_rows(nibzk_kc) volume = np.array((volume, )) volumes = np.zeros(world.size, float) world.all_gather(volume, volumes) minrank = np.argmin(volumes) minshape = np.array(ibzk_kc.shape) world.broadcast(minshape, minrank) if world.rank != minrank: ibzk_kc = np.zeros(minshape, float) world.broadcast(ibzk_kc, minrank) return ibzk_kc