Ejemplo n.º 1
0
    def smooth(self, input_path, **kwargs):
        """
        Specfem2D requires additional model parameters in directory to perform
        the xsmooth_sem task. This function will copy these files into the 
        directory before performing the base smooth operations. 

        Kwargs should match arguments of solver.base.smooth()
        
        .. note::
            This operation is usually run with run(single=True) so only one
            task will be performing these operations.

        :type input_path: str
        :param input_path: path to data
        """
        # Redundant to 'base' class but necessary
        if not exists(input_path):
            unix.mkdir(input_path)

        unix.cd(self.cwd)
        unix.cd("DATA")

        # Copy over only the files that are required. Won't execute if no match
        files = []
        for tag in ["jacobian", "NSPEC_ibool", "x", "y", "z"]:
            files += glob(f"*_{tag}.bin")
        for src in files:
            unix.cp(src=src, dst=input_path)

        super().smooth(input_path=input_path, **kwargs)
Ejemplo n.º 2
0
    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"))
Ejemplo n.º 3
0
    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"))
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    def setup_seisflows_working_directory(self):
        """
        Create and set the SeisFlows3 parameter file, making sure all required
        parameters are set correctly for this example problem
        """
        cd(self.cwd)

        self.sf.setup(force=True)  # Force will delete existing parameter file
        self.sf.configure()

        self.sf.par("ntask", self.ntask)  # default 3 sources for this example
        self.sf.par("materials", "elastic")  # how velocity model parameterized
        self.sf.par("density", "constant")  # update density or keep constant
        self.sf.par("nt", 5000)  # set by SPECFEM2D Par_file
        self.sf.par("dt", .06)  # set by SPECFEM2D Par_file
        self.sf.par("f0", 0.084)  # set by SOURCE file
        self.sf.par("format", "ascii")  # how to output synthetic seismograms
        self.sf.par("begin", 1)  # first iteration
        self.sf.par("end", self.niter)  # final iteration -- we will run 2
        self.sf.par("case", "synthetic")  # synthetic-synthetic inversion
        self.sf.par("attenuation", False)

        self.sf.par("specfem_bin", self.workdir_paths.bin)
        self.sf.par("specfem_data", self.workdir_paths.data)
        self.sf.par("model_init", self.workdir_paths.model_init)
        self.sf.par("model_true", self.workdir_paths.model_true)
Ejemplo n.º 6
0
    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"))
Ejemplo n.º 7
0
 def write_sources(self):
     """
     Write sources to text file
     """
     unix.cd(self.cwd)
     _, h = preprocess.load(dir="traces/obs")
     solvertools.write_sources(PAR=vars(PAR), h=h)
Ejemplo n.º 8
0
    def eval_func(self, path, write_residuals=True):
        """
        High level solver interface

        Performs forward simulations and evaluates the misfit function

        :type path: str
        :param path: directory from which model is imported and where residuals
            will be exported
        :type write_residuals: bool
        :param write_residuals: calculate and export residuals
        """
        if self.taskid == 0:
            self.logger.info("running forward simulations")

        unix.cd(self.cwd)
        self.import_model(path)
        self.forward()

        if write_residuals:
            if self.taskid == 0:
                self.logger.debug("calling preprocess.prepare_eval_grad()")
            preprocess.prepare_eval_grad(cwd=self.cwd,
                                         taskid=self.taskid,
                                         source_name=self.source_name,
                                         filenames=self.data_filenames)
            self.export_residuals(path)
Ejemplo n.º 9
0
    def initialize_adjoint_traces(self):
        """
        Setup utility: Creates the "adjoint traces" expected by SPECFEM

        Note:
            Adjoint traces are initialized by writing zeros for all channels.
            Channels actually in use during an inversion or migration will be
            overwritten with nonzero values later on.
        """
        # Initialize adjoint traces as zeroes for all data_filenames
        # write to `traces/adj`
        super().initialize_adjoint_traces()

        # Rename data to work around Specfem naming convetions
        self.rename_data()

        # Workaround for Specfem3D's requirement that all components exist,
        # even ones not in use as adjoint traces
        if PAR.FORMAT.upper() == "SU":
            unix.cd(os.path.join(self.cwd, "traces", "adj"))

            for iproc in range(PAR.NPROC):
                for channel in ["x", "y", "z"]:
                    dst = f"{iproc:d}_d{channel}_SU.adj"
                    if not exists(dst):
                        src = f"{iproc:d}_d{PAR.COMPONENTS[0]}_SU.adj"
                        unix.cp(src, dst)
