Пример #1
0
def test_lj_insert():
    system_file = pkg_resources.resource_filename(__name__, '../data/lp_avg.chk')
    ff_file = pkg_resources.resource_filename(__name__, '../data/pars.txt')
    adsorbate_file = pkg_resources.resource_filename(__name__, '../data/argon.chk')

    T = 87 * kelvin
    P = 1 * bar
    MD_trial_fraction = 0.000
    rcut = 15 * angstrom
    fugacity = P

    # Try 10 configurations
    for i in range(10):

        mcmd = MCMD(system_file, adsorbate_file, ff_file, T, P, fugacity, MD_trial_fraction, rcut, fixed_N = 25)

        N_frame = len(mcmd.pos)
        new_pos = random_ads(mcmd.pos_ads, mcmd.rvecs)
        pos = np.append(mcmd.pos, new_pos, axis=0)

        new_pos2 = random_ads(mcmd.pos_ads, mcmd.rvecs)
        pos2 = np.append(pos, new_pos2, axis=0)

        Z_ads = 2
        plen = len(pos)
        plen2 = len(pos2)

        e_vdw = LJ(pos2, N_frame, Z_ads, mcmd.rvecs_flat, mcmd.data.sigmas[:plen2], mcmd.data.epsilons[:plen2], rcut)

        e_vdw_insert = LJ_insert(pos, len(pos)-mcmd.nads, mcmd.N_frame, mcmd.rvecs_flat, mcmd.data.sigmas[:plen], mcmd.data.epsilons[:plen], mcmd.rcut)
        e_vdw_insert += LJ_insert(pos2, len(pos2)-mcmd.nads, mcmd.N_frame, mcmd.rvecs_flat, mcmd.data.sigmas[:plen2], mcmd.data.epsilons[:plen2], mcmd.rcut)
        print(e_vdw/kjmol, e_vdw_insert/kjmol)

        assert np.abs(e_vdw - e_vdw_insert) < 1e-8
Пример #2
0
def test_ei_rcut():
    system_file = pkg_resources.resource_filename(__name__, '../data/lp_avg.chk')
    ff_file = pkg_resources.resource_filename(__name__, '../data/pars.txt')
    adsorbate_file = pkg_resources.resource_filename(__name__, '../data/CO2.chk')

    T = 87 * kelvin
    P = 1 * bar
    MD_trial_fraction = 0.000
    fugacity = P


    def get_total_ei(rcut, new_pos, new_pos2):

        mcmd = MCMD(system_file, adsorbate_file, ff_file, T, P, fugacity, MD_trial_fraction, rcut, fixed_N = 25)
        sfac = Sfac(mcmd.pos, mcmd.N_frame, mcmd.rvecs_flat, mcmd.charges, mcmd.alpha, mcmd.gcut)

        pos = np.append(mcmd.pos, new_pos, axis=0)
        pos2 = np.append(pos, new_pos2, axis=0)
        Z_ads = 2
        plen = len(pos)
        plen2 = len(pos2)

        ei_insert_1 = electrostatics_realspace_insert(mcmd.N_frame, len(pos)-mcmd.nads, pos, mcmd.rvecs_flat, mcmd.data.charges[:plen], mcmd.data.radii[:plen], rcut, mcmd.alpha, mcmd.gcut)
        sfac = np.array(ewald_insertion(sfac, new_pos, mcmd.rvecs_flat, mcmd.data.charges_ads, mcmd.alpha, mcmd.gcut))
        ew_insert_1 = ewald_from_sfac(sfac, mcmd.rvecs_flat, mcmd.alpha, mcmd.gcut)

        ei_insert_2 = ei_insert_1 + electrostatics_realspace_insert(mcmd.N_frame, len(pos2)-mcmd.nads, pos2, mcmd.rvecs_flat, mcmd.data.charges[:plen2], mcmd.data.radii[:plen2], rcut, mcmd.alpha, mcmd.gcut)
        sfac = np.array(ewald_insertion(sfac, new_pos2, mcmd.rvecs_flat, mcmd.data.charges_ads, mcmd.alpha, mcmd.gcut))
        ew_insert_2 = ewald_from_sfac(sfac, mcmd.rvecs_flat, mcmd.alpha, mcmd.gcut)

        ei_full_2 = electrostatics(pos2, mcmd.N_frame, 2, mcmd.rvecs_flat, mcmd.data.charges[:plen2], mcmd.data.radii[:plen2], rcut, mcmd.alpha, mcmd.gcut)

        return ew_insert_2 + ei_insert_2, ei_full_2


    # Try 10 configurations
    for i in range(10):

        mcmd = MCMD(system_file, adsorbate_file, ff_file, T, P, fugacity, MD_trial_fraction, 30*angstrom, fixed_N = 25)
        sfac = Sfac(mcmd.pos, mcmd.N_frame, mcmd.rvecs_flat, mcmd.charges, mcmd.alpha, mcmd.gcut)

        new_pos = random_ads(mcmd.pos_ads, mcmd.rvecs)
        new_pos2 = random_ads(mcmd.pos_ads, mcmd.rvecs)

        ei_base, ei_base_check = get_total_ei(30*angstrom, new_pos, new_pos2)
        print('base: ', ei_base/kjmol)
        assert(np.abs(ei_base - ei_base_check) < 1e-8)

        for rcut in 25, 20, 15, 12, 10:
            ei, ei_check = get_total_ei(rcut*angstrom, new_pos, new_pos2)
            print(rcut, ei/kjmol)
            assert np.abs(ei_base - ei)/kjmol < 0.2
            assert(np.abs(ei - ei_check) < 1e-8)
