def cleanup_xspecfem2d_run(self, choice=None): """ Do some cleanup after running the SPECFEM2D binaries to make sure files are in the correct locations, and rename the OUTPUT_FILES directory so that it does not get overwritten by subsequent runs :type choice: str :param choice: Rename the OUTPUT_FILES directory with a suffix tag msut be 'INIT' or 'TRUE'. If None, will not rename but the """ cd(self.workdir_paths.workdir) print("> Cleaning up after xspecfem2d, setting up for new run") # SPECFEM2D outputs its models in the DATA/ directory by default, # while SeisFlows3 expects this in the OUTPUT_FILES/ directory (which is # the default in SPECFEM3D) mv(glob.glob("DATA/*bin"), self.workdir_paths.output) if choice == "INIT": mv(self.workdir_paths.output, self.workdir_paths.model_init) # Create a new OUTPUT_FILES/ directory for TRUE run rm(self.workdir_paths.output) mkdir(self.workdir_paths.output) elif choice == "TRUE": mv(self.workdir_paths.output, self.workdir_paths.model_true)
def clean(self): """ Clean up solver-dependent run directory by removing the OUTPUT_FILES/ directory """ unix.cd(self.cwd) unix.rm("OUTPUT_FILES") unix.mkdir("OUTPUT_FILES")
def clean(self): """ Cleans directories in which function and gradient evaluations were carried out """ self.logger.info(msg.mnr("CLEANING WORKDIR FOR NEXT ITERATION")) unix.rm(PATH.GRAD) unix.rm(PATH.FUNC) unix.mkdir(PATH.GRAD) unix.mkdir(PATH.FUNC)
def finalize_search(self): """ Prepares algorithm machinery and scratch directory for next model update Removes old model/search parameters, moves current parameters to old, sets up new current parameters and writes statistic outputs """ self.logger.info(msg.sub("FINALIZING LINE SEARCH")) g = self.load(self.g_new) p = self.load(self.p_new) x = self.line_search.search_history()[0] f = self.line_search.search_history()[1] # Clean scratch directory unix.cd(PATH.OPTIMIZE) # Remove the old model parameters if self.iter > 1: self.logger.info("removing previously accepted model files (old)") for fid in [self.m_old, self.f_old, self.g_old, self.p_old]: unix.rm(fid) self.logger.info( "shifting current model (new) to previous model (old)") unix.mv(self.m_new, self.m_old) unix.mv(self.f_new, self.f_old) unix.mv(self.g_new, self.g_old) unix.mv(self.p_new, self.p_old) self.logger.info("setting accepted line search model as current model") unix.mv(self.m_try, self.m_new) self.savetxt(self.f_new, f.min()) self.logger.info(f"current misfit is {self.f_new}={f.min():.3E}") # !!! TODO Describe what stats are being written here self.logger.info(f"writing optimization stats to: {CFGPATHS.STATSDIR}") self.write_stats(self.log_factor, value=-dot(g, g)**-0.5 * (f[1] - f[0]) / (x[1] - x[0])) self.write_stats(self.log_gradient_norm_L1, value=np.linalg.norm(g, 1)) self.write_stats(self.log_gradient_norm_L2, value=np.linalg.norm(g, 2)) self.write_stats(self.log_misfit, value=f[0]) self.write_stats(self.log_restarted, value=self.restarted) self.write_stats(self.log_slope, value=(f[1] - f[0]) / (x[1] - x[0])) self.write_stats(self.log_step_count, value=self.line_search.step_count) self.write_stats(self.log_step_length, value=x[f.argmin()]) self.write_stats(self.log_theta, value=180. * np.pi**-1 * angle(p, -g)) self.logger.info("resetting line search step count to 0") self.line_search.step_count = 0
def adjoint(self): """ Calls SPECFEM3D adjoint solver, creates the `SEM` folder with adjoint traces which is required by the adjoint solver """ setpar(key="SIMULATION_TYPE", val="3", file="DATA/Par_file") setpar(key="SAVE_FORWARD", val=".false.", file="DATA/Par_file") setpar(key="ATTENUATION", val=".false.", file="DATA/Par_file") unix.rm("SEM") unix.ln("traces/adj", "SEM") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xspecfem3D")
def setup_specfem2d_for_model_true(self): """ Overwrites MODEL TRUE creation from EX1 Make some adjustments to the parameter file to create the final velocity model. This function assumes it is running from inside the SPECFEM2D/DATA directory """ cd(self.workdir_paths.data) assert (os.path.exists("Par_file")), f"I cannot find the Par_file!" print("> EX: Updating SPECFEM2D to set checkerboard model as " "MODEL_TRUE") self.sf.sempar("model", "legacy") # read model_velocity.dat_checker rm("proc000000_model_velocity.dat_input") ln("model_velocity.dat_checker", "proc000000_model_velocity.dat_input")
def initialize_solver_directories(self): """ Creates solver directories in serial using a single node. Should only be run by master job. Differs from Base initialize_solver_directories() as this serial task will create directory structures for each source, rather than having each source create its own. However the internal dir structure is the same. """ for source_name in self.source_names: cwd = os.path.join(PATH.SOLVER, source_name) # Remove any existing scratch directory unix.rm(cwd) # Create internal directory structure, change into directory to make # all actions RELATIVE path actions unix.mkdir(cwd) unix.cd(cwd) for cwd_dir in [ "bin", "DATA", "OUTPUT_FILES/DATABASES_MPI", "traces/obs", "traces/syn", "traces/adj" ]: unix.mkdir(cwd_dir) # Copy exectuables src = glob(os.path.join(PATH.SPECFEM_BIN, "*")) dst = os.path.join("bin", "") unix.cp(src, dst) # Copy all input files except source files src = glob(os.path.join(PATH.SPECFEM_DATA, "*")) src = [_ for _ in src if self.source_prefix not in _] dst = os.path.join("DATA", "") unix.cp(src, dst) # symlink event source specifically src = os.path.join(PATH.SPECFEM_DATA, f"{self.source_prefix}_{source_name}") dst = os.path.join("DATA", self.source_prefix) unix.ln(src, dst) if source_name == self.mainsolver: # Symlink taskid_0 as mainsolver in solver directory unix.ln(source_name, os.path.join(PATH.SOLVER, "mainsolver")) # Only check the solver parameters once self.check_solver_parameter_files()
def clean(self): """ Determine if forward simulation from line search can be carried over. We assume clean() is the final flow() argument so that we can update the thrifty status here. """ self.update_status() if self.thrifty: self.logger.info( msg.mnr("THRIFTY CLEANING WORKDIR FOR NEXT " "ITERATION")) unix.rm(PATH.GRAD) unix.mv(PATH.FUNC, PATH.GRAD) unix.mkdir(PATH.FUNC) else: super().clean()
def setup_specfem2d_for_model_init(self): """ Make some adjustments to the original parameter file to. This function assumes it is running from inside the SPECFEM2D/DATA dir """ cd(self.workdir_paths.data) assert (os.path.exists("Par_file")), f"I cannot find the Par_file!" print("> Setting the SPECFEM2D Par_file for SeisFlows3 compatiblility") self.sf.sempar("setup_with_binary_database", 1) # create .bin files self.sf.sempar("save_model", "binary") # output model in .bin format self.sf.sempar("save_ASCII_kernels", ".false.") # kernels also .bin rm(self.workdir_paths.output) mkdir(self.workdir_paths.output)
def adjoint(self): """ Calls SPECFEM2D adjoint solver, creates the `SEM` folder with adjoint traces which is required by the adjoint solver """ setpar(key="SIMULATION_TYPE", val="3", file="DATA/Par_file") setpar(key="SAVE_FORWARD", val=".false.", file="DATA/Par_file") unix.rm("SEM") unix.ln("traces/adj", "SEM") # Deal with different SPECFEM2D name conventions for regular traces and # "adjoint" traces if PAR.FORMAT.upper == "SU": unix.rename(old=".su", new=".su.adj", names=glob(os.path.join("traces", "adj", "*.su"))) call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xmeshfem2D") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xspecfem2D")
def setup(self): """ Prepares solver for inversion or migration. Sets up directory structure expected by SPECFEM and copies or generates seismic data to be inverted or migrated .. note:; As input for an inversion or migration, users can choose between providing data, or providing a target model from which data are generated on the fly. In the former case, a value for PATH.DATA must be supplied; in the latter case, a value for PATH.MODEL_TRUE must be provided. """ # Clean up for new inversion unix.rm(self.cwd) self.initialize_solver_directories() # Determine where observation data will come from if PAR.CASE.upper() == "SYNTHETIC" and PATH.MODEL_TRUE is not None: if self.taskid == 0: self.logger.info( "generating 'data' with MODEL_TRUE synthetics") # Generate synthetic data on the fly using the true model self.generate_data(model_path=PATH.MODEL_TRUE, model_name="model_true", model_type="gll") elif PATH.DATA is not None and os.path.exists(PATH.DATA): # If Data provided by user, copy directly into the solver directory unix.cp(src=glob(os.path.join(PATH.DATA, self.source_name, "*")), dst=os.path.join("traces", "obs")) # Prepare initial model if self.taskid == 0: self.logger.info("running mesh generation for MODEL_INIT") self.generate_mesh(model_path=PATH.MODEL_INIT, model_name="model_init", model_type="gll") # Create blank adjoint traces to be overwritten self.initialize_adjoint_traces()
def finalize_specfem2d_par_file(self): """ Final changes to the SPECFEM2D Par_file before running SeisFlows. Par_file will be used to control all the child specfem2d directories. Need to tell them to read models from .bin files, and to use existing station files rather than create them from the Par_file """ print("> EX2: Finalizing SPECFEM2D Par_file for SeisFlows3 inversion") cd(self.workdir_paths.data) self.sf.sempar("model", "gll") # GLL so SPECFEM reads .bin files self.sf.sempar("use_existing_stations", ".true.") # Use STATIONS file # Assign STATIONS_checker file which has 132 stations rm("STATIONS") # Only write the first 10 lines to get 10 stations in inversion with open("STATIONS_checker", "r") as f: lines = f.readlines() print(f"> EX2: Using {self.nsta} stations in this inversion workflow") with open("STATIONS", "w") as f: f.writelines(lines[:self.nsta])
def create_specfem2d_working_directory(self): """ Create the working directory where we will generate our initial and final models using one of the SPECFEM2D examples """ assert (os.path.exists(self.sem2d_paths["example"])), ( f"SPECFEM2D/EXAMPLE directory: '{self.sem2d['example']}' " f"does not exist, please check this path and try again.") # Incase this example has written before, remove dir. that were created rm(self.workdir_paths.workdir) mkdir(self.workdir_paths.workdir) # Copy the binary executables and DATA from the SPECFEM2D example cp(self.sem2d_paths.bin, self.workdir_paths.bin) cp(self.sem2d_paths.example_data, self.workdir_paths.data) # Make sure that SPECFEM2D can find the expected files in the DATA/ dir cd(self.workdir_paths.data) rm("SOURCE") ln("SOURCE_001", "SOURCE") rm("Par_file") ln("Par_file_Tape2007_onerec", "Par_file")
""" Create a test directory structure with actual data to test individual modules """ import os from seisflows3.tools import unix from seisflows3.config import CFGPATHS, ROOT_DIR testdir = os.path.join(ROOT_DIR, "tests", "test_data") workdir = os.path.join(testdir, "workdir") for key, path in CFGPATHS.__dict__.items(): full_path = os.path.join(workdir, path) if os.path.exists(full_path): unix.rm(full_path) # Identify files by the separator between filename and extension if "." in path: open(full_path, "w") else: unix.mkdir(os.path.join(workdir, path)) # Make the scratch solver directory scratchdir = os.path.join(workdir, CFGPATHS.SCRATCHDIR) solverdir = os.path.join(scratchdir, "solver", "001") tracesdir = os.path.join(solverdir, "traces") for dir_ in [scratchdir, solverdir, tracesdir]: unix.mkdir(dir_) for dir_ in ["obs", "syn", "adj"]: unix.mkdir(os.path.join(tracesdir, dir_))