Ejemplo n.º 10
0
    def generate_mesh(self, model_path, model_name, model_type='gll'):
        """
        Performs meshing with internal mesher Meshfem2D and database generation

        :type model_path: str
        :param model_path: path to the model to be used for mesh generation
        :type model_name: str
        :param model_name: name of the model to be used as identification
        :type model_type: str
        :param model_type: available model types to be passed to the Specfem3D
            Par_file. See Specfem3D Par_file for available options.
        """
        assert (exists(model_path)), f"model {model_path} does not exist"

        available_model_types = ["gll"]
        assert(model_type in available_model_types), \
            f"{model_type} not in available types {available_model_types}"

        unix.cd(self.cwd)

        # Run mesh generation
        if model_type == "gll":
            self.check_mesh_properties(model_path)

            # Copy the model files (ex: proc000023_vp.bin ...) into DATA
            src = glob(os.path.join(model_path, "*"))
            dst = self.model_databases
            unix.cp(src, dst)

        # Export the model into output folder
        if self.taskid == 0:
            self.export_model(os.path.join(PATH.OUTPUT, model_name))
Ejemplo n.º 11
0
    def initialize_solver_directories(self):
        """
        Creates directory structure expected by SPECFEM3D (bin/, DATA/) copies
        executables, and prepares input files. Executables must be supplied
        by user as there is no mechanism for automatically compiling from source

        Directories will act as completely independent Specfem run directories.
        This allows for embarrassing parallelization while avoiding the need
        for intra-directory communications, at the cost of redundancy and
        extra files.
        """
        if self.taskid == 0:
            self.logger.info(f"initializing {PAR.NTASK} solver directories")

        unix.mkdir(self.cwd)
        unix.cd(self.cwd)

        # Create directory structure
        for cwd_dir in [
                "bin", "DATA", "OUTPUT_FILES/DATABASES_MPI", "traces/obs",
                "traces/syn", "traces/adj", self.model_databases,
                self.kernel_databases
        ]:
            unix.mkdir(cwd_dir)

        # Copy exectuables into the bin/ directory
        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, strip the source name as SPECFEM
        # just expects `source_name`
        src = os.path.join(PATH.SPECFEM_DATA,
                           f"{self.source_prefix}_{self.source_name}")
        dst = os.path.join("DATA", self.source_prefix)
        unix.ln(src, dst)

        if self.taskid == 0:
            mainsolver = os.path.join(PATH.SOLVER, "mainsolver")
            # Symlink taskid_0 as mainsolver in solver directory for convenience
            if not os.path.exists(mainsolver):
                unix.ln(self.cwd, mainsolver)
                self.logger.debug(f"source {self.source_name} symlinked as "
                                  f"mainsolver")
        else:
            # Copy the initial model from mainsolver into current directory
            # Avoids the need to run multiple instances of xgenerate_databases
            src = os.path.join(PATH.SOLVER, "mainsolver", "OUTPUT_FILES",
                               "DATABASES_MPI")
            dst = os.path.join(self.cwd, "OUTPUT_FILES", "DATABASES_MPI")
            unix.cp(src, dst)

        self.check_solver_parameter_files()
Ejemplo n.º 12
0
 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")
Ejemplo n.º 13
0
 def finalize_specfem2d_par_file(self):
     """
     Last minute changes to get the SPECFEM2D Par_file in the correct format
     for running SeisFlows3. Setting model type to read from .bin GLL files
     change number of stations etc.
     """
     cd(self.workdir_paths.data)
     self.sf.sempar("model", "gll")  # GLL so SPECFEM reads .bin files
Ejemplo n.º 14
0
    def write_parameters(self):
        """
        Write a set of parameters

        !!! This calls on plugins.solver.specfem3d.write_parameters()
            but that function doesn't exist !!!
        """
        unix.cd(self.cwd)
        solvertools.write_parameters(vars(PAR))