Пример #3
0
def test_mm3_tail():

    system_file = pkg_resources.resource_filename(__name__, '../data/lp_avg.chk')
    ff_file = pkg_resources.resource_filename(__name__, '../data/pars.txt')
    adsorbate_file = pkg_resources.resource_filename(__name__, '../data/argon.chk')

    T = 87 * kelvin
    P = 1 * bar
    MD_trial_fraction = 0.000
    fugacity = P

    mcmd = MCMD(system_file, adsorbate_file, ff_file, T, P, fugacity, MD_trial_fraction, 8*angstrom, fixed_N = 25)

    # Try 10 configurations
    for i in range(10):

        N_frame = len(mcmd.pos)
        new_pos = random_ads(mcmd.pos_ads, mcmd.rvecs)
        pos = np.append(mcmd.pos, new_pos, axis=0)

        Z_ads = 1
        plen = len(pos)

        e_vdw_insert_base = MM3_insert(pos, len(pos)-mcmd.nads, mcmd.N_frame, mcmd.rvecs_flat, mcmd.data.sigmas[:plen], mcmd.data.epsilons[:plen], 30*angstrom)
        print('base: ', e_vdw_insert_base/kjmol)

        for rcut in 25, 20, 15, 12, 10:
            e_vdw_insert = MM3_insert(pos, len(pos)-mcmd.nads, mcmd.N_frame, mcmd.rvecs_flat, mcmd.data.sigmas[:plen], mcmd.data.epsilons[:plen], rcut*angstrom)
            print(rcut, e_vdw_insert/kjmol)
            assert np.abs(e_vdw_insert_base - e_vdw_insert)/kjmol < 0.2
