Ejemplo n.º 1
0
    def get_elastic_tensor(self, n=5, d=2, mode='full', systems=None):
        '''
        Calculate elastic tensor of the crystal.
        It is assumed that the crystal is converged and optimized 
        under intended pressure/stress.
        The geometry and stress at the call point is taken as
        the reference point. No additional optimization will be run.
        It is also assumed that the calculator is set to pure IDOF optimization.
        The size of used finite deformation is passed in d parameter as a 
        percentage relative deformation. The n parameter defines number of 
        deformed structures used in the calculation.
        '''
        # TODO: Provide API to enforce calculator selection

        # Deformation look-up table
        # Perhaps the number of deformations for trigonal
        # system could be reduced to [0,3] but better safe then sorry
        deform = {
            "Cubic": [[0, 3], regular],
            "Hexagonal": [[0, 2, 3, 5], hexagonal],
            "Trigonal": [[0, 1, 2, 3, 4, 5], trigonal],
            "Tetragonal": [[0, 2, 3, 5], tetragonal],
            "Orthorombic": [[0, 1, 2, 3, 4, 5], orthorombic],
            "Monoclinic": [[0, 1, 2, 3, 4, 5], monoclinic],
            "Triclinic": [[0, 1, 2, 3, 4, 5], triclinic]
        }

        self.get_lattice_type()
        # Decide which deformations should be used
        axis, symm = deform[self.bravais]

        if mode != 'restart':
            # Generate deformations if we are not in restart mode
            systems = []
            for a in axis:
                if a < 3:  # tetragonal deformation
                    for dx in linspace(-d, d, n):
                        systems.append(
                            self.get_cart_deformed_cell(axis=a, size=dx))
                elif a < 6:  # sheer deformation (skip the zero angle)
                    for dx in linspace(d / 10.0, d, n):
                        systems.append(
                            self.get_cart_deformed_cell(axis=a, size=dx))

        # Just generate deformations for manual calculation
        if mode == 'deformations':
            return systems

        if mode != 'restart':
            # Run the calculation if we are not restarting
            r = ParCalculate(systems, self.get_calculator())
        else:
            r = systems

        ul = []
        sl = []
        p = self.get_pressure()
        for g in r:
            ul.append(g.get_strain(self))
            # Remove the pressure from the stress tensor
            sl.append(g.get_stress() - array([p, p, p, 0, 0, 0]))
        eqm = array(map(symm, ul))
        #print(eqm[0].shape, eqm.shape)
        eqm = reshape(eqm, (eqm.shape[0] * eqm.shape[1], eqm.shape[2]))
        #print(eqm)
        slm = reshape(array(sl), (-1, ))
        #print(eqm.shape, slm.shape)
        #print(slm)
        Bij = lstsq(eqm, slm)
        #print(Bij[0] / units.GPa)
        # Calculate elastic constants from Birch coeff.
        # TODO: Check the sign of the pressure array in the B <=> C relation
        if (symm == orthorombic):
            Cij = Bij[0] - array([-p, -p, -p, p, p, p, -p, -p, -p])
        elif (symm == tetragonal):
            Cij = Bij[0] - array([-p, -p, p, p, -p, -p])
        elif (symm == regular):
            Cij = Bij[0] - array([-p, p, -p])
        elif (symm == trigonal):
            Cij = Bij[0] - array([-p, -p, p, p, -p, p])
        elif (symm == hexagonal):
            Cij = Bij[0] - array([-p, -p, p, p, -p])
        elif (symm == monoclinic):
            #TODO: verify this pressure array
            Cij = Bij[0] - array([-p, -p, -p, p, p, p, -p, -p, -p, p, p, p, p])
        elif (symm == triclinic):
            #TODO: verify this pressure array
            Cij = Bij[0] - array(
                [-p, -p, -p, p, p, p, -p, -p, -p, p, p, p, p, p, p, p, p, p])
        return Cij, Bij
Ejemplo n.º 2
0
# Clean up the directory
calc.clean()

# Switch to cell IDOF optimizer
calc.set(isif=2)

# Elastic tensor by internal routine
Cij, Bij = cryst.get_elastic_tensor(n=5, d=0.2)
print("Cij (GPa):", Cij / units.GPa)

