def remove_simulationsWithoutOutncFile(input_files): ''' Analysis can only be performed if *out.nc file or *out.h5 file is present. Returns ------- input_files : list of PosixPaths List of input_files of which the existence of *out.nc or *out.h5 files is checked. ''' # Select the cases without an *out.nc file printv("Checking existence of *out.nc file corresponding inputs.") count = 0 not_valid = [] for input_file in input_files: if os.path.isfile(input_file.with_suffix('.out.nc')): count = count + 1 elif os.path.isfile(input_file.with_suffix('.out.h5')): count = count + 1 else: printv(" File " + input_file.name + " removed from input list.") not_valid.append(input_file) printv( " Number of found input files with corresponding output *.nc = " + str(count)) # Remove the selected cases for input_file in not_valid: i = input_files.index(input_file) input_files.pop(i) return input_files
def read_vmecgeo(input_file): ''' Read the "*.vmec_geo" file and return the a_ref and B_ref. ''' # Find the omega file corresponding to the input file vmec_file = input_file.parent / "vmec_geo" geometry_file = input_file.with_suffix(".geometry") # Read the *vmec_geo file and get the first two lines (header + values) if os.path.isfile(vmec_file): geo_data = read_vmecgeoFromVmecgeoFile(vmec_file) # Read the *.geometry file and get the first two lines (header + values) elif os.path.isfile(geometry_file): geo_data = read_vmecgeoFromGeometryFile(geometry_file) # Data is saved to the reduced "wout*.h5" file elif get_filesInFolder(input_file.parent, start="wout", end=".h5"): geo_data = read_vmecgeoFromH5File(input_file) # ERROR IF BOTH FILES DON'T EXIST else: printv("No '*.vmec_geo' or '*.geometry' or 'wout*.h5' file found."); return {} # Return the data return geo_data
def print_research(self): # Print some information printv("") printv("##############################") printv(" RESEARCH ") printv("##############################") for experiment in self.experiments: printv("") printv(" Experiment " + str(self.experiments.index(experiment) + 1) + ": " + experiment.id) printv(" --------------------------------") if self.number_variedVariables == -1: printv( " Each simulation is assumed to be its own experiment at the following radial position: " ) if self.number_variedVariables == 1: printv( " There is 1 varied parameter with the following values: " ) if self.number_variedVariables > 1: printv(" There are " + str(self.number_variedVariables) + " varied parameters with the following values: ") for varied_value in experiment.variedValues: printv(" " + varied_value) printv(" The simulations associated to this experiment are: ") for simulation in experiment.simulations: printv(" " + '"' + simulation.id) printv("") # Dont collapse header on the next line if True: return
def create_experiments(\ # To create the simulations we need their location and whether to ignore the resolution folders=None, input_files=None, ignore_resolution=True, \ # To group them by experiment we need to know how many variables differ between the simulations # Or which variable is unique for each experiment number_variedVariables=1, experiment_knob="vmec_parameters", experiment_key="vmec_filename",\ # Give some extra rules folderIsExperiment = False): ''' Divide the simulations in experiments, based on the number of varied values or based on a parameter that differs for each experiment. For example, when we scan multiple radial positions of multiple shots, then each shot is defined by its unique magnetic field file. If they are run for the same magnetic field, just make sure they have a different symbolic link to differ between the experiments. ''' # First create the simulations simulations = create_simulations(folders, input_files, ignore_resolution, number_variedVariables, folderIsExperiment) # Create an empty list to hold the experiment objects experiments = [] # Iterate over the simulations objects for simulation in simulations: # Assume we have a new experiment newExperiment = True # Go through all the current experiments and see if the simulation belongs to one of these for experiment in experiments: # If the simulations are in a different folder its a new experiment. if not (folderIsExperiment == True and simulation.parent != experiment.simulations[0].parent): # Look at the difference in the input parameters of the experiment and the simulation dict_difference = get_differenceInDictionaries( experiment.inputParameters, simulation.inputParameters) # Sometimes we rerun the exact same simulation with different modes: we have the same experiment # However if we want this simulation to be a new simulation, force this with number_variedValues=-1 # If number_variedVariables==-2 then we want to sort by "1 folder = 1 experiment" # If number_variedVariables==-1 then we want to sort by "1 folder = 1 simulation" if len(list(dict_difference.keys())) == 0: if number_variedVariables == -2: newExperiment = True if number_variedVariables != -2: newExperiment = False experiment.simulations.append(simulation) # The simulation belongs to the current experiment if # inputParameters[experiment_knob][experiment_key] is the same. # For example if the radial position is varied then different experiments are characterized # by having a different vmec_filename in the knob "vmec_parameters". # Therefore if inputParameters[experiment_knob][experiment_key] # Is the same in the simulation and experiment, add the simulation to this experiment. # This rule is overriden if less than or equal to <number_variedVariables> are varied. elif experiment_knob in list(dict_difference.keys()): if experiment_key not in dict_difference[experiment_knob]: newExperiment = False experiment.simulations.append(simulation) varied_variable = experiment_knob + ": " + dict_difference[ experiment_knob][0] if varied_variable not in experiment.variedVariables: experiment.variedVariables.append(varied_variable) # The simulation is part of the experiment if up to <number_variedVariables> input variables are different elif len(list( dict_difference.keys())) <= number_variedVariables: varied_values = 0 for knob in list(dict_difference.keys()): for parameter in dict_difference[knob]: varied_values += 1 if varied_values <= number_variedVariables: newExperiment = False experiment.simulations.append(simulation) for knob in list(dict_difference.keys()): for parameter in dict_difference[knob]: varied_variable = knob + ": " + parameter if varied_variable not in experiment.variedVariables: experiment.variedVariables.append( varied_variable) # If more than <number_variedVariables> variables are different we have a new experiment # If inputParameters[experiment_knob][experiment_key] is different we have a new experiment # If the experiments list is empty we have a new experiment # In this case create an <Experiment> object and add it to the list if newExperiment == True: experiments.append(Experiment(simulation)) # For the variables that are varied, get their exact values for the labels in the graph # For example if experiment.variedVariables = ["vmec_parameter: torflux", "knobs: delt"] # then experiment.variedValues = ["rho=0.5; delt=0.1", "rho=0.5; delt=0.01"; "rho=0.6; delt=0.1"] for experiment in experiments: for simulation in experiment.simulations: varied_values = "" # This will be used as the label in the figure for variable in experiment.variedVariables: knob = variable.split(":")[0] variable = variable.split(": ")[-1] if knob != experiment_knob and variable != experiment_key: value = simulation.inputParameters[knob][variable] variable = variable.replace("_", " ") if variable == "torflux": value = str(round(np.sqrt(float(value)), 2)) if variable == "torflux": variable = "$\\rho$" if variable == "rho": variable = "$\\rho$" if variable == "delt": variable = "$\\Delta t$" if knob == 'species_parameters_1': if variable == "tprim": variable = "$a/L_{Ti}$" if variable == "fprim": variable = "$a/L_{ne}$" if knob == 'species_parameters_2': if variable == "tprim": variable = "$a/L_{Te}$" if variable == "fprim": variable = "$a/L_{ni}$" varied_values = varied_values + variable + "$\,=\,$" + str( value) + "; " # If a/Lne = a/Lni replace it by a/Ln if "$a/L_{ni}$" in varied_values and "$a/L_{ne}$" in varied_values: _elements = varied_values.split("; ") _elements = [e for e in _elements if e != ''] _ionDensityGrad = float([ e for e in _elements if "$a/L_{ni}$" in e ][0].split("$\,=\,$")[-1]) _others = [ e for e in _elements if ("$a/L_{ni}$" not in e) and ("$a/L_{ne}$" not in e) ] if len(_others) != 0: varied_values = "; ".join( _others) + "; " + "$a/L_{n}$" + "$\,=\,$" + str( _ionDensityGrad) + "; " if len(_others) == 0: varied_values = "$a/L_{n}$" + "$\,=\,$" + str( _ionDensityGrad) + "; " # If nothing was different because we only have one experiment, use the radial position as the label if varied_values == "": varied_values = "$\\rho$" + "$\,=\,$" + str( experiment.inputParameters['vmec_parameters'] ['rho']) + "; " # The time step in the input_file isn't the actual time step if "$\\Delta t$" in varied_values and False: netcdf_data = read_netcdf(simulation.input_files[0]) try: delt = (netcdf_data['vec_time'][5] - netcdf_data['vec_time'][4] ) / simulation.inputParameters[ 'stella_diagnostics_knobs']['nwrite'] except: delt = (netcdf_data['vec_time'][5] - netcdf_data['vec_time'][4] ) / simulation.inputs[simulation.input_files[0]][ 'stella_diagnostics_knobs']['nwrite'] simulation.inputParameters['knobs']['delt'] = round(delt, 4) varied_values = varied_values + "$\\delta t$$\,=\,$" + str( round(delt, 4)) + "; " # Add the string of varied values to the dictionary experiment.variedValues.append(varied_values[0:-2]) # Make sure we don't have the same label for each simulation for experiment in experiments: if len(set(experiment.variedValues)) == 1 and len( experiment.variedValues) != 1: simulation_ids = [ simulation.id for simulation in experiment.simulations ] common_prefix = ''.join(c[0] for c in takewhile( lambda x: all(x[0] == y for y in x), zip(*simulation_ids))) common_prefix = common_prefix if common_prefix.endswith( "_") else '_'.join(common_prefix.split('_')[0:-1]) + "_" simulation_ids2 = [ simulation.id.split("__")[0] for simulation in experiment.simulations ] common_prefix2 = ''.join(c[0] for c in takewhile( lambda x: all(x[0] == y for y in x), zip(*simulation_ids2))) common_prefix2 = common_prefix if common_prefix2.endswith( "_") else '_'.join(common_prefix2.split('_')[0:-1]) + "_" for i in range(len(experiment.variedValues)): if "__" in common_prefix: experiment.variedValues[i] = str( experiment.simulations[i].id).split("__")[-1] if "__" not in common_prefix: experiment.variedValues[i] = str( experiment.simulations[i].id).split("__")[0].split( common_prefix2)[1] # Add the delt t for now if False: for i in range(len(experiment.variedValues)): simulation = experiment.simulations[i] for input_file in simulation.input_files: netcdf_data = read_netcdf(input_file) delt1 = (netcdf_data['vec_time'][5] - netcdf_data['vec_time'][4] ) / simulation.inputParameters[ 'stella_diagnostics_knobs']['nwrite'] delt2 = (netcdf_data['vec_time'][-4] - netcdf_data['vec_time'][-5] ) / simulation.inputParameters[ 'stella_diagnostics_knobs']['nwrite'] print(" Time step input:", simulation.inputParameters['knobs']['delt']) print(" Time step start:", delt1) print(" Time step end:", delt2) experiment.variedValues[i] = experiment.variedValues[ i] + "; $\\delta t$$\,=\,$" + str(round(delt1, 4)) #experiment.variedValues[i] = experiment.variedValues[i] + "; $\\delta_e t$$\,=\,$" + str(round(delt2,4)) # # Sort the simulations and varied values # numbers = [ int("".join([s for s in value if s.isdigit()])) for value in experiment.variedValues ] # sorted_indexes = list(np.array(numbers).argsort()) # experiment.variedValues = [experiment.variedValues[i] for i in sorted_indexes] # experiment.simulations = [experiment.simulations[i] for i in sorted_indexes] # Now that all the simulations are added to experiments, we an add labels and markers and write config for experiment in experiments: experiment.finish_initialization() # Print some information for experiment in experiments: printv("") printv("Experiment " + str(experiments.index(experiment) + 1) + ":") printv(" Experiment ID: " + experiment.id) printv(" Varied Values: " + '["' + ('", "').join(experiment.variedValues) + '"]') printv(" Simulation IDs: " + "[" + (",").join([s.id for s in experiment.simulations]) + "]") printv("") return experiments
def read_inputParameters(input_file): ''' Read "*.in" file and return all the input parameters. Parameters ---------- input_file : PosixPath Absolute path of the *.in file that needs to be read. Returns ------- inputParameters = dict[knobs][variable] Returns the namelists from the stella code with their values. Knobs and variables ------------------- zgrid_parameters: nzed; nperiod; ntubes; boundary_option; zed_equal_arc; shat_zero; nzgrid" geo_knobs: geo_option; overwrite_bmag; overwrite_gradpar; overwrite_gds2; overwrite_gds21; overwrite_gds22; overwrite_gds23; overwrite_gds24; overwrite_gbdrift; overwrite_cvdrift; overwrite_gbdrift0; geo_file vmec_parameters vmec_filename; alpha0; zeta_center; nfield_periods; torflux; surface_option; verbose; zgrid_scalefac; zgrid_refinement_factor parameters beta; vnew_ref; rhostar; zeff; tite; nine vpamu_grids_parameters nvgrid; vpa_max; nmu; vperp_max; equally_spaced_mu_grid dist_fn_knobs adiabatic_option time_advance_knobs explicit_option; xdriftknob; ydriftknob; wstarknob; flip_flo kt_grids_knobs grid_option kt_grids_box_parameters nx; ny; dkx; dky; jtwist; y0; naky; nakx kt_grids_range_parameters nalpha; naky; nakx; aky_min; aky_max; akx_min; akx_max; theta0_min; theta0_max physics_flags full_flux_surface; include_mirror; nonlinear; include_parallel_nonlinearity; include_parallel_streaming init_g_knobs tstart; scale; ginit_option; width0; refac; imfac; den0; par0; tperp0; den1; upar1; tpar1; tperp1; den2; upar2; tpar2; tperp2; phiinit; zf_init; chop_side; left; even; restart_file; restart_dir; read_many knobs nstep; delt; fapar; fbpar; delt_option; zed_upwind; vpa_upwind; time_upwind; avail_cpu_time; cfl_cushion delt_adjust; mat_gen; mat_read; fields_kxkyz; stream_implicit; mirror_implicit; driftkinetic_implicit mirror_semi_lagrange; mirror_linear_interp; maxwellian_inside_zed_derivative; stream_matrix_inversion species_knobs nspec; species_option species_parameters_1; species_parameters_2; species_parameters_3; ... z; mass; dens; temp; tprim; fprim; d2ndr2; d2Tdr2; type stella_diagnostics_knobs nwrite; navg; nmovie; nsave; save_for_restart; write_omega; write_phi_vs_time; write_gvmus write_gzvs; write_kspectra; write_moments; flux_norm; write_fluxes_kxky millergeo_parameters rhoc; rmaj; shift; qinp; shat; kappa; kapprim; tri; triprim; rgeo; betaprim; betadbprim d2qdr2; d2psidr2; nzed_local; read_profile_variation; write_profile_variation layouts_knobs xyzs_layout; vms_layout neoclassical_input include_neoclassical_terms; nradii; drho; neo_option sfincs_input read_sfincs_output_from_file; nproc_sfincs; irad_min; irad_max; calculate_radial_electric_field includeXDotTerm; includeElectricFieldTermInXiDot; magneticDriftScheme; includePhi1 includePhi1InKineticEquation; geometryScheme; VMECRadialOption; equilibriumFile coordinateSystem; inputRadialCoordinate; inputRadialCoordinateForGradients; aHat psiAHat; Delta; nu_n; dPhiHatdrN; Er_window; nxi; nx; Ntheta; Nzeta ''' # Read the *in file and get the parameters for species_1 input_data = open(input_file, 'r') input_text = input_data.read().replace(' ', '') # Initiate the dictionary: load the default parameters input_parameters = load_defaultInputParameters() # Get the number of species nspec = read_integerInput(input_text, 'nspec') if nspec == "USE DEFAULT": nspec = 1 # Add more default species if nspec>2 for i in range(2, nspec + 1): knob = "species_parameters_" + str(i) keys = list(input_parameters["species_parameters_1"].keys()) input_parameters[knob] = {} for key in keys: input_parameters[knob][key] = input_parameters[ "species_parameters_1"][key] # Overwrite the default values if they have been changed in the <input files> knobs = list(input_parameters.keys()) knobs.remove(" ") for knob in knobs: try: # Only look at the text of the specific knob input_text_knob = input_text.split("&" + knob)[1].split("/")[0] parameters = list(input_parameters[knob].keys()) for parameter in parameters: default_value = input_parameters[knob][parameter] input_parameters[knob][ parameter] = read_parameterFromInputFile( input_text_knob, parameter, default_value) except: # If the knob is not present just pass pass # Fill in the species know for the adiabatic electrons: custom knob for the GUI if input_parameters["species_knobs"]["nspec"] == 1: input_parameters["species_parameters_a"]["nine"] = input_parameters[ "parameters"]["nine"] input_parameters["species_parameters_a"]["tite"] = input_parameters[ "parameters"]["tite"] input_parameters["species_parameters_a"]["dens"] = round( 1 / input_parameters["parameters"]["nine"], 4) input_parameters["species_parameters_a"]["temp"] = round( 1 / input_parameters["parameters"]["tite"], 4) # Calculate some extra variables input_parameters["vmec_parameters"]["rho"] = np.sqrt( input_parameters["vmec_parameters"]["torflux"]) # Now calculate some values manually if input_parameters["sfincs_input"]["irad_min"] == "-nradii/2": nradii = input_parameters["neoclassical_input"]["nradii"] input_parameters["sfincs_input"]["irad_min"] = -nradii / 2 input_parameters["sfincs_input"]["irad_max"] = nradii / 2 else: printv( "WARNING: <irad_min> was set by the input file but it should be calculated indirectly through <nradii>." ) if input_parameters["zgrid_parameters"][ "nzgrid"] == "nzed/2 + (nperiod-1)*nzed": nzed = input_parameters["zgrid_parameters"]["nzed"] nperiod = input_parameters["zgrid_parameters"]["nperiod"] input_parameters["zgrid_parameters"]["nzgrid"] = nzed / 2 + (nperiod - 1) * nzed else: printv( "WARNING: <nzgrid> was set by the input file but it should be calculated indirectly through <nzed> and <nperiod>." ) if input_parameters["vmec_parameters"][ "zgrid_scalefac"] == "2.0 if zed_equal_arc else 1.0": zed_equal_arc = input_parameters["zgrid_parameters"]["zed_equal_arc"] input_parameters["vmec_parameters"][ "zgrid_scalefac"] = 2.0 if zed_equal_arc else 1.0 input_parameters["vmec_parameters"][ "zgrid_refinement_factor"] = 4 if zed_equal_arc else 1 else: printv( "WARNING: <zgrid_scalefac> was set by the input file but it should be calculated indirectly through <zed_equal_arc>." ) if input_parameters["physics_flags"]["nonlinear"] == True: if input_parameters["kt_grids_box_parameters"][ "naky"] == "(ny-1)/3 + 1": ny = input_parameters["kt_grids_box_parameters"]["ny"] nx = input_parameters["kt_grids_box_parameters"]["nx"] input_parameters["kt_grids_box_parameters"]["naky"] = get_naky(ny) input_parameters["kt_grids_box_parameters"]["nakx"] = get_nakx(nx) if input_parameters["kt_grids_box_parameters"]["y0"] == -1.0: if input_parameters["physics_flags"]["full_flux_surface"] == False: print( "WARNING: When simulating a flux tube, y0 needs to be set in the input file." ) if input_parameters["physics_flags"]["full_flux_surface"] == True: input_parameters["kt_grids_box_parameters"][ "y0"] = "1./(rhostar*geo_surf%rhotor)" # Calculate some extra variables for nonlinear runs, note that svalue=psitor=torflux?? nfield = input_parameters["vmec_parameters"]["nfield_periods"] y0 = input_parameters["kt_grids_box_parameters"]["y0"] vmec_file = input_parameters["vmec_parameters"]["vmec_filename"] psitor = input_parameters["vmec_parameters"]["torflux"] nx = input_parameters["kt_grids_box_parameters"]["nx"] input_parameters["kt_grids_box_parameters"]["dky"] = round( 1. / y0, 4 ) # get the grid spacing in ky and then in kx using twist-and-shift BC try: vmec_file = input_file.parent + "/" + vmec_file if input_parameters["kt_grids_box_parameters"]["jtwist"] == -1: input_parameters["kt_grids_box_parameters"][ "jtwist"] = get_jtwist(vmec_file, psitor, nfield) input_parameters["kt_grids_box_parameters"]["dkx"] = get_dkx( vmec_file, psitor, nfield, y0) input_parameters["kt_grids_box_parameters"]["shat"] = get_shat( vmec_file, psitor) except: try: vmec_file = str(vmec_file).split(".nc")[0] + ".h5" if input_parameters["kt_grids_box_parameters"]["jtwist"] == -1: input_parameters["kt_grids_box_parameters"][ "jtwist"] = round( get_jtwist(vmec_file, psitor, nfield), 4) input_parameters["kt_grids_box_parameters"]["dkx"] = round( get_dkx(vmec_file, psitor, nfield, y0), 4) input_parameters["kt_grids_box_parameters"]["shat"] = round( get_shat(vmec_file, psitor), 4) except: printv('WARNING: vmec file was not found to calculate jtwist.') if input_parameters["physics_flags"]["nonlinear"] == False: for key in input_parameters["kt_grids_box_parameters"].keys(): input_parameters["kt_grids_box_parameters"][ key] = "Linear simulation" input_data.close() # Return the input_parameters return input_parameters if True: return
def write_unstableModes(self): ''' Sort the modes by stable/unstable and converged/unconverged. ''' # The section headers to save the data mode_header = 'STABLE MODE; CONVERGED MODE; PHI_END - PHI_START, RELATIVE FLUCTUATION OVER THE LAST 10% OF OMEGA AND GAMMA' # If the section already existed, remove it if mode_header in self.simulation_file: self.simulation_file.remove_section(mode_header) # Create the empty section self.simulation_file[mode_header] = {} # Build a matrix to hold whether the mode is stable/unstable and converged/unconverged self.stable_modes = np.empty((len(self.vec_kx), len(self.vec_ky))); self.stable_modes[:,:] = np.NaN self.converged_modes = np.empty((len(self.vec_kx), len(self.vec_ky))); self.converged_modes[:,:] = np.NaN # Go through the modes for i in range(len(self.vec_kx)): for j in range(len(self.vec_ky)): # Update the progress bar of the GUI if self.Progress: c = i*len(self.vec_ky) + j; length = len(self.vec_kx)*len(self.vec_ky) if self.Progress: self.Progress.move(c/length*100,"Determining the stability ("+str(c)+"/"+str(length)+")") # An unstable mode will grow in phi2 while a stable mode will decrease in phi2, therefore: # if phi_end < phi_start the mode is stable, if phi_end > phi_start, the mode is unstable. phi2_noNaN = self.phi2_kxky[:,i,j][np.isfinite(self.phi2_kxky[:,i,j])] first_10percent = int(np.size(phi2_noNaN)*1/10) last_10percent = int(np.size(phi2_noNaN)*9/10) phi_start = np.mean(phi2_noNaN[0:first_10percent]) phi_end = np.mean(phi2_noNaN[last_10percent:-1]) # Add <True> to <stable_modes> if the mode is stable, otherwise add <False> if phi_end - phi_start < 0: self.stable_modes[i,j] = True if phi_end - phi_start > 0: self.stable_modes[i,j] = False if len(phi2_noNaN) < 15: self.stable_modes[i,j] = False # # The above criteria doesn't always work correctly: stable modes have noise of gamma around zero! # gamma_noNaN = self.gamma_kxky[:,i,j][np.isfinite(self.gamma_kxky[:,i,j])] # last_10percent = int(np.size(gamma_noNaN)*9/10) # gamma_10percent = gamma_noNaN[last_10percent:-1] # sign_changes = np.where(np.diff(np.sign(gamma_10percent)))[0] # if len(sign_changes) >= : self.stable_modes[i,j] = True # Only if the mode is unstable, look if it is converged fluctuation_o = np.NaN; fluctuation_g = np.NaN if self.stable_modes[i,j] == False: # Look at omega and gamma to determine whether the mode has converged omega = abs(self.omega_kxky)[:,i,j] gamma = abs(self.gamma_kxky)[:,i,j] # Remove the nan and inifinite values omega = omega[np.isfinite(gamma)] gamma = gamma[np.isfinite(gamma)] # If the mode has converged it approaches a fixed value in time # Consinder the mode converged if gamma/omega doesn't change more than 2% over the last 10% of time did_yConverge = np.all( np.isclose(omega[int(len(omega)*0.90):], omega[-1], rtol=0.02) ) did_yConverge = np.all( np.isclose(gamma[int(len(gamma)*0.90):], gamma[-1], rtol=0.02) ) & did_yConverge # Add <True> to <converged_modes> if the mode has converged, otherwise add <False> if did_yConverge == True: self.converged_modes[i,j] = True if did_yConverge == False: self.converged_modes[i,j] = False # Add some information to the command prompt if did_yConverge == False: kx = self.vec_kx[i]; ky = self.vec_ky[j] fluctuation_o = (max(omega[int(len(omega)*0.90):])-min(omega[int(len(omega)*0.90):]))/omega[-1]*100 fluctuation_g = (max(gamma[int(len(gamma)*0.90):])-min(gamma[int(len(gamma)*0.90):]))/gamma[-1]*100 printv("\nThe simulation for (kx,kx) = ("+str(kx)+", "+str(ky)+") has not converged yet:") printv(" The relative fluctuation of omega between (0.9*t, t) is: "+str(fluctuation_o)+"%.") printv(" The relative fluctuation of gamma between (0.9*t, t) is: "+str(fluctuation_g)+"%.") # Save the linear data: [omega_last, omega_avg, omega_min, omega_max] kx = self.vec_kx[i]; ky = self.vec_ky[j]; mode_indices = '(' + str(kx) + ", " + str(ky) + ')' mode_data = [ str(k) for k in [self.stable_modes[i,j], self.converged_modes[i,j], phi_end - phi_start, fluctuation_o, fluctuation_g] ] self.simulation_file[mode_header][mode_indices] = "[" + ", ".join(mode_data) + "]" # Write the simulation file self.simulation_file.write(open(self.simulation_path, 'w')) return