Пример #4
0
def test_ei_insert():
    system_file = pkg_resources.resource_filename(__name__, '../data/lp_avg.chk')
    ff_file = pkg_resources.resource_filename(__name__, '../data/pars.txt')
    adsorbate_file = pkg_resources.resource_filename(__name__, '../data/CO2.chk')

    T = 87 * kelvin
    P = 1 * bar
    MD_trial_fraction = 0.000
    rcut = 15 * angstrom
    fugacity = P

    # Try 10 configurations
    for i in range(10):

        mcmd = MCMD(system_file, adsorbate_file, ff_file, T, P, fugacity, MD_trial_fraction, rcut, fixed_N = 25)
        sfac = Sfac(mcmd.pos, mcmd.N_frame, mcmd.rvecs_flat, mcmd.charges, mcmd.alpha, mcmd.gcut)

        N_frame = len(mcmd.pos)
        new_pos = random_ads(mcmd.pos_ads, mcmd.rvecs)
        pos = np.append(mcmd.pos, new_pos, axis=0)

        new_pos2 = random_ads(mcmd.pos_ads, mcmd.rvecs)
        pos2 = np.append(pos, new_pos2, axis=0)

        Z_ads = 2
        plen = len(pos)
        plen2 = len(pos2)

        ei_insert_1 = electrostatics_realspace_insert(mcmd.N_frame, len(pos)-mcmd.nads, pos, mcmd.rvecs_flat, mcmd.data.charges[:plen], mcmd.data.radii[:plen], mcmd.rcut, mcmd.alpha, mcmd.gcut)
        sfac = np.array(ewald_insertion(sfac, new_pos, mcmd.rvecs_flat, mcmd.data.charges_ads, mcmd.alpha, mcmd.gcut))
        ew_insert_1 = ewald_from_sfac(sfac, mcmd.rvecs_flat, mcmd.alpha, mcmd.gcut)

        ei_full_1 = electrostatics(pos, mcmd.N_frame, 1, mcmd.rvecs_flat, mcmd.data.charges[:plen], mcmd.data.radii[:plen], mcmd.rcut, mcmd.alpha, mcmd.gcut)

        print(ei_insert_1/kjmol, ew_insert_1/kjmol, ei_full_1/kjmol)
        assert (np.abs(ei_full_1 - ei_insert_1 - ew_insert_1) < 1e-8).all()


        ei_insert_2 = ei_insert_1 + electrostatics_realspace_insert(mcmd.N_frame, len(pos2)-mcmd.nads, pos2, mcmd.rvecs_flat, mcmd.data.charges[:plen2], mcmd.data.radii[:plen2], mcmd.rcut, mcmd.alpha, mcmd.gcut)
        sfac = np.array(ewald_insertion(sfac, new_pos2, mcmd.rvecs_flat, mcmd.data.charges_ads, mcmd.alpha, mcmd.gcut))
        ew_insert_2 = ewald_from_sfac(sfac, mcmd.rvecs_flat, mcmd.alpha, mcmd.gcut)

        ei_full_2 = electrostatics(pos2, mcmd.N_frame, 2, mcmd.rvecs_flat, mcmd.data.charges[:plen2], mcmd.data.radii[:plen2], mcmd.rcut, mcmd.alpha, mcmd.gcut)

        print(ei_insert_2/kjmol, ew_insert_2/kjmol, ei_full_2/kjmol)
        assert (np.abs(ei_full_2 - ei_insert_2 - ew_insert_2) < 1e-8).all()
Пример #5
0
    def run_widom(self, N_iterations, N_sample):

        if not (os.path.isdir('results')):
            os.mkdir('results')

        E_samples = []

        print('\n Iteration  inst. Hads [kJ/mol]  inst. K_H [mol/kg/bar]  time [s]')
        print('-------------------------------------------------------------------')

        t_it = time()

        for iteration in range(N_iterations):

            new_pos = random_ads(self.pos_ads, self.rvecs)
            self.pos = np.append(self.pos, new_pos, axis=0)
            e_insertion = self.compute_insertion(new_pos)
            E_samples.append(e_insertion)

            # Reset e_el_real, e_vdw, sfac and pos
            self.sfac = deepcopy(self.sfac_frame)
            self.e_el_real, self.e_vdw = 0, 0
            self.pos = self.pos[:-self.n_ad]

            if iteration > 0 and iteration % N_sample == 0:

                E_temp = np.array(E_samples)

                # if adsorption energies are very positive, a division by 0 error will occur here:
                try:
                    E_ads = np.average(E_temp*np.exp(-self.beta*E_temp))/np.average(np.exp(-self.beta*E_temp))
                except:
                    E_m = min(E_temp)
                    E_ads = np.average(E_temp*np.exp(-self.beta*(E_temp-E_m)))/np.average(np.exp(-self.beta*(E_temp-E_m)))

                rho = self.mass/np.linalg.det(self.rvecs)
                K_H = self.beta/rho*np.average(np.exp(-self.beta*E_temp))

                print(' {:7.7}       {:7.7}                {:7.7}         {:7.4}'.format(
                      str(iteration),str((E_ads - 1/self.beta)/kjmol),str(K_H/(avogadro/(kilogram*bar))), time()-t_it)
                      )
                t_it = time()

        E_samples = np.array(E_samples)

        if self.write_all:
            np.save('results/Widom_E.npy', np.array(E_samples))
        else:
            rho = self.mass/np.linalg.det(self.rvecs)
            # Do bootstrapping
            def bootstrap(data, type='Hads'):
                means = []
                for i in range(100):
                    means.append(sample_mean(data, type))
                return np.average(means), np.std(np.array(means))

            def sample_mean(data, type):
                resampled = np.random.choice(data, len(data), replace=True)
                if type == 'Hads':
                    try:
                        return (np.average(resampled*np.exp(-self.beta*resampled))/np.average(np.exp(-self.beta*resampled)) - 1/self.beta)/kjmol
                    except:
                        E_m = min(resampled)
                        return (np.average(resampled*np.exp(-self.beta*(resampled-E_m)))/np.average(np.exp(-self.beta*(resampled-E_m))) - 1/self.beta)/kjmol
                elif type == 'KH':
                    return np.average(self.beta/rho*np.exp(-self.beta*resampled) / (avogadro/(kilogram*bar)))

            H_ads_av, H_ads_std = bootstrap(E_samples, type='Hads')
            K_H_av, K_H_std = bootstrap(E_samples, type='KH')

            bootstrap_data = np.array([[H_ads_av, H_ads_std], [K_H_av, K_H_std]])
            print(bootstrap_data)

            np.save('results/Widom_result.npy', bootstrap_data)