Ejemplo n.º 15
0
    def smooth(self,
               input_path,
               output_path,
               parameters=None,
               span_h=0.,
               span_v=0.,
               output="solver.log"):
        """
        Postprocessing wrapper: xsmooth_sem
        Smooths kernels by convolving them with a Gaussian.

        .. note::
            paths require a trailing `/` character when calling xsmooth_sem

        .. note::
            It is ASSUMED that this function is being called by
            system.run(single=True) so that we can use the main solver
            directory to perform the kernel smooth task

        :type input_path: str
        :param input_path: path to data
        :type output_path: str
        :param output_path: path to export the outputs of xcombine_sem
        :type parameters: list
        :param parameters: optional list of parameters,
            defaults to `self.parameters`
        :type span_h: float
        :param span_h: horizontal smoothing length in meters
        :type span_v: float
        :param span_v: vertical smoothing length in meters
        :type output: str
        :param output: file to output stdout to
        """
        if parameters is None:
            parameters = self.parameters

        if not exists(output_path):
            unix.mkdir(output_path)

        # Apply smoothing operator inside scratch/solver/*
        unix.cd(self.cwd)

        # mpiexec ./bin/xsmooth_sem SMOOTH_H SMOOTH_V name input output use_gpu
        for name in parameters:
            call_solver(mpiexec=PAR.MPIEXEC,
                        executable=" ".join([
                            "bin/xsmooth_sem",
                            str(span_h),
                            str(span_v), f"{name}_kernel",
                            os.path.join(input_path, ""),
                            os.path.join(output_path, ""), ".false"
                        ]),
                        output=output)

        # Rename output files
        files = glob(os.path.join(output_path, "*"))
        unix.rename(old="_smooth", new="", names=files)
Ejemplo n.º 16
0
    def update(self):
        """
        Updates L-BFGS algorithm history

        .. note::
            Because models are large, and multiple iterations of models need to
            be stored in memory, previous models are stored as `memmaps`,
            which allow for access of small segments of large files on disk,
            without reading the entire file. Memmaps are array like objects.

        .. note::
            Notation for s and y taken from Liu & Nocedal 1989
            iterate notation: sk = x_k+1 - x_k and yk = g_k+1 - gk

        :rtype s: np.memmap
        :return s: memory of the model differences `m_new - m_old`
        :rtype y: np.memmap
        :return y: memory of the gradient differences `g_new - g_old`
        """
        unix.cd(PATH.OPTIMIZE)

        # Determine the iterates for model m and gradient g
        s_k = self.load(self.m_new) - self.load(self.m_old)
        y_k = self.load(self.g_new) - self.load(self.g_old)

        # Determine the shape of the memory map (length of model, length of mem)
        m = len(s_k)
        n = PAR.LBFGSMEM

        # Initial iteration, need to create the memory map
        if self.memory_used == 0:
            s = np.memmap(filename=self.s_file, mode="w+", dtype="float32",
                          shape=(m, n))
            y = np.memmap(filename=self.y_file, mode="w+", dtype="float32",
                          shape=(m, n))
            # Store the model and gradient differences in memmaps
            s[:, 0] = s_k
            y[:, 0] = y_k
            self.memory_used = 1
        # Subsequent iterations will append to memory maps
        else:
            s = np.memmap(filename=self.s_file, mode="r+", dtype="float32",
                          shape=(m, n))
            y = np.memmap(filename=self.y_file, mode="r+", dtype="float32",
                          shape=(m, n))
            # Shift all stored memory by one index to make room for latest mem
            s[:, 1:] = s[:, :-1]
            y[:, 1:] = y[:, :-1]
            # Store the latest model and gradient in first index
            s[:, 0] = s_k
            y[:, 0] = y_k

            # Keep track of the memory used
            if self.memory_used < PAR.LBFGSMEM:
                self.memory_used += 1

        return s, y
Ejemplo n.º 17
0
    def setup(self):
        """
        Set up the LBFGS optimization schema
        """
        super().setup()

        # Create a separate directory for LBFGS matters
        unix.cd(PATH.OPTIMIZE)
        unix.mkdir(self.LBFGS_dir)
