Example #1
0
crack_slab.info['OrigWidth'] = orig_width
crack_slab.info['OrigHeight'] = orig_height
crack_slab.info['CrackDirection'] = params.crack_direction
crack_slab.info['CleavagePlane'] = params.cleavage_plane
crack_slab.info['CrackFront'] = params.crack_front
crack_slab.info['cell_origin'] = -np.diag(crack_slab.cell) / 2.0

crack_slab.set_array('fixed_mask', fixed_mask)
ase.io.write('slab.xyz', crack_slab, format='extxyz')

# ****** Apply initial strain ramp *****

strain = G_to_strain(params.initial_G, E, nu, orig_height)

crack_slab.positions[:, 1] += thin_strip_displacement_y(
    crack_slab.positions[:, 0], crack_slab.positions[:, 1], strain,
    left + params.crack_seed_length,
    left + params.crack_seed_length + params.strain_ramp_length)

print('Applied initial load: strain=%.4f, G=%.2f J/m^2' %
      (strain, params.initial_G / (units.J / units.m**2)))

# ***** Relaxation of crack slab  *****

# optionally, relax the slab, keeping top and bottom rows fixed
if hasattr(params, 'relax_slab') and params.relax_slab:
    print('Relaxing slab...')
    crack_slab.set_calculator(params.calc)
    opt = FIRE(crack_slab)
    opt.run(fmax=params.relax_fmax)

# Find initial position of crack tip
Example #2
0
orig_width = right - left
orig_height = top - bottom

print(('Made slab with %d atoms, original width and height: %.1f x %.1f A^2' %
       (len(crack_slab), orig_width, orig_height)))


# ****** Apply initial strain ramp *****

strain = params.initial_strain

displacement = thin_strip_displacement_y(
    crack_slab.positions[:, 0],
    crack_slab.positions[:, 1],
    strain,
    left + params.crack_seed_length,
    left + params.crack_seed_length +
    params.strain_ramp_length)

displacement[swap] = -displacement[swap]
crack_slab.positions[:, 1] += displacement
# cleanup_crack_tip(crack_slab)

# fix atoms in the top and bottom rows
if 'groups' in crack_slab.arrays:
    fixed_mask = crack_slab.get_array('groups') == 0
else:
    fixed_mask = ((abs(crack_slab.positions[:, 1] - top) < 1.0) |
                  (abs(crack_slab.positions[:, 1] - bottom) < 1.0))