Пример #6
0
    def run_GCMC(self, N_iterations, N_sample):

        A = Acceptance()

        if rank == 0:
            if not (os.path.isdir('results')):
                try:
                    os.mkdir('results')
                except:pass

            if self.write_traj:
                ftraj = open('results/traj_%.8f.xyz'%(self.P/bar), 'w')

        e = 0
        t_it = time()

        N_samples = []
        E_samples = []
        pressures = []
        traj = []
        q0s = []

        if rank == 0:
            print('\n Iteration  inst. N    inst. E    inst. V     time [s]')
            print('--------------------------------------------------------')

        for iteration in range(N_iterations+1):

            if self.ads_ei:
                sfac_init = deepcopy(self.sfac)
            pos_init = deepcopy(self.pos)
            rvecs_init = deepcopy(self.rvecs)
            rvecs_flat_init = deepcopy(self.rvecs_flat)
            V_init = self.V
            e_el_real_init = self.e_el_real
            e_vdw_init = self.e_vdw
            switch = np.random.rand()
            acc = 0

            # Insertion / deletion
            if(switch < self.prob[0] and not self.Z_ads == self.fixed_N):

                if(switch < self.prob[0]/2):

                    new_pos = random_ads(self.pos_ads, self.rvecs)
                    e_new = self.insertion(new_pos)

                    exp_value = self.beta * (-e_new + e)
                    if(exp_value > 100):
                        acc = 1
                    elif(exp_value < -100):
                        acc = 0
                    else:
                        acc = min(1, self.V*self.beta*self.fugacity/self.Z_ads * np.exp(exp_value))

                    # Reject monte carlo move
                    if np.random.rand() > acc:
                        self.pos = pos_init
                        if self.ads_ei:
                            self.sfac = sfac_init
                        self.e_el_real = e_el_real_init
                        self.e_vdw = e_vdw_init
                        self.Z_ads -= 1
                    else:
                        e = e_new

                elif(self.Z_ads > 0):

                    deleted_coord, e_new = self.deletion()

                    exp_value = -self.beta * (e_new - e)
                    if(exp_value > 100):
                        acc = 1
                    else:
                        acc = min(1, (self.Z_ads+1)/self.V/self.beta/self.fugacity * np.exp(exp_value))

                    # Reject monte carlo move
                    if np.random.rand() > acc:
                        self.pos = pos_init
                        if self.ads_ei:
                            self.sfac = sfac_init
                        self.e_el_real = e_el_real_init
                        self.e_vdw = e_vdw_init
                        self.Z_ads += 1
                    else:
                        e = e_new

            elif(switch < self.prob[1]):

                if self.Z_ads != 0:

                    trial = np.random.randint(self.Z_ads)

                    if((switch < self.prob[0] + (self.prob[1]-self.prob[0])/2) or self.nads == 1):

                        # Calculate translation energy as deletion + insertion of molecule
                        deleted_coord, e_new = self.deletion()
                        deleted_coord += self.step * (np.random.rand(3) - 0.5)
                        e_new = self.insertion(deleted_coord)

                    else:

                        # Calculate rotation energy as deletion + insertion of molecule
                        deleted_coord, e_new = self.deletion()
                        deleted_coord = random_rot(deleted_coord, circlefrac=0.1)
                        e_new = self.insertion(deleted_coord)

                    exp_value = -self.beta * (e_new - e)
                    if(exp_value > 0):
                        exp_value = 0
                    acc = min(1, np.exp(exp_value))

                    # Reject monte carlo move
                    if np.random.rand() > acc:
                        self.pos = pos_init
                        if self.ads_ei:
                            self.sfac = sfac_init
                        self.e_el_real = e_el_real_init
                        self.e_vdw = e_vdw_init
                    else:
                        e = e_new

            else:

                # Construct system and forcefield class for the MD engine
                from yaff import System, ForceField, XYZWriter, VerletScreenLog, MTKBarostat, \
                       NHCThermostat, TBCombination, VerletIntegrator, HDF5Writer, log
                log.set_level(0)

                n = np.append(self.data.numbers_MOF, np.tile(self.data.numbers_ads, self.Z_ads))

                ffa_MOF = self.data.system.ffatypes[self.data.system.ffatype_ids]
                ffa_ads = self.data.system_ads.ffatypes[self.data.system_ads.ffatype_ids]
                ffa = np.append(ffa_MOF, np.tile(ffa_ads, self.Z_ads))
                assert len(self.pos) == len(ffa)

                s = System(n, self.pos, ffatypes = ffa, rvecs=self.rvecs)
                s.detect_bonds()

                ff = ForceField.generate(s, self.ff_file,
                                            rcut=self.rcut,
                                            alpha_scale=self.alpha_scale,
                                            gcut_scale=self.gcut_scale,
                                            tailcorrections=True)

                ff_lammps = swap_noncovalent_lammps(ff, fn_system='system_%.8f.dat'%(self.P/bar),
                                        fn_table='table.dat',
                                        nrows=5000,
                                        kspace='pppm',
                                        kspace_accuracy=1e-7,
                                        scalings_ei = [1.0, 1.0, 1.0],
                                        move_central_cell=False,
                                        fn_log="none",
                                        overwrite_table=False, comm=comm)

                # Setup and NPT MD run
                if rank == 0:
                    vsl = VerletScreenLog(step=50)

                    if self.write_h5s:
                        if self.fixed_N:
                            hdf5_writer = HDF5Writer(h5.File('results/temp_%d.h5'%self.fixed_N, mode='w'), step=101)
                        else:
                            hdf5_writer = HDF5Writer(h5.File('results/temp_%.8f.h5'%(self.P/bar), mode='w'), step=101)

       	       	ensemble_hook = NHCThermostat(temp=self.T, timecon=100*femtosecond, chainlength=3)
                if self.barostat:
                    mtk = MTKBarostat(ff_lammps, temp=self.T, press=self.P, \
                        timecon=1000*femtosecond, vol_constraint = self.vol_constraint, anisotropic = True)
                    ensemble_hook = TBCombination(ensemble_hook, mtk)

                if self.meta:
                    cv = CVVolume(ff_lammps.system)
                    sigma = 1000*angstrom**3
                    K = 20*kjmol
                    step = 498

                # Run MD
                t = time()
                if self.write_h5s:
                    if rank == 0:
                        verlet = VerletIntegrator(ff_lammps, self.timestep, hooks=[ensemble_hook, vsl, hdf5_writer], temp0=self.T)
                    else:
                        verlet = VerletIntegrator(ff_lammps, self.timestep, hooks=[ensemble_hook], temp0=self.T)
                else:
                    if rank == 0:
                        hooks = [ensemble_hook, vsl]
                        if self.meta:
                            meta = MTDHook(ff_lammps, cv, sigma, K, start=step, step=step)
                            for q0 in q0s:
                                meta.hills.add_hill(q0, K)
                            hooks.append(meta)
                        verlet = VerletIntegrator(ff_lammps, self.timestep, hooks=hooks, temp0=self.T)
                    else:
                        hooks = [ensemble_hook]
                        if self.meta:
                            meta = MTDHook(ff_lammps, cv, sigma, K, start=step, step=step) 
                            for q0 in q0s:
                                meta.hills.add_hill(q0, K)
                            hooks.append(meta)
                        verlet = VerletIntegrator(ff_lammps, self.timestep, hooks=hooks, temp0=self.T)

                e0_tot = verlet._compute_ekin() + ff_lammps.compute()
                verlet.run(600)
                ef_tot = verlet._compute_ekin() + ff_lammps.compute()

                if not self.vol_constraint:
                    Vn = np.linalg.det(ff_lammps.system.cell.rvecs)
                    exp_value = -self.beta * (ef_tot - e0_tot + self.P * (Vn - self.V) - len(self.pos)/self.beta * np.log(Vn/self.V))
                else:
                    exp_value = -self.beta * (ef_tot - e0_tot)

                if(exp_value > 0):
                    exp_value = 0
                acc = min(1, np.exp(exp_value))

                # Accept monte carlo move
                if np.random.rand() < acc:

                    if self.write_h5s:
                        # Append MD data to previous data
                        self.append_h5()

                    # Rebuild data for MC
                    pos_total = ff_lammps.system.pos
                    self.pos = pos_total[:self.N_frame]
                    pos_molecules = pos_total[self.N_frame:]

                    self.rvecs = ff_lammps.system.cell.rvecs
                    self.rvecs_flat = self.rvecs.reshape(9)
                    self.V = np.linalg.det(self.rvecs)
                    if self.meta:
                        q0s.append(self.V)
                    if self.ads_ei:
                        self.sfac = Sfac(self.pos, self.N_frame, self.rvecs_flat, \
                                            self.charges, self.alpha, self.gcut)
                    self.e_el_real = 0
                    self.e_vdw = 0

                    if self.Z_ads > 0:
                        for p in np.split(pos_molecules, self.Z_ads):
                            e_new = self.insertion(p)
                            self.Z_ads -= 1
                        e = e_new
                    else:
                        e = 0
                else:

                    self.pos = pos_init
                    self.rvecs = rvecs_init
                    self.rvecs_flat = rvecs_flat_init
                    self.V = V_init

                if rank == 0:
                    log.set_level(log.medium)

            if(iteration % N_sample == 0 and iteration > 0):
                eprint = e
                if np.abs(eprint) < 1e-10:
                    eprint = 0
                if rank == 0:
                    print(' {:7.7}       {:7.7} {:7.7} {:7.7}    {:7.4}'.format(
                          str(iteration),str(self.Z_ads),str(eprint/kjmol),str(self.V/angstrom**3),time()-t_it)
                          )
                t_it = time()
                N_samples.append(self.Z_ads)
                E_samples.append(e)
                if self.Z_ads == self.fixed_N:
                    traj.append(self.pos)

                if rank == 0 and self.write_traj:

                    natom = self.N_frame + self.nads * self.Z_ads
                    rv = self.rvecs_flat/angstrom
                    ffa_MOF = self.data.system.ffatypes[self.data.system.ffatype_ids]
                    ffa_ads = self.data.system_ads.ffatypes[self.data.system_ads.ffatype_ids]
                    ffa = np.append(ffa_MOF, np.tile(ffa_ads, self.Z_ads))

                    ftraj.write('%d\n%f %f %f %f %f %f %f %f %f\n'%(natom, rv[0], rv[1], rv[2], rv[3], rv[4], rv[5], rv[6], rv[7], rv[8]))
                    for s, p in zip(ffa, self.pos/angstrom):
                        ftraj.write('%s %f %f %f\n'%(s, p[0], p[1], p[2]))


        if rank == 0:
            print('Average N: %.3f'%np.average(N_samples))
            if self.fixed_N:
                np.save('results/N_%d.npy'%self.fixed_N, np.array(N_samples))
                np.save('results/E_%d.npy'%self.fixed_N, np.array(E_samples))
            else:
                np.save('results/N_%.8f.npy'%(self.P/bar), np.array(N_samples))
                np.save('results/E_%.8f.npy'%(self.P/bar), np.array(E_samples))

            if self.fixed_N:

                from yaff import System
                n = np.append(self.data.numbers_MOF, np.tile(self.data.numbers_ads, self.Z_ads))
                s = System(n, self.pos, rvecs=self.rvecs)
                s.to_file('results/end_%d.xyz'%self.fixed_N)

                mol = Molecule.from_file('results/end_%d.xyz'%self.fixed_N)
                symbols = mol.symbols
                self.write_traj(traj, symbols)
                os.remove('results/end_%d.xyz'%self.fixed_N)

            if self.write_traj:
                ftraj.close()