def add_eos_tasks(self): """ Read the optimized structure from the netcdf file and add to self a new a new list of ScfTask for the computation of the EOS with the GBRV parameters. """ self.history.info("Building EOS tasks") # Get the relaxed structure. self.relaxed_structure = relaxed_structure = self.relax_task.get_final_structure() # GBRV use nine points from -1% to 1% of the initial guess and fitting the results to a parabola. # Note that it's not clear to me if they change the volume or the lattice parameter! self.volumes = relaxed_structure.volume * np.arange(99, 101.25, 0.25) / 100. for vol in self.volumes: new_lattice = relaxed_structure.lattice.scale(vol) new_structure = Structure(new_lattice, relaxed_structure.species, relaxed_structure.frac_coords) # Add ecutsm extra = self.extra_abivars.copy() extra["ecutsm"] = 0.5 scf_input = abilab.AbinitInput(new_structure, self.dojo_pseudo) scf_input.add_abiobjects(self.ksampling, self.spin_mode, self.smearing) scf_input.set_vars(extra) # Register new task self.register_scf_task(scf_input) # Allocate new tasks and update the pickle database. self.flow.allocate() self.flow.build_and_pickle_dump()
def from_gsinp(cls, workdir, gsinp, volumes, ngqpt, manager=None): """ Args: workdir: gsinp: volumes: ngqpt: manager: """ ngqpt = np.reshape(ngqpt, 3) flow = cls(workdir=workdir, manager=manager) # Construct len(volumes) works. Each work performs the structure relaxation # at fixed volume followed by DFPT calculation with the relaxed structure. for vol in volumes: # Build GS input file for new structure with rescaled volume. new_lattice = gsinp.structure.lattice.scale(vol) new_structure = Structure(new_lattice, gsinp.structure.species, gsinp.structure.frac_coords) new_input = gsinp.new_with_structure(new_structure) # Register work. work = RelaxAndPhononWork.from_gsinp(new_input, ngqpt, optcell=3, ionmov=3) flow.register_work(work) return flow
def structure(self): coords = [] coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) return Structure(lattice, ["Si", "Si"], coords)
def from_gs_input(cls, gsinp, voldelta, scdims, phonopy_kwargs=None, displ_kwargs=None): """ Build the work from an :class:`AbinitInput` object representing a GS calculations. Args: gsinp: :class:`AbinitInput` object representing a GS calculation in the initial unit cell. voldelta: Absolute increment for unit cell volume. The three volumes are: [v0 - voldelta, v0, v0 + voldelta] where v0 is taken from gsinp.structure. scdims: Number of unit cell replicas along the three reduced directions. phonopy_kwargs: (Optional) dictionary with arguments passed to Phonopy constructor. displ_kwargs: (Optional) dictionary with arguments passed to generate_displacements. Return: `PhonopyGruneisenWork` instance. """ new = cls() # Save arguments that will be used to call phonopy for creating # the supercells with the displacements once the three volumes have been relaxed. new.scdims = np.array(scdims) if new.scdims.shape != (3, ): raise ValueError("Expecting 3 int in scdims but got %s" % str(new.scdims)) new.phonopy_kwargs = phonopy_kwargs if phonopy_kwargs is not None else {} new.displ_kwargs = displ_kwargs if displ_kwargs is not None else {} # Build three tasks for structural optimization at constant volume. v0 = gsinp.structure.volume if voldelta <= 0: raise ValueError("voldelta must be > 0 but got %s" % voldelta) volumes = [v0 - voldelta, v0, v0 + voldelta] if any(v <= 0 for v in volumes): raise ValueError("volumes must be > 0 but got %s" % str(volumes)) for vol in volumes: # Build new structure new_lattice = gsinp.structure.lattice.scale(vol) new_structure = Structure(new_lattice, gsinp.structure.species, gsinp.structure.frac_coords) new_input = gsinp.new_with_structure(new_structure) # Set variables for structural optimization at constant volume. new_input.pop_tolerances() new_input.set_vars(optcell=3, ionmov=3, tolvrs=1e-10, toldff=1.e-6) new_input.set_vars_ifnotin(ecutsm=0.5, dilatmx=1.05) new.register_relax_task(new_input) return new
def structure_from_atoms(atoms): """ Convert a phonopy Atoms object into a pymatgen Structure. """ return Structure(lattice=atoms.cell, species=atoms.symbols, coords=atoms.scaled_positions, validate_proximity=False, to_unit_cell=False, coords_are_cartesian=False, site_properties=None)
def from_scf_input(cls, scf_input, npoints=4, deltap_vol=0.25, ecutsm=0.5, move_atoms=True, manager=None): """ Build a EosWork from an AbinitInput representing a SCF-GS calculation. Args: scf_input: AbinitInput for SCF-GS used as template to generate the other inputs. npoints: Number of volumes generated on the right (left) of the equilibrium volume The total number of points is therefore 2 * n + 1. deltap_vol: Step of the linear mesh given in relative percentage of the equilibrium volume The step is thus: v0 * deltap_vol / 100. ecutsm: Value of ecutsm input variable. If `scf_input` does not provide ecutsm, this value will be used else the vale in `scf_input`. move_atoms: If True, a structural relaxation of ions is performed for each volume This is needed if the atomic positions are non fixed by symmetry. manager: TaskManager instance. Use default if None. Return: EosWork instance. """ new_work = cls(manager=manager) structure = scf_input.structure lattice_type = structure.spget_lattice_type() assert lattice_type is not None dvol = structure.volume * deltap_vol / 100 v0 = structure.volume - dvol * npoints new_work.input_volumes = [v0 + ipt * dvol for ipt in range(2 * npoints + 1)] if "ecutsm" not in scf_input: print("Input does not define ecutsm input variable.\n", "A default value of %s will be added to all the EOS inputs" % ecutsm) for vol in new_work.input_volumes: # Build structure with new volume and generate new input. new_lattice = structure.lattice.scale(vol) new_structure = Structure(new_lattice, structure.species, structure.frac_coords) new_input = scf_input.new_with_structure(new_structure) # Add ecutsm if not already present. new_input.set_vars_ifnotin(ecutsm=ecutsm) if lattice_type == "cubic" and not move_atoms: # Perform GS calculations without moving atoms, cells do not need to be relaxed. new_input.pop_vars(["ionmov", "optcell", "ntime"]) new_work.register_scf_task(new_input) else: # Constant-volume optimization of cell geometry + atoms. # (modify acell and rprim under constraint - normalize the vectors of rprim to generate the acell) # In principle one could take into account the symmetry of the lattice... new_input.set_vars_ifnotin(ionmov=2, ntime=50, optcell=3, dilatmx=1.05) new_work.register_relax_task(new_input) return new_work
def from_gs_input(cls, gs_inp, voldelta, ngqpt, tolerance=None, with_becs=False, ddk_tolerance=None, workdir=None, manager=None): """ Build the work from an |AbinitInput| representing a GS calculations. Args: gs_inp: |AbinitInput| representing a GS calculation in the initial unit cell. voldelta: Absolute increment for unit cell volume. The three volumes are: [v0 - voldelta, v0, v0 + voldelta] where v0 is taken from gs_inp.structure. ngqpt: three integers defining the q-mesh for phonon calculations. tolerance: dict {"varname": value} with the tolerance to be used in the phonon run. None to use AbiPy default. with_becs: Activate calculation of Electric field and Born effective charges. ddk_tolerance: dict {"varname": value} with the tolerance used in the DDK run if with_becs. None to use AbiPy default. """ new = cls(workdir=workdir, manager=manager) new.ngqpt = np.reshape(ngqpt, (3,)) new.with_becs = with_becs new.ddk_tolerance = ddk_tolerance new.tolerance = tolerance if any(gs_inp["ngkpt"] % new.ngqpt != 0): raise ValueError("Kmesh and Qmesh must be commensurate.\nGot ngkpt: `%s`\nand ngqpt: `%s`" % ( str(gs_inp["ngkpt"]), str(new.ngqpt))) # Build three tasks for structural optimization at constant volume. v0 = gs_inp.structure.volume if voldelta <= 0: raise ValueError("voldelta must be > 0 but got %s" % voldelta) volumes = [v0 - voldelta, v0, v0 + voldelta] if any(v <= 0 for v in volumes): raise ValueError("volumes must be > 0 but got %s" % str(volumes)) # Keep a copy of the GS input that will be used to generate the Phonon Works new.gs_inp = gs_inp.deepcopy() new.relax_tasks = [] for vol in volumes: # Build new structure new_lattice = gs_inp.structure.lattice.scale(vol) new_structure = Structure(new_lattice, gs_inp.structure.species, gs_inp.structure.frac_coords) new_input = gs_inp.new_with_structure(new_structure) # Set variables for structural optimization at constant volume. new_input.pop_tolerances() new_input.set_vars(optcell=3, ionmov=3, tolvrs=1e-10, toldff=1.e-6) new_input.set_vars_ifnotin(ecutsm=0.5, dilatmx=1.05) t = new.register_relax_task(new_input) new.relax_tasks.append(t) return new
def half_heusler(a, species): # fcc lattice with 3 atoms as basis # prototype: AgAlGe # See also http://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-wp-list?gnum=216 lattice = 0.5 * float(a) * np.array([0, 1, 1, 1, 0, 1, 1, 1, 0]) frac_coords = np.reshape( [ 0, 0, 0, # Ag 0.5, 0.5, 0.5, # Al 1 / 4, 1 / 4, 1 / 4, # Ge #3/4, 3/4, 3/4, # Z ], (3, 3)) return Structure(lattice, species, frac_coords, coords_are_cartesian=False)
def AbinitNscfTasks(structure, kpoints, ecut, nscf_bands, nscf_kpoints=None, **kwargs): from abipy.core.structure import Structure from abipy.abio.factories import scf_for_phonons from pymatgen.core.units import bohr_to_ang #extract pseudos pseudo_list = [] for atype, (mass, pseudo) in structure['atypes'].items(): pseudo_list.append(pseudo) pseudo_table = kwargs.pop("pseudo_table", pseudo_list) #create a PwInput file just to read the ibrav from structure qe_input = PwIn.from_structure_dict(structure) lattice, coords, species = qe_input.get_cell() lattice = [[col * bohr_to_ang for col in row] for row in lattice] structure = Structure(lattice, species, coords) #create an AbinitInput file from structure spin_mode = kwargs.pop('spin_mode', 'unpolarized') smearing = kwargs.pop('smearing', 'nosmearing') inp = scf_for_phonons(structure, pseudo_table, spin_mode=spin_mode, smearing=smearing, ecut=ecut / 2) return AbinitNscfTasksFromAbinitInput(inp, kpoints, ecut, nscf_bands, nscf_kpoints=nscf_kpoints, **kwargs)
def _parse_dims(self): """ Parse basic dimensions and get structure from the header of the file. """ self.version, self._structure, self.grid_size = None, None, None # Init dictionary with parameters. self.params_section = OrderedDict([ (s, OrderedDict()) for s in ("MAIN", "WANNIERISE", "PLOTTING", "DISENTANGLE") ]) params_done = False for iln, line in enumerate(self.lines): # Check for any warnings if 'Warning' in line: self.warnings.append(line) continue if "Time to read parameters" in line: params_done = True continue # Get release string. if "Release:" in line: i = line.find("Release:") self.version = line[i:].split()[1] continue # Parse lattice. if "Lattice Vectors" in line and self._structure is None: # Lattice Vectors (Ang) # a_1 0.000000 2.715473 2.715473 # a_2 2.715473 0.000000 2.715473 # a_3 2.715473 2.715473 0.000000 lattice = np.array([ list(map(float, self.lines[iln + j].split()[1:])) for j in range(1, 4) ]) continue # Parse atoms. if "| Site " in line and self._structure is None: # *----------------------------------------------------------------------------* # | Site Fractional Coordinate Cartesian Coordinate (Ang) | # +----------------------------------------------------------------------------+ # | Si 1 0.00000 0.00000 0.00000 | 0.00000 0.00000 0.00000 | # | Si 2 0.25000 0.25000 0.25000 | 1.35774 1.35774 1.35774 | # *----------------------------------------------------------------------------* frac_coords, species = [], [] i = iln + 2 while True: l = self.lines[i].strip() if l.startswith("*"): break i += 1 tokens = l.replace("|", " ").split() species.append(tokens[0]) frac_coords.append(np.array(list(map(float, tokens[2:5])))) self._structure = Structure(lattice, species, frac_coords) continue # Parse kmesh. if "Grid size" in line: # Grid size = 2 x 2 x 2 Total points = 8 tokens = line.split("=")[1].split("Total")[0].split("x") self.grid_size = np.array(list(map(int, tokens))) continue if not params_done and any(sname in line for sname in self.params_section): #*---------------------------------- MAIN ------------------------------------* #| Number of Wannier Functions : 4 | #| Wavefunction spin channel : up | #*----------------------------------------------------------------------------* # Use params_done to avoid parsing the second section with WANNIERISE key = line.replace("*", "").replace("-", "").strip() i = iln + 1 l = self.lines[i].strip() while not l.startswith("*-"): tokens = [s.strip() for s in l.replace("|", "").split(":")] self.params_section[key][tokens[0]] = tokens[1] i += 1 l = self.lines[i].strip() continue # Extract important metadata from sections and convert from string. self.nwan = int( self.params_section["MAIN"]["Number of Wannier Functions"]) if self.params_section["DISENTANGLE"].get("Using band disentanglement", "F") == "T": self.use_disentangle = True
def __init__(self, structure, pseudo, kppa, connect, ecut=None, pawecutdg=None, ecutsm=0.5, spin_mode="polarized", include_soc=False, toldfe=1.e-9, smearing="fermi_dirac:0.1 eV", chksymbreak=0, workdir=None, manager=None, **kwargs): """ Build a :class:`Work` for the computation of the deltafactor. Args: structure: :class:`Structure` object pseudo: :class:`Pseudo` object. kppa: Number of k-points per reciprocal atom. connect: True if the SCF run should be initialized from the previous run. spin_mode: Spin polarization mode. toldfe: Tolerance on the energy (Ha) smearing: Smearing technique. workdir: String specifing the working directory. manager: :class:`TaskManager` responsible for the submission of the tasks. """ super(DeltaFactorWork, self).__init__(workdir=workdir, manager=manager) self._pseudo = pseudo self.include_soc = include_soc spin_mode = SpinMode.as_spinmode(spin_mode) smearing = Smearing.as_smearing(smearing) # Compute the number of bands from the pseudo and the spin-polarization. # Add 6 bands to account for smearing. #nval = structure.num_valence_electrons(self.pseudo) #nband = int(nval / spin_mode.nsppol) + 6 # Set extra_abivars self.ecut, self.pawecutdg = ecut, pawecutdg extra_abivars = dict( ecut=ecut, pawecutdg=pawecutdg, ecutsm=ecutsm, toldfe=toldfe, prtwf=-1 if not connect else 1, chkprim=0, nstep=200, fband=2.0, # 0.5 is the default value but it's not large enough from some systems. #paral_kgb=paral_kgb, #nband=nband, #mem_test=0, ) extra_abivars.update(**kwargs) self._input_structure = structure v0 = structure.volume # From 94% to 106% of the equilibrium volume. self.volumes = v0 * np.arange(94, 108, 2) / 100. for vol in self.volumes: # Build new structure new_lattice = structure.lattice.scale(vol) new_structure = Structure(new_lattice, structure.species, structure.frac_coords) ksampling = KSampling.automatic_density(new_structure, kppa, chksymbreak=chksymbreak, use_time_reversal=spin_mode.nspinor==1) scf_input = abilab.AbinitInput(structure=new_structure, pseudos=self.dojo_pseudo) scf_input.add_abiobjects(ksampling, smearing, spin_mode) scf_input.set_vars(extra_abivars) # Magnetic materials with nspinor = 2 requires connection # and a double SCF run (nsppol = 2 first then nspinor = 2). if connect and spin_mode.nspinor == 2: print("Using collinear then noncollinear scf task") self.register_collinear_then_noncollinear_scf_task(scf_input) else: self.register_scf_task(scf_input) if connect: logger.info("Connecting SCF tasks using previous WFK file") middle = len(self.volumes) // 2 filetype = "WFK" for i, task in enumerate(self[:middle]): task.add_deps({self[i + 1]: filetype}) for i, task in enumerate(self[middle+1:]): task.add_deps({self[middle + i]: filetype})
ns = 2000 # number of structures to be included in the movie (0 for taking all structures) skip_freq = 10 # number of structures to be skipped after each structure ang = 0.529177249 def divide_chunks(l, n): # looping till length l for i in range(0, len(l), n): yield l[i:i + n] # Reading Files cellfile = open(pref + '.cel').read().splitlines() posfile = open(pref + '.pos').read().splitlines() lats = list(divide_chunks(cellfile, 4)) coords = list(divide_chunks(posfile, len(sp) + 1)) # Loading Structures structures = [] if (ns == 0): ns = len(lats) for i in range(0, ns, skip_freq): tlat = np.loadtxt(lats[i], skiprows=1).transpose() * ang tpos = np.loadtxt(coords[i], skiprows=1) * ang structures.append(Structure(tlat, sp, tpos, coords_are_cartesian=True)) # Writing output to '[pref]_movie.axsf' f = open(pref + '_movie.axsf', 'w') xsf.xsf_write_structure(f, structures)