def export_residuals(self, path): """ File transfer utility. Export residuals to disk. :type path: str :param path: path to save residuals """ if self.taskid == 0: self.logger.debug(f"exporting residuals to:\n{path}") unix.mkdir(os.path.join(path, "residuals")) src = os.path.join(self.cwd, "residuals") # If this residuals directory has not been created, something # has gone wrong with the preprocessing and workflow cannot proceed if not os.path.exists(src): print( msg.cli( "The Solver function 'export_residuals' expected " "'residuals' directories to be created but could not " "find them and cannot continue the workflow. Please " "check the preprocess.prepare_eval_grad() function", header="preprocess error", border="=")) sys.exit(-1) dst = os.path.join(path, "residuals", self.source_name) unix.mv(src, dst)
def generate_data(self, **model_kwargs): """ Generates data using the True model, exports traces to `traces/obs` :param model_kwargs: keyword arguments to pass to `generate_mesh` """ self.generate_mesh(**model_kwargs) unix.cd(self.cwd) setpar(key="SIMULATION_TYPE", val="1", file="DATA/Par_file") setpar(key="SAVE_FORWARD", val=".true.", file="DATA/Par_file") call_solver(PAR.MPIEXEC, "bin/xmeshfem2D", output="mesher.log") call_solver(PAR.MPIEXEC, "bin/xspecfem2D", output="solver.log") if PAR.FORMAT.upper() == "SU": # Work around SPECFEM2D's version dependent file names for tag in ["d", "v", "a", "p"]: unix.rename(old=f"single_{tag}.su", new="single.su", names=glob(os.path.join("OUTPUT_FILES", "*.su"))) unix.mv(src=glob(os.path.join("OUTPUT_FILES", self.data_wildcard)), dst=os.path.join("traces", "obs")) if PAR.SAVETRACES: self.export_traces(os.path.join(PATH.OUTPUT, "traces", "obs"))
def generate_data(self, **model_kwargs): """ Generates data using the True model, exports traces to `traces/obs` :param model_kwargs: keyword arguments to pass to `generate_mesh` """ # Create the mesh self.generate_mesh(**model_kwargs) # Run the Forward simulation unix.cd(self.cwd) setpar(key="SIMULATION_TYPE", val="1", file="DATA/Par_file") setpar(key="SAVE_FORWARD", val=".true.", file="DATA/Par_file") if PAR.ATTENUATION: setpar(key="ATTENUATION", val=".true.", file="DATA/Par_file") else: setpar(key="ATTENUATION", val=".false.", file="DATA/Par_file") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xspecfem3D") unix.mv(src=glob(os.path.join("OUTPUT_FILES", self.data_wildcard)), dst=os.path.join("traces", "obs")) # Export traces to disk for permanent storage if PAR.SAVETRACES: self.export_traces(os.path.join(PATH.OUTPUT, "traces", "obs"))
def generate_data(self): """ Overload seisflows.solver.base.generate_data. To be run in parallel Not used if PAR.CASE == "Data" Generates data in the synthetic-synthetic comparison case. Automatically calls generate mesh for the true model, rather than passing them in as kwargs. Also turns on attenuation for the forward model !!! attenuation could be moved into parameters.yaml? !!! """ unix.cd(self.cwd) setpar(key="SIMULATION_TYPE", val="1", file="DATA/Par_file") setpar(key="SAVE_FORWARD", val=".true.", file="DATA/Par_file") if PAR.ATTENUATION: setpar(key="ATTENUATION ", val=".true.", file="DATA/Par_file") else: setpar(key="ATTENUATION ", val=".false.", file="DATA/Par_file") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xspecfem3D") # move ASCII .sem? files into appropriate directory unix.mv(src=glob(os.path.join("OUTPUT_FILES", self.data_wildcard)), dst=os.path.join("traces", "obs")) # Export traces to permanent storage on disk if PAR.SAVETRACES: self.export_traces(os.path.join(PATH.OUTPUT, "traces", "obs"))
def save_residuals(self): """ Save the residuals to disk """ src = os.path.join(PATH.GRAD, "residuals") dst = os.path.join(PATH.OUTPUT, f"residuals_{optimize.iter:04d}") self.logger.debug(f"saving residuals to path:\n{dst}") unix.mv(src, dst)
def save_kernels(self): """ Save the kernel vector as a Fortran binary file on disk """ src = os.path.join(PATH.GRAD, "kernels") dst = os.path.join(PATH.OUTPUT, f"kernels_{optimize.iter:04d}") self.logger.debug(f"saving kernels to path:\n{dst}") unix.mv(src, dst)
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 save_traces(self): """ Save the waveform traces to disk. !!! This doesn't work? Traces are not saved to PATH.GRAD so src does !!! not exist """ src = os.path.join(PATH.GRAD, "traces") dst = os.path.join(PATH.OUTPUT, f"traces_{optimize.iter:04d}") self.logger.debug(f"saving traces to path:\n{dst}") unix.mv(src, dst)
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 save_gradient(self): """ Save the gradient vector. Allows saving numpy array or standard Fortran .bin files Saving as a vector saves on file count, but requires numpy and seisflows functions to read """ dst = os.path.join(PATH.OUTPUT, f"gradient_{optimize.iter:04d}") if PAR.SAVEAS in ["binary", "both"]: src = os.path.join(PATH.GRAD, "gradient") unix.mv(src, dst) if PAR.SAVEAS in ["vector", "both"]: src = os.path.join(PATH.OPTIMIZE, optimize.g_old) unix.cp(src, dst + ".npy") self.logger.debug(f"saving gradient to path:\n{dst}")
def export_kernels(self, path): """ File transfer utility. Export kernels to disk :type path: str :param path: path to save kernels """ if self.taskid == 0: self.logger.debug(f"exporting kernels to:\n{path}") unix.cd(self.kernel_databases) # Work around conflicting name conventions self.rename_kernels() src = glob("*_kernel.bin") dst = os.path.join(path, "kernels", self.source_name) unix.mkdir(dst) unix.mv(src, dst)
def forward(self, path="traces/syn"): """ Calls SPECFEM3D forward solver, exports solver outputs to traces dir :type path: str :param path: path to export traces to after completion of simulation """ # Set parameters and run forward simulation setpar(key="SIMULATION_TYPE", val="1", file="DATA/Par_file") setpar(key="SAVE_FORWARD", val=".true.", file="DATA/Par_file") if PAR.ATTENUATION: setpar(key="ATTENUATION", val=".true.", file="DATA/Par_file") else: setpar(key="ATTENUATION", val=".false`.", file="DATA/Par_file") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xgenerate_databases") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xspecfem3D") # Find and move output traces, by default to synthetic traces dir unix.mv(src=glob(os.path.join("OUTPUT_FILES", self.data_wildcard)), dst=path)
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 forward(self, path='traces/syn'): """ Calls SPECFEM2D forward solver, exports solver outputs to traces dir :type path: str :param path: path to export traces to after completion of simulation """ setpar(key="SIMULATION_TYPE", val="1", file="DATA/Par_file") setpar(key="SAVE_FORWARD", val=".true.", file="DATA/Par_file") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xmeshfem2D") call_solver(mpiexec=PAR.MPIEXEC, executable="bin/xspecfem2D") if PAR.FORMAT.upper() == "SU": # Work around SPECFEM2D's version dependent file names for tag in ["d", "v", "a", "p"]: unix.rename(old=f"single_{tag}.su", new="single.su", names=glob(os.path.join("OUTPUT_FILES", "*.su"))) unix.mv(src=glob(os.path.join("OUTPUT_FILES", self.data_wildcard)), dst=path)
# ============================================================================== print("Generating initial model from Tape 2007 example") os.chdir(SPECFEM3D_WORKDIR) cmd = f"./bin/xdecompose_mesh 4 ./MESH-default {SPECFEM3D_OUTPUT}/DATABASES_MPI" with open("xdecompose_mesh.out", "w") as f: subprocess.run(cmd.split(), shell=True, stdout=f) cmd = f"mpirun -np 4 ./bin/xgenerate_databases" with open("xgenerate_databases.out", "w") as f: subprocess.run("mpiexec ./bin/xSPECFEM3D", shell=True, stdout=f) # Move the model files (*.bin) into the OUTPUT_FILES directory, # where SeisFlows3 expects them unix.mv(glob.glob("DATA/*bin"), "OUTPUT_FILES") # Make sure we don't overwrite this initial model when creating our target model in the next step unix.mv("OUTPUT_FILES", "OUTPUT_FILES_INIT") # ============================================================================== print("Modifying Tape 2007 example for target model") # GENERATE MODEL_TRUE os.chdir(SPECFEM3D_DATA) # Edit the Par_file by increasing velocities by ~10% sf.sempar("velocity_model", "1 1 2600.d0 5900.d0 3550.0d0 0 0 10.d0 10.d0 0 0 0 0 0 0", skip_print=True) # ==============================================================================