Ejemplo n.º 18
0
    def setup_specfem2d_for_model_true(self):
        """
        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("> Updating initial homogeneous velocity model values")
        new_model = "1 1 2600.d0 5900.d0 3550.0d0 0 0 10.d0 10.d0 0 0 0 0 0 0"
        self.sf.sempar("velocity_model", new_model)
Ejemplo n.º 19
0
    def write_receivers(self):
        """
        Write a list of receivers into a text file

        !!! This calls on plugins.solver.specfem3d.write_receivers()
            but incorrect number of parameters is forwarded !!!
        """
        unix.cd(self.cwd)
        setpar(key="use_existing_STATIONS", val=".true", file="DATA/Par_file")

        _, h = preprocess.load("traces/obs")
        solvertools.write_receivers(h.nr, h.rx, h.rz)
Ejemplo n.º 20
0
    def data_filenames(self):
        """
        Returns the filenames of all data, either by the requested components
        or by all available files in the directory.

        :rtype: list
        :return: list of data filenames
        """
        unix.cd(os.path.join(self.cwd, "traces", "obs"))

        if PAR.FORMAT.upper() == "ASCII":
            return sorted(glob("*.???.sem.ascii"))
Ejemplo n.º 21
0
    def apply(self, q, s=None, y=None):
        """
        Applies L-BFGS inverse Hessian to given vector

        :type q: np.array
        :param q: gradient direction to apply L-BFGS to
        :type s: np.memmap
        :param s: memory of model differences
        :param s: memory of model differences
        :type y: np.memmap
        :param y: memory of gradient direction differences
        :rtype r: np.array
        :return r: new search direction from application of L-BFGS
        """
        unix.cd(PATH.OPTIMIZE)

        # If no memmaps are given as arguments, instantiate them
        if s is None or y is None:
            m = len(q)
            n = PAR.LBFGSMEM
            s = np.memmap(filename=self.s_file, mode="w+", dtype="float32",
                          shape=(m, n))
            y = np.memmap(filename=self.y_file, mode="w+", dtype="float32",
                          shape=(m, n))

        # First matrix product
        # Recursion step 2 from appendix A of Modrak & Tromp 2016
        kk = self.memory_used
        rh = np.zeros(kk)
        al = np.zeros(kk)
        for ii in range(kk):
            rh[ii] = 1 / np.dot(y[:, ii], s[:, ii])
            al[ii] = rh[ii] * np.dot(s[:, ii], q)
            q = q - al[ii] * y[:, ii]

        # Apply a preconditioner if available
        if self.precond:
            r = self.precond(q)
        else:
            r = q

        # Use scaling M3 proposed by Liu and Nocedal 1989
        sty = np.dot(y[:, 0], s[:, 0])
        yty = np.dot(y[:, 0], y[:, 0])
        r *= sty/yty

        # Second matrix product
        # Recursion step 4 from appendix A of Modrak & Tromp 2016
        for ii in range(kk - 1, -1, -1):
            be = rh[ii] * np.dot(y[:, ii], r)
            r = r + s[:, ii] * (al[ii] - be)

        return r
Ejemplo n.º 22
0
    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
Ejemplo n.º 23
0
    def eval_fwd(self, path=''):
        """
        High level solver interface

        Performans forward simulations only, function evaluation is split off
        into its own function

        :type path: str
        :param path: path in the scratch directory to use for I/O
        """
        unix.cd(self.cwd)
        self.import_model(path)
        self.forward()
Ejemplo n.º 24
0
    def configure_specfem2d_and_make_binaries(self):
        """
        Run ./configure within the SPECFEM2D repo directory.
        This function assumes it is being run from inside the repo. Should guess
        all the configuration options. Probably the least stable part of the
        example
        """
        cd(self.sem2d_paths.repo)
        try:
            if not os.path.exists("./config.log"):
                cmd = "./configure"
                print(f"Configuring SPECFEM2D with command: {cmd}")
                # Ignore the configure outputs from SPECFEM
                subprocess.run(cmd,
                               shell=True,
                               check=True,
                               stdout=subprocess.DEVNULL)
            else:
                print("SPECFEM2D already configured, skipping 'configure'")
        except subprocess.CalledProcessError as e:
            print(f"SPECFEM `configure` step has failed, please check and "
                  f"retry. If this command repeatedly fails, you may need "
                  f"to configure SPECFEM2D manually.\n{e}")
            sys.exit(-1)

        try:
            if not glob.glob("./bin/x*"):
                cmd = "make all"
                print(f"Making SPECFEM2D binaries with command: {cmd}")
                # Ignore the make outputs from SPECFEM
                subprocess.run(cmd,
                               shell=True,
                               check=True,
                               stdout=subprocess.DEVNULL)
            else:
                print("executables found in SPECFEM2D/bin directory, "
                      "skipping 'make'")
        except subprocess.CalledProcessError as e:
            print(f"SPECFEM 'make' step has failed, please check and "
                  f"retry. If this command repeatedly fails, you may need "
                  f"to compile SPECFEM2D manually.\n{e}")
            sys.exit(-1)

        # Symlink the finished repo into the working directory so that any
        # subsequent runs won't need to have the user re-type repo location
        if not os.path.exists(os.path.join(self.cwd, "specfem2d")):
            print("symlinking existing specfem2D repository to cwd")
            ln(self.sem2d_paths.repo, os.path.join(self.cwd, "specfem2d"))
    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")
Ejemplo n.º 26
0
    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()
Ejemplo n.º 27
0
    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)
Ejemplo n.º 28
0
    def run_xspecfem2d_binaries(self):
        """
        Runs the xmeshfem2d and then xspecfem2d binaries using subprocess and then
        do some cleanup to get files in the correct locations. Assumes that we
        can run the binaries directly with './'
        """
        cd(self.workdir_paths.workdir)

        cmd_mesh = f"./bin/xmeshfem2D > OUTPUT_FILES/mesher.log.txt"
        cmd_spec = f"./bin/xspecfem2D > OUTPUT_FILES/solver.log.txt"

        for cmd in [cmd_mesh, cmd_spec]:
            print(f"Running SPECFEM2D with command: {cmd}")
            subprocess.run(cmd,
                           shell=True,
                           check=True,
                           stdout=subprocess.DEVNULL)
Ejemplo n.º 29
0
    def restart(self):
        """
        On top of base restart class, include a restart of the LBFGS internal
        memory and memmaps
        """
        super().restart()

        self.logger.info("restarting L-BFGS optimization algorithm by clearing "
                         "internal memory")
        self.LBFGS_iter = 1
        self.memory_used = 0

        unix.cd(PATH.OPTIMIZE)
        s = np.memmap(filename=self.s_file, mode="r+")
        y = np.memmap(filename=self.y_file, mode="r+")
        s[:] = 0.
        y[:] = 0.
Ejemplo n.º 30
0
    def initialize_adjoint_traces(self):
        """
        Setup utility: Creates the "adjoint traces" expected by SPECFEM.
        This is only done for the 'base' the Preprocess class.

        Note:
            Adjoint traces are initialized by writing zeros for all channels.
            Channels actually in use during an inversion or migration will be
            overwritten with nonzero values later on.
        """
        super().initialize_adjoint_traces()

        unix.cd(self.cwd)
        unix.cd(os.path.join("traces", "adj"))

        # work around SPECFEM2D's use of different name conventions for
        # regular traces and 'adjoint' traces
        if PAR.FORMAT.upper() == "SU":
            files = glob("*SU")
            unix.rename(old="_SU", new="_SU.adj", names=files)
        elif PAR.FORMAT.upper() == "ASCII":
            files = glob("*sem?")

            # Get the available extensions, which are named based on unit
            extensions = set([os.path.splitext(_)[-1] for _ in files])
            for extension in extensions:
                unix.rename(old=extension, new=".adj", names=files)

        # SPECFEM2D requires that all components exist even if ununsed
        components = ["x", "y", "z", "p"]

        if PAR.FORMAT.upper() == "SU":
            for comp in components:
                src = f"U{PAR.COMPONENTS[0]}_file_single.su.adj"
                dst = f"U{comp.lower()}s_file_single.su.adj"
                if not exists(dst):
                    unix.cp(src, dst)
        elif PAR.FORMAT.upper() == "ASCII":
            for fid in glob("*.adj"):
                net, sta, cha, ext = fid.split(".")
                for comp in components:
                    # Replace the last value in the channel with new component
                    cha_check = cha[:-1] + comp.upper()
                    fid_check = ".".join([net, sta, cha_check, ext])
                    if not exists(fid_check):
                        unix.cp(fid, fid_check)