print('Fixed atoms: %d\n' % fixed_mask.sum())
Example #3
0
def ribs(params, voidcount, voidrad, frame_count=1000):

    calc = IdealBrittleSolid(rc=params.rc,
                             k=params.k,
                             a=params.a,
                             beta=params.beta)

    x_dimer = np.linspace(params.a - (params.rc - params.a),
                          params.a + 1.1 * (params.rc - params.a), 51)
    dimers = [
        Atoms('Si2', [(0, 0, 0), (x, 0, 0)], cell=[10., 10., 10.], pbc=True)
        for x in x_dimer
    ]
    calc.set_reference_crystal(dimers[0])
    e_dimer = []
    f_dimer = []
    f_num = []
    for d in dimers:
        d.set_calculator(calc)
        e_dimer.append(d.get_potential_energy())
        f_dimer.append(d.get_forces())
        f_num.append(calc.calculate_numerical_forces(d))
    e_dimer = np.array(e_dimer)
    f_dimer = np.array(f_dimer)
    f_num = np.array(f_num)
    assert abs(f_dimer - f_num).max() < 0.1

    #! crystal is created here, the length and height can be modified here as well
    #! edit 3N changed to different values to test
    crystal = triangular_lattice_slab(params.a, params.lm * params.N, params.N)
    calc.set_reference_crystal(crystal)
    crystal.set_calculator(calc)

    e0 = crystal.get_potential_energy()
    l = crystal.cell[0, 0]
    h = crystal.cell[1, 1]
    print('l=', l, 'h=', h)

    # compute surface (Griffith) energy
    b = crystal.copy()
    b.set_calculator(calc)
    shift = calc.parameters['rc'] * 2
    y = crystal.positions[:, 1]
    b.positions[y > h / 2, 1] += shift
    b.cell[1, 1] += shift
    e1 = b.get_potential_energy()
    E_G = (e1 - e0) / l
    print('Griffith energy', E_G)

    # compute Griffith strain
    eps = 0.0  # initial strain is zero
    eps_max = 2 / np.sqrt(3) * (params.rc - params.a) * np.sqrt(
        params.N - 1) / h  # Griffith strain assuming harmonic energy
    deps = eps_max / 100.  # strain increment
    e_over_l = 0.0  # initial energy per unit length is zero
    energy = []
    strain = []
    while e_over_l < E_G:
        c = crystal.copy()
        c.set_calculator(calc)
        c.positions[:, 1] *= (1.0 + eps)
        c.cell[1, 1] *= (1.0 + eps)
        e_over_l = c.get_potential_energy() / l
        energy.append(e_over_l)
        strain.append(eps)
        eps += deps

    energy = np.array(energy)
    eps_of_e = interp1d(energy, strain, kind='linear')
    eps_G = eps_of_e(E_G)

    print('Griffith strain', eps_G)

    c = crystal.copy()
    c.info['E_G'] = E_G
    c.info['eps_G'] = eps_G

    # open up the cell along x and y by introducing some vaccum
    orig_cell_width = c.cell[0, 0]
    orig_cell_height = c.cell[1, 1]
    c.center(params.vacuum, axis=0)
    c.center(params.vacuum, axis=1)

    # centre the slab on the origin
    c.positions[:, 0] -= c.positions[:, 0].mean()
    c.positions[:, 1] -= c.positions[:, 1].mean()

    c.info['cell_origin'] = [-c.cell[0, 0] / 2, -c.cell[1, 1] / 2, 0.0]
    ase.io.write('crack_1.xyz', c, format='extxyz')

    width = (c.positions[:, 0].max() - c.positions[:, 0].min())
    height = (c.positions[:, 1].max() - c.positions[:, 1].min())

    c.info['OrigHeight'] = height

    print((
        'Made slab with %d atoms, original width and height: %.1f x %.1f A^2' %
        (len(c), width, height)))

    top = c.positions[:, 1].max()
    bottom = c.positions[:, 1].min()
    left = c.positions[:, 0].min()
    right = c.positions[:, 0].max()

    crack_seed_length = 0.2 * width
    strain_ramp_length = 8.0 * params.a  # make this bigger until crack looks nicer
    delta_strain = params.strain_rate * params.dt

    # fix top and bottom rows, and setup Stokes damping mask
    # initial use constant strain
    set_constraints(c, params.a)

    # apply initial displacment field
    c.positions[:, 1] += thin_strip_displacement_y(
        c.positions[:, 0], c.positions[:, 1], params.delta * eps_G,
        left + crack_seed_length,
        left + crack_seed_length + strain_ramp_length)

    print('Applied initial load: delta=%.2f strain=%.4f' %
          (params.delta, params.delta * eps_G))

    ase.io.write('crack_2.xyz', c, format='extxyz')

    c.set_calculator(calc)

    cl, cs, cr = calc.get_wave_speeds(c)

    print("rayleigh speed = %f" % cr)

    # relax initial structure
    # opt = FIRE(c)
    # opt.run(fmax=1e-3)

    ase.io.write('crack_3.xyz', c, format='extxyz')

    #length and height of the slab is defined here
    L = params.N * params.lm
    H = params.N * 2
    #Atomic Void Simulations
    #😵------------------------------------------------------------------------------
    #the following lines of code were written to convert 1D positions into a 2D array
    #so as to make manipulation of slab easier
    if True:
        #void parameters are defined here.
        #around a max of [0.3--1.7] recommended
        y_offset = 0.1
        #y offset is a fraction of distance from the end of the slab
        x_offset = 0.4
        rad = voidrad

        #this reference code is for 160x40 slab
        #in steps of 40 i.e. the height, create a list upto the length of slab
        #row0 = range(0,6400,40)
        row0 = range(0, len(c), H)
        #the 2D array will be held in slab
        slab = []
        for col in range(H):
            row = []
            for r in row0:
                i = col + r
                row.append(c.positions[i])
            slab.append(row)

        slab = np.array(slab)
        #all items in the array reversed, needed because the salb is built from bottom left up
        #in other words, reflected in x axis
        slab = slab[::-1]
        # print(slab[0])

        # slab[modifiers.mask(h=H,w=L, center=[int(L*x_offset),int((H - 1)*y_offset)], radius=rad)] = 0

        #multiple voids if need be
        if True:
            for i in range(voidcount):
                y_offset = rand.uniform(0.1, 0.9)
                x_offset = rand.uniform(0.3, 0.95)
                slab[modifiers.mask(
                    h=H,
                    w=L,
                    center=[int(L * x_offset),
                            int((H - 1) * y_offset)],
                    radius=rad)] = 0
        #reversed the slab back again here
        slab = slab[::-1]
        # # this is a useful text-array representation of the slab, for debugging purposes
        # mtext = open('masktest.txt','w')
        # for row in slab:
        #        mtext.write(str(row).replace("\n",",")+"\n")
        # mtext.close

        slab_1d = []
        for col in range(L):
            for row in range(H):
                slab_1d.append(slab[row, col])
        slab_1d = np.array(slab_1d)

        todel = []
        for i in range(len(c)):
            if slab_1d[i][2] == 0:
                todel.append(i)
        print(todel)
        del c[todel]
        # return
    #End of Void Simulation
    #-------------------------------------------------------------------------------

    #Grain Boundary Simulations
    #-------------------------------------------------------------------------------
    if False:
        hi = 2

    #-------------------------------------------------------------------------------

    #! replaced velcityVerlet with Lagevin to add temperature parameter
    if params.v_verlet:
        dyn = VelocityVerlet(c, params.dt * units.fs, logfile=None)
        # set_initial_velocities(dyn.atoms)
    else:
        print("Using NVT!")
        # mbd(c, 20 * units.kB, force_temp = True)
        # dyn = Langevin(c,params.dt*units.fs,params.T*units.kB, 5)
        dyn = NVTBerendsen(c,
                           params.dt * units.fs,
                           params.T,
                           taut=0.5 * 1000 * units.fs)

    #dyn.atoms.rattle(1e-3) # non-deterministic simulations - adjust to suit

    #!simulation outputs numbered, avoids deleting exisiting results
    iterFile = open("simIteration.txt", 'r+')
    iteration = int(iterFile.readlines()[0]) + 1
    if params.overwrite_output:
        iteration -= 1
    iterFile.seek(0, 0)
    iterFile.write(str(iteration))
    iterFile.close()

    dir = "./.simout/void/sim_" + str(iteration) + "_" + str(
        params.keep_test).lower() + "_" + params.desc + "/"
    cf.createFolder(dir)

    #!Saving parameter values for each iteration of the simulation
    logFile = open(dir + "params_log.txt", 'w')
    for p, value in params.compose_params().iteritems():
        logFile.write(p + " ==> " + str(value) + "\n")
    logFile.close
    crack_pos = []
    if params.keep_test:
        traj = NetCDFTrajectory(dir + 'traj' + str(iteration) + '.nc', 'w', c)
        dyn.attach(traj.write, 10, dyn.atoms, arrays=['stokes', 'momenta'])

    # #! isolating crack tip_x for saving
    # crack_tip_file2 = open(dir+'tip_x.txt','w')
    # crack_tip_file2.close()
    tip_x_file = open(dir + 'tip_x.txt', 'a')
    console_output = open(dir + 'console_output.txt', 'a')
    coord_file = open(dir + 'coordinates.csv', 'a')

    coordinates = []
    distances = []

    dyn.attach(find_crack_tip,
               10,
               dyn.atoms,
               tipxfile=tip_x_file,
               cout=console_output,
               coord=coordinates,
               d=distances,
               dt=params.dt * 10,
               store=True,
               results=crack_pos)

    # run for 2000 time steps to reach steady state at initial load
    # for i in range(10):
    #     dyn.run(250)
    #     if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum):
    #         set_constraints(dyn.atoms, params.a)

    # start decreasing strain
    #set_constraints(dyn.atoms, params.a, delta_strain=delta_strain)

    # strain_atoms = ConstantStrainRate(dyn.atoms.info['OrigHeight'],
    #                                   delta_strain)
    # dyn.attach(strain_atoms.apply_strain, 1, dyn. atoms   )

    # for i in range(50):
    #     dyn.run(100)
    #     if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum):
    #         set_constraints(dyn.atoms, params.a)

    # #cleardel dyn.observers[-1] # stop increasing the strain

    # for i in range(1000):
    #     dyn.run(100)
    #     if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum):
    #         set_constraints(dyn.atoms, params.a)

    dyn.run(int(1 * frame_count) * 10 + 10)

    # print("\n\n\n\n\n -----Adding Temperature------\n\n\n\n\n")
    # # mbd(c, 2*params.T * units.kB, force_temp = True)
    # dyn.set_temperature(params.T*units.kB)
    # dyn.run(int(0.5*frame_count)*10+10)
    for c in coordinates:
        coord_file.write(str(c[0]) + ',' + str(c[1]) + '\n')
    coord_file.close()

    if params.keep_test:
        traj.close()
    tip_x_file.close()
    console_output.close()