# Now let us do it (only c11 and c12) by hand
# with 3rd order polynomial fitting the points.
sys = []
for d in linspace(-0.2, 0.2, 10):
    sys.append(cryst.get_cart_deformed_cell(axis=0, size=d))
r = ParCalculate(sys, cryst.calc)
ss = []
for s in r:
    ss.append([s.get_strain(cryst), s.get_stress()])

# Plot strain-stress relation
ss = []
for p in r:
    ss.append([p.get_strain(cryst), p.get_stress()])
ss = array(ss)
lo = min(ss[:, 0, 0])
hi = max(ss[:, 0, 0])
mi = (lo + hi) / 2
wi = (hi - lo) / 2
xa = linspace(mi - 1.1 * wi, mi + 1.1 * wi, 50)
plt.plot(ss[:, 0, 0], ss[:, 1, 0], 'k.')
Ejemplo n.º 3
0
    def get_BM_EOS(self,
                   n=5,
                   lo=0.98,
                   hi=1.02,
                   recalc=False,
                   cleanup=True,
                   mode='full',
                   data=None):
        """
        Calculate Birch-Murnaghan Equation of State for the crystal:
        
        .. math::
           P(V)= \\frac{B_0}{B'_0}\\left[
           \\left({\\frac{V}{V_0}}\\right)^{-B'_0} - 1
           \\right]
        
        using n single-point structures ganerated from the 
        crystal (self) by the scan_volumes method between lo and hi 
        relative volumes. The BM EOS is fitted to the computed points by 
        least squares method. The returned value is a list of fitted 
        parameters: :math:`V_0, B_0, B_0'` if the fit succeded. 
        If the fitting fails the RuntimeError('Calculation failed') is reised.
        The data from the calculation and fit is stored in the bm_eos and pv
        members for future reference.
        
        *Note:* For now you have to set up the calculator to properly 
        optimize the structure without changing the volume at each point.
        There will be a way to specify basic types of the calculator 
        minimization at the later stage.
        """
        if self._calc is None:
            raise RuntimeError('Crystal object has no calculator.')

        if getattr(self, 'bm_eos', None) is None or recalc:
            # NOTE: The calculator should properly minimize the energy
            # at each volume by optimizing the internal degrees of freedom
            # in the cell and/or cell shape without touching the volume.
            # TODO: Provide api for specifying IDOF and Full optimization
            #       calculators. Maybe just calc_idof and calc_full members?
            if data is not None:  # analyse results of previous calc
                res = data
            elif mode == 'full':  # Make blocking calc of everything
                res = ParCalculate(self.scan_volumes(lo, hi, n),
                                   self.get_calculator(),
                                   cleanup=cleanup)
            elif mode == 'gener':  # generate data for separate calc
                return self.scan_volumes(lo, hi, n)
            else:
                print('Error: Unrecognized mode and no data. Read the docs!')
                return

        #for r in res :
        #print(r.get_volume(), self.get_pressure(), r.get_cell())

            pvdat = array([[
                r.get_volume(),
                self.get_pressure(r.get_stress()),
                norm(r.get_cell()[:, 0]),
                norm(r.get_cell()[:, 1]),
                norm(r.get_cell()[:, 2])
            ] for r in res])
            #print(pvdat)

            # Fitting functions
            fitfunc = lambda p, x: [BMEOS(xv, p[0], p[1], p[2]) for xv in x]
            errfunc = lambda p, x, y: fitfunc(p, x) - y

            # Estimate the initial guess assuming b0p=1
            # Limiting volumes
            v1 = min(pvdat[:, 0])
            v2 = max(pvdat[:, 0])
            # The pressure is falling with the growing volume
            p2 = min(pvdat[:, 1])
            p1 = max(pvdat[:, 1])
            b0 = (p1 * v1 - p2 * v2) / (v2 - v1)
            v0 = v1 * (p1 + b0) / b0
            # Initial guess
            p0 = [v0, b0, 1]
            #Fitting
            #print(p0)
            p1, succ = optimize.leastsq(errfunc,
                                        p0[:],
                                        args=(pvdat[:, 0], pvdat[:, 1]))
            if not succ:
                raise RuntimeError('Calculation failed')
            self.bm_eos = p1
            self.pv = pvdat
        return self.bm_eos
