def check_domain(): """ Check sensible subdomain strategy """ if len(boundaries) != Ns + 1 or len(bins) != Ns or len(rules) != Ns: error(this_file, "Mismatch between number of subdomains and number of boundaries/bins/rules. \nMake sure there are Ns + 1 boundaries, Ns bins and Ns rules in params.py!") for i in range(len(boundaries)-1): if boundaries[i+1] <= boundaries[i]: error(this_file, "Mu boundaries do not increase monotonically from left to right.") # These are center-of-bin values mu_s_cross = dom.get_local_mu_bins(dom.s_cross) # Want a bin centered on mu=0 mu_hi = mu_s_cross[dom.i_cross] mu_lo = mu_s_cross[dom.i_cross - 1] # If we've failed to get a bin centered on mu=0 if np.allclose(mu_hi,0.0) == False: # rtol=1e-5, atol=1e-8 print "Error:", this_file print "mu = 0 falls into a bin centered on mu = %1.8f, rather than 0." %mu_hi bound_hi = boundaries[dom.s_cross + 1] bound_lo = boundaries[dom.s_cross] rnd_bin_width = np.around(dom.subdom[dom.s_cross]['bin_width'],decimals=10) hbw = 0.5*rnd_bin_width bins_lo = dom.i_cross - dom.subdom[dom.s_cross]['l_bin_olap'] bins_hi = bins[dom.s_cross] - bins_lo prop_bound_hi = bins_hi * rnd_bin_width prop_bound_lo = bins_lo * rnd_bin_width * -1 print "Try %.10f --> %.10f, %.10f --> %.10f" \ %(bound_lo,prop_bound_lo+hbw,bound_hi,prop_bound_hi+hbw) print "Or %.10f --> %.10f, %.10f --> %.10f" \ %(bound_lo,prop_bound_lo-hbw,bound_hi,prop_bound_hi-hbw) print "Or change the number of bins and try again." exit(1) print "Subdomain strategy OK." return
############################## ## Set up fine-grained grid ## ############################## """ Sets up 'windows' centered on each regular bin, containing 'mini-bins'. This allows more precise measurement of local transition probabilities, while keeping relatively thick bins for everything else. """ # ------------------------- # # Iterate over subdomains # # ------------------------- # subdom = [] for s in range(Ns): # These are the centres of the windows mu_bins = dom.get_local_mu_bins(s) # Widths in terms of mu minibin_width = dom.subdom[s]['bin_width'] / float(minibins_per_bin) window_width = minibin_width * minibins_per_window # Dictionary of info for this subdomain, including a list of dictionaries # containing info about individual windows windows = { 'mbin_width': minibin_width, 'win_width': window_width, 'win_info': [] } # ---------------------------------- # # Iterate over regular bin indices #
def run(atoms_alpha, atoms_beta, disp, binned_data, dyn_data, p, s, F=1, relax=False): # ------------------------------------------------- # # Set simulation parameters and useful quantities # # ------------------------------------------------- # lendata = len(binned_data['w']) # length of weights kTlogF = kT * np.log(F) # Convert frequency parameters from sweeps to steps if relax == True: Nsteps = sweeps_relax * Natoms else: Nsteps = alg.Nsteps save_step = sweeps_save * Natoms series_step = int(sweeps_series * Natoms) recalc_step = sweeps_recalc * Natoms refresh_step = sweeps_refresh * Natoms # Set boundaries for this simulation if TRAP == True: # restrict to subdomain 's'; local bin indices global_mu_min = dom.subdom[s]['min'] global_mu_max = dom.subdom[s]['max'] get_index = dom.get_local_index mu_bins = dom.get_local_mu_bins(s) else: # can access entire domain; global bin indices global_mu_min = boundaries[0] global_mu_max = boundaries[-1] get_index = dom.get_global_index mu_bins = dom.get_global_mu_bins() # Set weights function if use_interpolated_weights == True: get_weight = interpolated_weight # Need to add bins to the edges of mu and weights for extrapolation mu_bins = np.concatenate((mu_bins, [0, 0])) extrapolate(mu_bins) binned_data['w'] = np.concatenate((binned_data['w'], [0, 0])) extrapolate(binned_data['w']) # Need to do the same for cuts since it uses mu_bins if track_dynamics == True: dyn_data['cu'] = np.concatenate((dyn_data['cu'], [0, 0])) else: get_weight = discrete_weight # ------------------------ # # Initialise usual stuff # # ------------------------ # # Get initial lattice energies and difference between them old_E_alpha, old_E_beta = lattice_energies(atoms_alpha, atoms_beta) old_mu = old_E_alpha - old_E_beta new_mu = old_mu # For a global simulation, the initial subdomain (corresponding to the # input disp vector) needs to be computed if TRAP != True: s = dom.get_subdomain(old_mu) # Initial index and weight old_index = get_index(old_mu, s) old_weight = get_weight(old_mu, old_index, mu_bins, binned_data['w']) # Active lattice if s < dom.s_cross: # start with lattice alpha(1) active ACT = 1 elif s > dom.s_cross: # start with lattice beta(-1) active ACT = -1 else: # randomise ACT = np.random.choice([-1, 1]) # Flag for histogram flatness FLAT = 1 # start with 1 < f if wang-landau, otherwise f = 1 = FLAT # Initialise counters step = 1 moves = 0 retakes = 0 switches = 0 # ---------------------------- # # Initialise 'dynamics' info # # ---------------------------- # if track_dynamics == True: cuts = {'counter': 0, 'old_mu': old_mu, 'new_mu': old_mu} if TRAP == True: rt_min = 0 rt_max = lendata - 1 else: rt_min = np.argmax(binned_data['w'][mu_bins < 0]) rt_max = np.argmax(binned_data['w'][mu_bins > 0]) + len( mu_bins[mu_bins < 0]) rtrips = { 'minmax': (0, rt_min, rt_max), 'counts': 0, 'flag': int(dyn_data['rt'][4]) } # Mini transition matrix only works for TRAP == True (due to indexing) if TRAP == True: minimat = { 'counter': 0, 'root_index': old_index, 'old_index': dyn.get_minibin_index(old_mu, s, old_index), 'Pprod': 1 } # --------------------------- # # Open file for data series # # --------------------------- # if track_series == True: series_file = alg.file_names('output', s, p)['s'] series = open(series_file, 'a') # ------------------------------------------------------------ # # Main loop over individual particle moves + switch attempts # # ------------------------------------------------------------ # while FLAT < F or step <= Nsteps: # Flat condition only relevant for Wang Landau # Refresh weights with eigenvector of transition matrix (TM only) if step % refresh_step == 0: alg.refresh_func(binned_data) # Write values from this step to the series file. Note high precision needed for mu if track_series == True: if step % series_step == 0: E_active = (0, old_E_alpha, old_E_beta)[ACT] series.write("%d %.10f %f %f \n" \ %(s, old_mu, old_weight, E_active) ) # --------------------------------------------------- # # Move a single particle and compute energy changes # # --------------------------------------------------- # # Pick a random particle ipart = np.random.randint(0, Natoms) # Calculate old local energies for the two lattices before trial move old_local_energies = local_energy(atoms_alpha, atoms_beta, ipart) # Pick a random displacement and update lattice positions dr = (np.random.random(3) * 2.0 - 1.0) * dr_max dr_alpha = np.dot(dr, atoms_alpha.get_cell()) dr_beta = np.dot(dr, atoms_beta.get_cell()) atoms_alpha.positions[ipart, :] += dr_alpha atoms_beta.positions[ipart, :] += dr_beta # Calculate new local energies after trial move new_local_energies = local_energy(atoms_alpha, atoms_beta, ipart) # Calculate energy difference delta_local_energies = new_local_energies - old_local_energies # New energy difference between lattices if step % recalc_step == 1: # full calculation new_E_alpha, new_E_beta = lattice_energies(atoms_alpha, atoms_beta) # Sanity check - don't want to be below ideal lattice energy! if new_E_alpha < E_ideal or new_E_beta < E_ideal: error(this_file, "Oh no! Energy is less than that of an Ideal lattice.") else: # update with local energy difference new_E_alpha = old_E_alpha + delta_local_energies[1] new_E_beta = old_E_beta + delta_local_energies[-1] new_mu = new_E_alpha - new_E_beta # ---------------- # # Switch attempt # # ---------------- # # Attempt a switch here 50% of the time - before any move evaluation coin = coinflip() ACT, switches = ((ACT, switches), attempt_switch(new_mu, ACT, switches))[coin] # --------------------------------------- # # Check if boundaries have been crossed # # --------------------------------------- # # 'Global' boundaries are those that the simulation may not cross if new_mu < global_mu_min or new_mu > global_mu_max: # Update the bin from which the move was attempted old_weight, FLAT = alg.update_func( step, binned_data, delta_local_energies[ACT], old_index, old_index, old_index, # only update edge bin old_mu, mu_bins, old_weight, get_weight, lendata, kTlogF, s) if track_dynamics == True: # Update data on simulation dynamics / mobility dyn.update_func(dyn_data, mu_bins, old_mu, old_index, s, cuts, rtrips, False, abs(new_mu - old_mu), abs(delta_local_energies[ACT])) if TRAP == True: dyn.update_minimat(dyn_data, mu_bins, old_mu, old_index, s, minimat, True, 0) # Undo lattice positions update atoms_alpha.positions[ipart, :] -= dr_alpha atoms_beta.positions[ipart, :] -= dr_beta retakes += 1 step += 1 # Go back to the start of the while loop continue # Check if subdomain boundary crossed old_s = s if new_mu < dom.subdom[s]['min']: s -= 1 elif new_mu > dom.subdom[s]['max']: s += 1 # ---------------------- # # Evaluate move result # # ---------------------- # # Get index for arrays using mu new_index = get_index(new_mu, s) # Difference in weights augments delta_local_energy new_weight = get_weight(new_mu, new_index, mu_bins, binned_data['w']) augment = new_weight - old_weight # Accept/reject trial move with weighted energy difference expnt = -B * (delta_local_energies[ACT] + augment) accepted = accept_move(expnt) """ #Difference between bin-averaged weight and interpolated intra_bin_weight = old_weight - weights[old_index] #sampling_adjust = np.exp( B*intra_bin_weight ) # doesn't work: centre of bins?""" old_index_copy = old_index # to update Cmat and extra info after move evaluation dmu = abs(new_mu - old_mu) # to update dynamics # Update things according to whether move accepted if accepted == True: disp[ipart, :] += dr old_E_alpha = new_E_alpha old_E_beta = new_E_beta old_index = new_index old_weight = new_weight old_mu = new_mu moves += 1 cuts_new_mu = new_mu else: atoms_alpha.positions[ipart, :] -= dr_alpha atoms_beta.positions[ipart, :] -= dr_beta s = old_s # Account for possible subdomain change cuts_new_mu = old_mu # ------------------------ # # Update bin information # # ------------------------ # old_weight, FLAT = alg.update_func(step, binned_data, delta_local_energies[ACT], new_index, old_index, old_index_copy, old_mu, mu_bins, old_weight, get_weight, lendata, kTlogF, s) # ------------------------------------ # # Update data on simulation dynamics # # ------------------------------------ # if track_dynamics == True: # Update data on simulation dynamics / mobility cuts['new_mu'] = cuts_new_mu dyn.update_func(dyn_data, mu_bins, old_mu, old_index, s, cuts, rtrips, accepted, dmu, abs(delta_local_energies[ACT])) if TRAP == True: dyn.update_minimat(dyn_data, mu_bins, old_mu, old_index, s, minimat, accepted, expnt) # ---------------- # # Switch attempt # # ---------------- # # 50/50 chance of attempting a switch after the move ACT, switches = (attempt_switch(old_mu, ACT, switches), (ACT, switches))[coin] # ------------------- # # Save periodically # # ------------------- # if step % save_step == 0: alg.save_func(binned_data, mu_bins, step, s, p) # Take this opportunity to reset centre of mass atoms_alpha.positions = atoms_alpha.positions - atoms_alpha.get_center_of_mass( ) atoms_beta.positions = atoms_beta.positions - atoms_beta.get_center_of_mass( ) step += 1 # End main loop print "- Steps: ", step print "- Moves: ", moves print "- Retakes: ", retakes print "- Switches: ", switches # --------------------------------------------- # # Finalise things before returning to main.py # # --------------------------------------------- # # Close series file if track_series == True: series.close() if track_dynamics == True: # Round trip output file dyn_data['rt'][0] += step dyn_data['rt'][1] += switches dyn_data['rt'][2] += rtrips['counts'] # half round trips rt_rate = float(0.5 * dyn_data['rt'][2] * Natoms) / dyn_data['rt'][0] # rtrips per sweep dyn_data['rt'][3] = 1.0 / rt_rate # sweeps per rtrip dyn_data['rt'][4] = rtrips['flag'] # to be picked up by next iteration # Compute eigenvector and refresh weights alg.refresh_func(binned_data) # If interpolated weights used, remove those extrapolated bins / 'ghost points' if len(binned_data['w']) != len(binned_data['h']): binned_data['w'] = binned_data['w'][:-2] if track_dynamics == True: dyn_data['cu'] = dyn_data['cu'][:-2] # Only care about difference in weights, set minimum to zero binned_data['w'] -= np.min(binned_data['w']) return step
input_files = alg.file_names('output', s, p) extra_input_files = dyn.file_names('output', s, p) # Load input files this_hist = np.loadtxt(input_files['h']) this_cuts = np.loadtxt(extra_input_files['cu']) # Add to combined hist = hist + this_hist cuts = cuts + this_cuts if TRAP == True: # Scale by bin width cuts = cuts * dom.subdom[s]['bin_width'] # Load mu subdomain mu_bins = dom.get_local_mu_bins(s) * B / float(Natoms) else: # Scale global data by bin widths for each subdomain bin_low = 0 bin_high = 0 for ss in range(Ns): bin_high += bins[ss] cuts[bin_low:bin_high] = cuts[bin_low:bin_high] * dom.subdom[ss]['bin_width'] bin_low = bin_high # Formula given in Tian et a. (2014), though coefficient is arbitrary in this case cuts = 0.5*cuts diffusivity = np.pi / dyn.cuts_step * (cuts**2) / (hist**2) # The edge bins are not well estimated with the cuts method implemented diffusivity[0] = diffusivity[1] + 0.25*(diffusivity[1] - diffusivity[4])
error(this_file, "No file name found in list of argument variables.") # Load data input_data = np.loadtxt(input_file[1]) # index 0 is the plot script itself # Extract subdomain index if provided as argument variable # This will be the first int given in the argv list s = [v for v in argv if v.isdigit()] # If no subdomain given as argument variable, plot global data if len(s) == 0: mu_bins = dom.get_global_mu_bins() * B / float(Natoms) s = 'all' else: s = s[0] mu_bins = dom.get_local_mu_bins(int(s)) * B / float(Natoms) # Check data and mu are the same size if len(input_data) != len(mu_bins): error(this_file, "Length of input data does not match number of mu bins in subdomain: "+s) # Plot parameters plt.rc('text',usetex=True) font = {'family' : 'serif', 'size' : 14} plt.rc('font', **font) plt.rcParams['axes.linewidth'] = 2 # Create plot fig, ax = plt.subplots()