left = c.positions[:, 0].min()
right = c.positions[:, 0].max()

crack_seed_length = 0.3*width
strain_ramp_length = 5.0*params.a
delta_strain = params.strain_rate*params.dt

# fix top and bottom rows, and setup Stokes damping mask
# initial use constant strain
set_constraints(c, params.a, delta_strain=None)

# apply initial displacment field
c.positions[:, 1] += thin_strip_displacement_y(
                                 c.positions[:, 0],
                                 c.positions[:, 1],
                                 params.delta*eps_G,
                                 left + crack_seed_length,
                                 left + crack_seed_length +
                                        strain_ramp_length)

print('Applied initial load: delta=%.2f strain=%.4f' %
      (params.delta, params.delta*eps_G))

ase.io.write('crack_2.xyz', c, format='extxyz')

c.set_calculator(calc)

# relax initial structure
#opt = FIRE(c)
#opt.run(fmax=1e-3)
Example #5
0
left = c.positions[:, 0].min()
right = c.positions[:, 0].max()

crack_seed_length = 0.3*width
strain_ramp_length = 5.0*params.a
delta_strain = params.strain_rate*params.dt

# fix top and bottom rows, and setup Stokes damping mask
# initial use constant strain
set_constraints(c, params.a)

# apply initial displacment field
c.positions[:, 1] += thin_strip_displacement_y(
                                 c.positions[:, 0],
                                 c.positions[:, 1],
                                 params.delta*eps_G,
                                 left + crack_seed_length,
                                 left + crack_seed_length +
                                        strain_ramp_length)