Ejemplo n.º 4
0
        # Scan over deformations

        # Switch to IDOF optimizer
        calc.set(isif=2)

        # Elastic tensor by internal routine
        Cij, Bij = cryst.get_elastic_tensor(n=5, d=0.33)
        print("Cij (GPa):", Cij / units.GPa)

        calc.clean()

        # Now let us do it (only c11 and c12) by hand
        sys = []
        for d in linspace(-0.5, 0.5, 6):
            sys.append(cryst.get_cart_deformed_cell(axis=0, size=d))
        r = ParCalculate(sys, cryst.get_calculator())
        ss = []
        for s in r:
            ss.append([s.get_strain(cryst), s.get_stress()])
        # Plot strain-stress relation
        figure(3)

        ss = []
        for p in r:
            ss.append([p.get_strain(cryst), p.get_stress()])
        ss = array(ss)
        lo = min(ss[:, 0, 0])
        hi = max(ss[:, 0, 0])
        mi = (lo + hi) / 2
        wi = (hi - lo) / 2
        xa = linspace(mi - 1.1 * wi, mi + 1.1 * wi, 50)
Ejemplo n.º 5
0
a=4 ; c=crystal('Si',[(0,0,0)],spacegroup=221,cellpar=[a,a,a,90,90,90])
spglib.get_spacegroup(c)

block=False

calc = ClusterVasp(block=block)

calc.set(prec = 'Accurate', xc = 'PBE', lreal = False,
            nsw=30, ediff=1e-8, ibrion=2, kpts=[3,3,3])

calc.set(isif=3)

c.set_calculator(calc)

l=ParCalculate(c,calc,cleanup=False,block=block)

s = l[0]

wait_for_results(l)

print(s.get_forces(), s.get_cell(), s.get_stress(), s.get_pressure())

calc.set(isif=2)
sl=s.get_BM_EOS(mode='gener')
res=ParCalculate(sl,calc,block=block,prefix='BMEOS_')

wait_for_results(res)

print(s.get_BM_EOS(data=res))
Ejemplo n.º 6
0
systems = []
for av in numpy.linspace(a * 0.95, a * 1.05, 5):
    systems.append(
        crystal(['Mg', 'O'], [(0, 0, 0), (0.5, 0.5, 0.5)],
                spacegroup=225,
                cellpar=[av, av, av, 90, 90, 90]))

pcalc = ClusterVasp(nodes=1, ppn=8)
pcalc.set(prec='Accurate',
          xc='PBE',
          lreal=False,
          isif=2,
          nsw=20,
          ibrion=2,
          kpts=[1, 1, 1])
res = ParCalculate(systems, pcalc)

v = []
p = []
for s in res:
    v.append(s.get_volume())
    p.append(s.get_pressure() / GPa)

plot(v, p, 'o')
show()

# You can specify the directory with prepared VASP crystal for the test run
# or run through all prepared cases.
if len(sys.argv) > 1:
    crystals = [crystal(ase.io.read(sys.argv[1] + '/CONTCAR'))]
else:
Ejemplo n.º 7
0
# Clean up the directory
calc.clean()

sys = []
# Iterate over lattice constant in the +/-5% range
for av in numpy.linspace(a * 0.95, a * 1.05, 5):
    sys.append(
        crystal(['Mg', 'O'], [(0, 0, 0), (0.5, 0.5, 0.5)],
                spacegroup=225,
                cellpar=[av, av, av, 90, 90, 90]))

# Define the template calculator for this run
# We can use the calc from above. It is only used as a template.
# Just change the params to fix the cell volume
calc.set(isif=2)

# Run the calculation for all systems in sys in parallel
# The result will be returned as list of systems res
res = ParCalculate(sys, calc)

# Collect the results
v = []
p = []
for s in res:
    v.append(s.get_volume())
    p.append(s.get_isotropic_pressure(s.get_stress()))

# Plot the result (you need matplotlib for this
plt.plot(v, p, 'o')
plt.show()