print('Applied initial load: delta=%.2f strain=%.4f' %
      (params.delta, params.delta*eps_G))

ase.io.write('crack_2.xyz', c, format='extxyz')

c.set_calculator(calc)

# relax initial structure
#opt = FIRE(c)
#opt.run(fmax=1e-3)
traj_file = open('traj-m-6r.xyz', 'w')
minimiser = FIRE(atoms, restart='hess.traj')

def trajectory_write():
    ase.io.extxyz.write_xyz(traj_file, atoms)

# swap is an array of atom indices that have to be sent in opposite direction wrt
# what thin_strip_displacement_y would do.
swap = np.loadtxt('swap_topbottom_atoms.csv')
for i in range(1000):

    minimiser.run(fmax=params.relax_fmax)
    trajectory_write()
    # Find initial position of crack tip
    #crack_pos = find_tip_stress_field(crack_slab, calc=params.calc)
    #print 'Found crack tip at position %s' % crack_pos
    
    atoms.info['strain'] = get_strain(atoms)
    strain = atoms.info['strain']
    print("Strain: %f" % strain)
    # update atomic positions
    displacement = thin_strip_displacement_y(
        atoms.positions[:, 0],
        atoms.positions[:, 1],
        params.strain_rate * params.timestep,
        left + params.crack_seed_length,
        left + params.crack_seed_length +
        params.strain_ramp_length)
    displacement[swap] = -displacement[swap]
    atoms.positions[:, 1] += displacement