Exemplo n.º 1
0
    def linear_forward_model_rho(self, shot, m0, m1, return_parameters=[], dWaveOp0=None, wavefield=None):
        """Applies the forward model to the model for the given solver in terms of a pertubation of rho.
        
        Parameters
        ----------
        shot : pysit.Shot
            Gives the source signal approximation for the right hand side.
        m0 : solver.ModelParameters
            The parameters upon where to center the linear approximation.
        m1 : solver.ModelParameters
            The parameters upon which to apply the linear forward model to.
        return_parameters : list of {'wavefield1', 'dWaveOp1', 'dWaveOp0', 'simdata'}
            Values to return.
        u0tt : ndarray
            Derivative field required for the imaging condition to be used as right hand side.
        
        
        Returns
        -------
        retval : dict
            Dictionary whose keys are return_parameters that contains the specified data.
        
        Notes
        -----
        * u1 is used as the target field universally.  It could be velocity potential, it could be displacement, it could be pressure.  
        * u1tt is used to generically refer to the derivative of u1 that is needed to compute the imaging condition.
        * If u0tt is not specified, it may be computed on the fly at potentially high expense.
        
        """

        # Local references
        solver = self.solver
        solver.model_parameters = m0 # this updates dt and the number of steps so that is appropriate for the current model
        
        mesh = solver.mesh
        sh=mesh.shape(include_bc=True,as_grid=True)

        d = solver.domain
        dt = solver.dt
        nsteps = solver.nsteps
        source = shot.sources
    
        model_2=1.0/m1.rho
        model_2=mesh.pad_array(model_2)

        #Lap = build_heterogenous_(sh,model_2,[mesh.x.delta,mesh.z.delta])
        rp=dict()
        rp['laplacian']=True
        Lap = build_heterogenous_matrices(sh,[mesh.x.delta,mesh.z.delta],model_2.reshape(-1,),rp=rp)

        # Storage for the field
        if 'wavefield1' in return_parameters:
            us = list()
        
        # Setup data storage for the forward modeled data
        if 'simdata' in return_parameters:
            simdata = np.zeros((solver.nsteps, shot.receivers.receiver_count))

        # Storage for the time derivatives of p
        if 'dWaveOp0' in return_parameters:
            dWaveOp0ret = list()
            
        if 'dWaveOp1' in return_parameters:
            dWaveOp1 = list()
        
        # Step k = 0
        # p_0 is a zero array because if we assume the input signal is causal
        # and we assume that the initial system (i.e., p_(-2) and p_(-1)) is
        # uniformly zero, then the leapfrog scheme would compute that p_0 = 0 as
        # well. ukm1 is needed to compute the temporal derivative.
        solver_data = solver.SolverData()
        
        if dWaveOp0 is None:
            solver_data_u0 = solver.SolverData()
            
            # For u0, set up the right hand sides
            rhs_u0_k   = np.zeros(mesh.shape(include_bc=True))
            rhs_u0_kp1 = np.zeros(mesh.shape(include_bc=True))              
            rhs_u0_k   = self._setup_forward_rhs(rhs_u0_k,   source.f(0*dt))
            rhs_u0_kp1 = self._setup_forward_rhs(rhs_u0_kp1, source.f(1*dt))
            
            # compute u0_kp1 so that we can compute dWaveOp0_k (needed for u1)
            solver.time_step(solver_data_u0, rhs_u0_k, rhs_u0_kp1)
            
            # compute dwaveop_0 (k=0) and allocate space for kp1 (needed for u1 time step)
            dWaveOp0_k = solver.compute_dWaveOp('time', solver_data_u0)         
            dWaveOp0_kp1 = dWaveOp0_k.copy()
            
            solver_data_u0.advance()
            # from here, it makes more sense to refer to rhs_u0 as kp1 and kp2, because those are the values we need 
            # to compute u0_kp2, which is what we need to compute dWaveOp0_kp1
            rhs_u0_kp1, rhs_u0_kp2 = rhs_u0_k, rhs_u0_kp1 # to reuse the allocated space and setup the swap that occurs a few lines down
            
        else:
            solver_data_u0 = None

        for k in xrange(nsteps):
            u0k=wavefield[k]
            
            if k < (nsteps-1):
                u0kp1=wavefield[k+1]
            else:
                u0kp1=wavefield[k]
            u0k=mesh.pad_array(u0k)
            u0kp1=mesh.pad_array(u0kp1)

            uk = solver_data.k.primary_wavefield
            uk_bulk = mesh.unpad_array(uk)
        
            if 'wavefield1' in return_parameters:
                us.append(uk_bulk.copy())
                
            # Record the data at t_k
            if 'simdata' in return_parameters:
                shot.receivers.sample_data_from_array(uk_bulk, k, data=simdata)
            
            # Note, we compute result for k+1 even when k == nsteps-1.  We need
            # it for the time derivative at k=nsteps-1.
            if dWaveOp0 is None:
                # compute u0_kp2 so we can get dWaveOp0_kp1 for the rhs for u1
                rhs_u0_kp1, rhs_u0_kp2 = rhs_u0_kp2, rhs_u0_kp1
                rhs_u0_kp2 = self._setup_forward_rhs(rhs_u0_kp2, source.f((k+2)*dt))
                solver.time_step(solver_data_u0, rhs_u0_kp1, rhs_u0_kp2)
                
                # shift the dWaveOp0's (ok at k=0 because they are equal then)
                dWaveOp0_k, dWaveOp0_kp1 = dWaveOp0_kp1, dWaveOp0_k
                dWaveOp0_kp1 = solver.compute_dWaveOp('time', solver_data_u0)
                
                solver_data_u0.advance()
            else:
                dWaveOp0_k = dWaveOp0[k]
                dWaveOp0_kp1 = dWaveOp0[k+1] if k < (nsteps-1) else dWaveOp0[k] # incase not enough dWaveOp0's are provided, repeat the last one
            
            if 'dWaveOp0' in return_parameters:
                dWaveOp0ret.append(dWaveOp0_k)  

            G0=Lap*u0k
            G1=Lap*u0kp1
            
            if k == 0:
                rhs_k   = G0
                rhs_kp1 = G1
            else:
                rhs_k, rhs_kp1 = rhs_kp1, G1
            
            solver.time_step(solver_data, rhs_k, rhs_kp1)

            # Compute time derivative of p at time k
            if 'dWaveOp1' in return_parameters:
                dWaveOp1.append(solver.compute_dWaveOp('time', solver_data))
        
            # When k is the nth step, the next step is uneeded, so don't swap 
            # any values.  This way, uk at the end is always the final step
            if(k == (nsteps-1)): break
            
            # Don't know what data is needed for the solver, so the solver data
            # handles advancing everything forward by one time step.
            # k-1 <-- k, k <-- k+1, etc
            solver_data.advance()
            
        retval = dict()
        
        if 'wavefield1' in return_parameters:
            retval['wavefield1'] = us
        if 'dWaveOp0' in return_parameters:
            retval['dWaveOp0'] = dWaveOp0ret
        if 'dWaveOp1' in return_parameters:
            retval['dWaveOp1'] = dWaveOp1
        if 'simdata' in return_parameters:
            retval['simdata'] = simdata
        
        return retval
Exemplo n.º 2
0
    def adjoint_model(self, shot, m0, operand_simdata, imaging_period, operand_dWaveOpAdj=None, operand_model=None, return_parameters=[], dWaveOp=None, wavefield=None):
        """Solves for the adjoint field.

        For constant density: m*q_tt - lap q = resid, where m = 1.0/c**2
        For variable density: m1*q_tt - div(m2 grad)q = resid, where m1=1.0/kappa, m2=1.0/rho, and C = (kappa/rho)**0.5


        Parameters
        ----------
        shot : pysit.Shot
            Gives the receiver model for the right hand side.
        operand_simdata : ndarray
            Right hand side component in the data space, usually the residual.
        operand_dWaveOpAdj : list of ndarray
            Right hand side component in the wave equation space, usually something to do with the imaging component...this needs resolving
        operand_simdata : ndarray
            Right hand side component in the data space, usually the residual.
        return_parameters : list of {'adjointfield', 'ic'}
        dWaveOp : ndarray
            Imaging component from the forward model.

        Returns
        -------
        retval : dict
            Dictionary whose keys are return_parameters that contains the specified data.

        Notes
        -----
        * q is the adjoint field.
        * ic is the imaging component.  Because this function computes many of
        the things required to compute the imaging condition, there is an option
        to compute the imaging condition as we go.  This should be used to save
        computational effort.  If the imaging condition is to be computed, the
        optional argument utt must be present.

        Imaging Condition for variable density has components:
            ic.m1 = u_tt * q
            ic.m2 = grad(u) dot grad(q)
        """

        # Local references
        solver = self.solver
        solver.model_parameters = m0

        mesh = solver.mesh

        d = solver.domain
        dt = solver.dt
        nsteps = solver.nsteps
        source = shot.sources

        if 'adjointfield' in return_parameters:
            qs = list()
            vs = list()

        # Storage for the time derivatives of p
        if 'dWaveOpAdj' in return_parameters:
            dWaveOpAdj = list()

        # If we are computing the imaging condition, ensure that all of the parts are there and allocate space.
        if dWaveOp is not None:
            ic = solver.model_parameters.perturbation()
            do_ic = True
        elif 'imaging_condition' in return_parameters:
            raise ValueError('To compute imaging condition, forward component must be specified.')
        else:
            do_ic = False

        # Variable-Density will call this, giving us matrices needed for the ic in terms of m2 (or rho)
        if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
            deltas = [mesh.x.delta,mesh.z.delta]
            sh = mesh.shape(include_bc=True,as_grid=True)
            D1,D2=build_heterogenous_matrices(sh,deltas)

        # Time-reversed wave solver
        solver_data = solver.SolverData()

        rhs_k   = np.zeros(mesh.shape(include_bc=True))
        rhs_km1 = np.zeros(mesh.shape(include_bc=True))

        if operand_model is not None:
            operand_model = operand_model.with_padding()

        # Loop goes over the valid indices backwards
        for k in xrange(nsteps-1, -1, -1): #xrange(int(solver.nsteps)):

            # Local reference
            vk = solver_data.k.primary_wavefield
            vk_bulk = mesh.unpad_array(vk)

            # If we are dealing with variable density, we will need the wavefield to compute the gradient of the objective in terms of m2.
            if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
                uk = mesh.pad_array(wavefield[k])

            # When dpdt is not set, store the current q, otherwise compute the
            # relevant gradient portion
            if 'adjointfield' in return_parameters:
                vs.append(vk_bulk.copy())

            # can maybe speed up by using only the bulk and not unpadding later
            if do_ic:
                if k%imaging_period == 0: #Save every 'imaging_period' number of steps
                    entry = k/imaging_period 
                    # if we are dealing with variable density, we compute 2 parts to the imagaing condition seperatly. Otherwise, if it is just constant density- we compute only 1. 
                    if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
                        ic.kappa += vk*dWaveOp[entry]
                        ic.rho += (D1[0]*uk)*(D1[1]*vk)+(D2[0]*uk)*(D2[1]*vk)
                    else:
                        ic += vk*dWaveOp[entry]

            if k == nsteps-1:
                rhs_k   = self._setup_adjoint_rhs( rhs_k,   shot, k,   operand_simdata, operand_model, operand_dWaveOpAdj)
                rhs_km1 = self._setup_adjoint_rhs( rhs_km1, shot, k-1, operand_simdata, operand_model, operand_dWaveOpAdj)
            else:
                # shift time forward
                rhs_k, rhs_km1 = rhs_km1, rhs_k
            rhs_km1 = self._setup_adjoint_rhs( rhs_km1, shot, k-1, operand_simdata, operand_model, operand_dWaveOpAdj)

            solver.time_step(solver_data, rhs_k, rhs_km1)

            # Compute time derivative of p at time k
            if 'dWaveOpAdj' in return_parameters:
                if k%imaging_period == 0: #Save every 'imaging_period' number of steps
                    dWaveOpAdj.append(solver.compute_dWaveOp('time', solver_data))

            # If k is 0, we don't need results for k-1, so save computation and
            # stop early
            if(k == 0): break

            # Don't know what data is needed for the solver, so the solver data
            # handles advancing everything forward by one time step.
            # k-1 <-- k, k <-- k+1, etc
            solver_data.advance()

        if do_ic:
            ic *= (-1*dt)
            ic *= imaging_period #Compensate for doing fewer summations at higher imaging_period
            ic = ic.without_padding() # gradient is never padded

        retval = dict()

        if 'adjointfield' in return_parameters:
            # List of qs is built in time reversed order, put them in time forward order
            qs = list(vs)
            qs.reverse()
            retval['adjointfield'] = qs
        if 'dWaveOpAdj' in return_parameters:
            dWaveOpAdj.reverse()
            retval['dWaveOpAdj'] = dWaveOpAdj

        if do_ic:
            retval['imaging_condition'] = ic

        return retval
Exemplo n.º 3
0
    def linear_forward_model_rho(self, shot, m0, m1, return_parameters=[], dWaveOp0=None, wavefield=None):
        """Applies the forward model to the model for the given solver in terms of a pertubation of rho.
        
        Parameters
        ----------
        shot : pysit.Shot
            Gives the source signal approximation for the right hand side.
        m0 : solver.ModelParameters
            The parameters upon where to center the linear approximation.
        m1 : solver.ModelParameters
            The parameters upon which to apply the linear forward model to.
        return_parameters : list of {'wavefield1', 'dWaveOp1', 'dWaveOp0', 'simdata'}
            Values to return.
        u0tt : ndarray
            Derivative field required for the imaging condition to be used as right hand side.
        
        
        Returns
        -------
        retval : dict
            Dictionary whose keys are return_parameters that contains the specified data.
        
        Notes
        -----
        * u1 is used as the target field universally.  It could be velocity potential, it could be displacement, it could be pressure.  
        * u1tt is used to generically refer to the derivative of u1 that is needed to compute the imaging condition.
        * If u0tt is not specified, it may be computed on the fly at potentially high expense.
        
        """

        # Local references
        solver = self.solver
        solver.model_parameters = m0 # this updates dt and the number of steps so that is appropriate for the current model
        
        mesh = solver.mesh
        sh=mesh.shape(include_bc=True,as_grid=True)

        d = solver.domain
        dt = solver.dt
        nsteps = solver.nsteps
        source = shot.sources
    
        model_2=1.0/m1.rho
        model_2=mesh.pad_array(model_2)

        #Lap = build_heterogenous_(sh,model_2,[mesh.x.delta,mesh.z.delta])
        rp=dict()
        rp['laplacian']=True
        Lap = build_heterogenous_matrices(sh,[mesh.x.delta,mesh.z.delta],model_2.reshape(-1,),rp=rp)

        # Storage for the field
        if 'wavefield1' in return_parameters:
            us = list()
        
        # Setup data storage for the forward modeled data
        if 'simdata' in return_parameters:
            simdata = np.zeros((solver.nsteps, shot.receivers.receiver_count))

        # Storage for the time derivatives of p
        if 'dWaveOp0' in return_parameters:
            dWaveOp0ret = list()
            
        if 'dWaveOp1' in return_parameters:
            dWaveOp1 = list()
        
        # Step k = 0
        # p_0 is a zero array because if we assume the input signal is causal
        # and we assume that the initial system (i.e., p_(-2) and p_(-1)) is
        # uniformly zero, then the leapfrog scheme would compute that p_0 = 0 as
        # well. ukm1 is needed to compute the temporal derivative.
        solver_data = solver.SolverData()
        
        if dWaveOp0 is None:
            solver_data_u0 = solver.SolverData()
            
            # For u0, set up the right hand sides
            rhs_u0_k   = np.zeros(mesh.shape(include_bc=True))
            rhs_u0_kp1 = np.zeros(mesh.shape(include_bc=True))              
            rhs_u0_k   = self._setup_forward_rhs(rhs_u0_k,   source.f(0*dt))
            rhs_u0_kp1 = self._setup_forward_rhs(rhs_u0_kp1, source.f(1*dt))
            
            # compute u0_kp1 so that we can compute dWaveOp0_k (needed for u1)
            solver.time_step(solver_data_u0, rhs_u0_k, rhs_u0_kp1)
            
            # compute dwaveop_0 (k=0) and allocate space for kp1 (needed for u1 time step)
            dWaveOp0_k = solver.compute_dWaveOp('time', solver_data_u0)         
            dWaveOp0_kp1 = dWaveOp0_k.copy()
            
            solver_data_u0.advance()
            # from here, it makes more sense to refer to rhs_u0 as kp1 and kp2, because those are the values we need 
            # to compute u0_kp2, which is what we need to compute dWaveOp0_kp1
            rhs_u0_kp1, rhs_u0_kp2 = rhs_u0_k, rhs_u0_kp1 # to reuse the allocated space and setup the swap that occurs a few lines down
            
        else:
            solver_data_u0 = None

        for k in xrange(nsteps):
            u0k=wavefield[k]
            
            if k < (nsteps-1):
                u0kp1=wavefield[k+1]
            else:
                u0kp1=wavefield[k]
            u0k=mesh.pad_array(u0k)
            u0kp1=mesh.pad_array(u0kp1)

            uk = solver_data.k.primary_wavefield
            uk_bulk = mesh.unpad_array(uk)
        
            if 'wavefield1' in return_parameters:
                us.append(uk_bulk.copy())
                
            # Record the data at t_k
            if 'simdata' in return_parameters:
                shot.receivers.sample_data_from_array(uk_bulk, k, data=simdata)
            
            # Note, we compute result for k+1 even when k == nsteps-1.  We need
            # it for the time derivative at k=nsteps-1.
            if dWaveOp0 is None:
                # compute u0_kp2 so we can get dWaveOp0_kp1 for the rhs for u1
                rhs_u0_kp1, rhs_u0_kp2 = rhs_u0_kp2, rhs_u0_kp1
                rhs_u0_kp2 = self._setup_forward_rhs(rhs_u0_kp2, source.f((k+2)*dt))
                solver.time_step(solver_data_u0, rhs_u0_kp1, rhs_u0_kp2)
                
                # shift the dWaveOp0's (ok at k=0 because they are equal then)
                dWaveOp0_k, dWaveOp0_kp1 = dWaveOp0_kp1, dWaveOp0_k
                dWaveOp0_kp1 = solver.compute_dWaveOp('time', solver_data_u0)
                
                solver_data_u0.advance()
            else:
                dWaveOp0_k = dWaveOp0[k]
                dWaveOp0_kp1 = dWaveOp0[k+1] if k < (nsteps-1) else dWaveOp0[k] # incase not enough dWaveOp0's are provided, repeat the last one
            
            if 'dWaveOp0' in return_parameters:
                dWaveOp0ret.append(dWaveOp0_k)  

            G0=Lap*u0k
            G1=Lap*u0kp1
            
            if k == 0:
                rhs_k   = G0
                rhs_kp1 = G1
            else:
                rhs_k, rhs_kp1 = rhs_kp1, G1
            
            solver.time_step(solver_data, rhs_k, rhs_kp1)

            # Compute time derivative of p at time k
            if 'dWaveOp1' in return_parameters:
                dWaveOp1.append(solver.compute_dWaveOp('time', solver_data))
        
            # When k is the nth step, the next step is uneeded, so don't swap 
            # any values.  This way, uk at the end is always the final step
            if(k == (nsteps-1)): break
            
            # Don't know what data is needed for the solver, so the solver data
            # handles advancing everything forward by one time step.
            # k-1 <-- k, k <-- k+1, etc
            solver_data.advance()
            
        retval = dict()
        
        if 'wavefield1' in return_parameters:
            retval['wavefield1'] = us
        if 'dWaveOp0' in return_parameters:
            retval['dWaveOp0'] = dWaveOp0ret
        if 'dWaveOp1' in return_parameters:
            retval['dWaveOp1'] = dWaveOp1
        if 'simdata' in return_parameters:
            retval['simdata'] = simdata
        
        return retval
Exemplo n.º 4
0
    def adjoint_model(self, shot, m0, operand_simdata, imaging_period, operand_dWaveOpAdj=None, operand_model=None, return_parameters=[], dWaveOp=None, wavefield=None):
        """Solves for the adjoint field.

        For constant density: m*q_tt - lap q = resid, where m = 1.0/c**2
        For variable density: m1*q_tt - div(m2 grad)q = resid, where m1=1.0/kappa, m2=1.0/rho, and C = (kappa/rho)**0.5

        Parameters
        ----------
        shot : pysit.Shot
            Gives the receiver model for the right hand side.
        operand_simdata : ndarray
            Right hand side component in the data space, usually the residual.
        operand_dWaveOpAdj : list of ndarray
            Right hand side component in the wave equation space, usually something to do with the imaging component...this needs resolving
        operand_simdata : ndarray
            Right hand side component in the data space, usually the residual.
        return_parameters : list of {'adjointfield', 'ic'}
        dWaveOp : ndarray
            Imaging component from the forward model.

        Returns
        -------
        retval : dict
            Dictionary whose keys are return_parameters that contains the specified data.

        Notes
        -----
        * q is the adjoint field.
        * ic is the imaging component.  Because this function computes many of
        the things required to compute the imaging condition, there is an option
        to compute the imaging condition as we go.  This should be used to save
        computational effort.  If the imaging condition is to be computed, the
        optional argument utt must be present.

        Imaging Condition for variable density has components:
            ic.m1 = u_tt * q
            ic.m2 = grad(u) dot grad(q)
        """

        # Local references
        solver = self.solver
        solver.model_parameters = m0

        mesh = solver.mesh

        d = solver.domain
        dt = solver.dt
        nsteps = solver.nsteps
        source = shot.sources

        if 'adjointfield' in return_parameters:
            qs = list()
            vs = list()

        # Storage for the time derivatives of p
        if 'dWaveOpAdj' in return_parameters:
            dWaveOpAdj = list()

        # If we are computing the imaging condition, ensure that all of the parts are there and allocate space.
        if dWaveOp is not None:
            ic = solver.model_parameters.perturbation()
            do_ic = True
        elif 'imaging_condition' in return_parameters:
            raise ValueError('To compute imaging condition, forward component must be specified.')
        else:
            do_ic = False

        # Variable-Density will call this, giving us matrices needed for the ic in terms of m2 (or rho)
        if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
            deltas = [mesh.x.delta,mesh.z.delta]
            sh = mesh.shape(include_bc=True,as_grid=True)
            D1,D2=build_heterogenous_matrices(sh,deltas)

        # Time-reversed wave solver
        solver_data = solver.SolverData()

        rhs_k   = np.zeros(mesh.shape(include_bc=True))
        rhs_km1 = np.zeros(mesh.shape(include_bc=True))

        if operand_model is not None:
            operand_model = operand_model.with_padding()

        # Loop goes over the valid indices backwards
        for k in xrange(nsteps-1, -1, -1): #xrange(int(solver.nsteps)):

            # Local reference
            vk = solver_data.k.primary_wavefield
            vk_bulk = mesh.unpad_array(vk)
            
            # If we are dealing with variable density, we will need the wavefield to compute the gradient of the objective in terms of m2.
            if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
                uk = mesh.pad_array(wavefield[k])

            # When dpdt is not set, store the current q, otherwise compute the
            # relevant gradient portion
            if 'adjointfield' in return_parameters:
                vs.append(vk_bulk.copy())

            # can maybe speed up by using only the bulk and not unpadding later
            if do_ic:
                if k%imaging_period == 0: #Save every 'imaging_period' number of steps
                    entry = k/imaging_period 
                # if we are dealing with variable density, we compute 2 parts to the imagaing condition seperatly. Otherwise, if it is just constant density- we compute only 1. 
                    if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
                        ic.kappa += vk*dWaveOp[entry]
                        ic.rho += (D1[0]*uk)*(D1[1]*vk)+(D2[0]*uk)*(D2[1]*vk)
                    else:
                        ic += vk*dWaveOp[entry]

            if k == nsteps-1:
                rhs_k   = self._setup_adjoint_rhs( rhs_k,   shot, k,   operand_simdata, operand_model, operand_dWaveOpAdj)
                rhs_km1 = self._setup_adjoint_rhs( rhs_km1, shot, k-1, operand_simdata, operand_model, operand_dWaveOpAdj)
            else:
                # shift time forward
                rhs_k, rhs_km1 = rhs_km1, rhs_k
            rhs_km1 = self._setup_adjoint_rhs( rhs_km1, shot, k-1, operand_simdata, operand_model, operand_dWaveOpAdj)

            solver.time_step(solver_data, rhs_k, rhs_km1)

            # Compute time derivative of p at time k
            if 'dWaveOpAdj' in return_parameters:
                if k%imaging_period == 0: #Save every 'imaging_period' number of steps
                    dWaveOpAdj.append(solver.compute_dWaveOp('time', solver_data))

            # If k is 0, we don't need results for k-1, so save computation and
            # stop early
            if(k == 0): break

            # Don't know what data is needed for the solver, so the solver data
            # handles advancing everything forward by one time step.
            # k-1 <-- k, k <-- k+1, etc
            solver_data.advance()

        if do_ic:
            ic *= (-1*dt)
            ic *= imaging_period #Compensate for doing fewer summations at higher imaging_period
            ic = ic.without_padding() # gradient is never padded

        retval = dict()

        if 'adjointfield' in return_parameters:
            # List of qs is built in time reversed order, put them in time forward order
            qs = list(vs)
            qs.reverse()
            retval['adjointfield'] = qs
        if 'dWaveOpAdj' in return_parameters:
            dWaveOpAdj.reverse()
            retval['dWaveOpAdj'] = dWaveOpAdj

        if do_ic:
            retval['imaging_condition'] = ic

        return retval
Exemplo n.º 5
0
    def linear_forward_model_rho(self, shot, m0, m1, frequencies, return_parameters=[], dWaveOp0=None, wavefield=None):
        """Applies the forward model to the model for the given solver in terms of a pertubation of rho
        
        Parameters
        ----------
        shot : pysit.Shot
            Gives the source signal approximation for the right hand side.
        m1 : solver.ModelParameters
        frequencies : list of 2-tuples
            2-tuple, first element is the frequency to use, second element the weight.
        return_parameters : list of {'dWaveOp0', 'wavefield1', 'dWaveOp1', 'simdata', 'simdata_time'}, optional
            Values to return.
        
        
        Returns
        -------
        retval : dict
            Dictionary whose keys are return_parameters that contains the specified data.
        
        Notes
        -----
        * u1 is used as the target field universally.  It could be velocity potential, it could be displacement, it could be pressure.  
        * u1tt is used to generically refer to the derivative of u1 that is needed to compute the imaging condition.
        * If u0tt is not specified, it may be computed on the fly at potentially high expense.
        
        """
        # Sanitize the input
        if not np.iterable(frequencies):
            frequencies = [frequencies] 
            
        # Local references
        solver = self.solver
        solver.model_parameters = m0 # this updates dt and the number of steps so that is appropriate for the current model
        
        mesh = solver.mesh
        sh = mesh.shape(include_bc=True,as_grid=True)

        d = solver.domain
        source = shot.sources

        model_2=1.0/m1.rho
        model_2=mesh.pad_array(model_2)

        rp=dict()
        rp['laplacian']=True
        Lap = build_heterogenous_matrices(sh,[mesh.x.delta,mesh.z.delta],model_2.reshape(-1,),rp=rp)
        # Storage for the field     
        u1hats = dict()
        
        # Setup data storage for the forward modeled data
        if 'simdata' in return_parameters:
            simdata = dict()

        # Storage for the time derivatives of p
        if 'dWaveOp0' in return_parameters:
            dWaveOp0ret = dict()

        # Storage for the time derivatives of p
        if 'dWaveOp1' in return_parameters:
            dWaveOp1 = dict()
                
        if dWaveOp0 is None:
            solver_data_u0 = solver.SolverData()
            
        solver_data = solver.SolverData()
        
        rhs = solver.WavefieldVector(mesh,dtype=solver.dtype)
        
        for nu in frequencies:
            
            u0_hat=wavefield[nu]
            u0_hat=mesh.pad_array(u0_hat)

            if dWaveOp0 is None:
                rhs = solver.build_rhs(mesh.pad_array(source.f(nu=nu)), rhs_wavefieldvector=rhs)
                solver.solve(solver_data_u0, rhs, nu)
                u0hat = solver_data_u0.k.primary_wavefield
                dWaveOp0_nu = solver.compute_dWaveOp('frequency', u0hat, nu)
            else:
                dWaveOp0_nu = dWaveOp0[nu]
                
            if 'dWaveOp0' in return_parameters:
                dWaveOp0ret[nu] = dWaveOp0_nu
                

            rhs_ = Lap*u0_hat
            rhs = solver.build_rhs(rhs_, rhs_wavefieldvector=rhs) # make the rhs vector the correct length
            solver.solve(solver_data,rhs,nu)
            
            u1hat = solver_data.k.primary_wavefield
            
            # Store the wavefield
            if 'wavefield1' in return_parameters:
                u1hats[nu] = mesh.unpad_array(u1hat, copy=True)
            
            # Compute the derivative
            if 'dWaveOp1' in return_parameters:
                dWaveOp1[nu] = solver.compute_dWaveOp('frequency', u1hat, nu)
            
            # Extract the data
            if 'simdata' in return_parameters:
                simdata[nu] = shot.receivers.sample_data_from_array(mesh.unpad_array(u1hat))

        retval = dict()
        
        if 'dWaveOp0' in return_parameters:
            retval['dWaveOp0'] = dWaveOp0ret
        if 'wavefield1' in return_parameters:
            retval['wavefield1'] = u1hats
        if 'dWaveOp1' in return_parameters:
            retval['dWaveOp1'] = dWaveOp1
        if 'simdata' in return_parameters:
            retval['simdata'] = simdata
        return retval
Exemplo n.º 6
0
    def adjoint_model(self, shot, m0,
                           operand_simdata, frequencies,
                           operand_dWaveOpAdj=None, operand_model=None,
                           frequency_weights=None,
                           return_parameters=[],
                           dWaveOp=None, wavefield=None):
        """Solves for the adjoint field in frequency.

        For constant density: -m*(omega**2)*q - lap q = resid, where m = 1.0/c**2
        For variable density: -m1*(omega**2)*q - div(m2 grad)q = resid, where m1=1.0/kappa, m2=1.0/rho, and C = (kappa/rho)**0.5

        Parameters
        ----------
        shot : pysit.Shot
            Gives the receiver model for the right hand side.
        operand : ndarray
            Right hand side, usually the residual.
        frequencies : list of 2-tuples
            2-tuple, first element is the frequency to use, second element the weight.
        return_parameters : list of {'q', 'qhat', 'ic'}
        dWaveOp : ndarray
            Imaging component from the forward model (in frequency).

        Returns
        -------
        retval : dict
            Dictionary whose keys are return_parameters that contains the specified data.

        Notes
        -----
        * q is the adjoint field.
        * qhat is the DFT of oq at the specified frequencies
        * ic is the imaging component.  Because this function computes many of
        the things required to compute the imaging condition, there is an option
        to compute the imaging condition as we go.  This should be used to save
        computational effort.  If the imaging condition is to be computed, the
        optional argument utt must be present.

        Imaging Condition for variable density has terms:
            ic.m1 = omegas**2 * conj(u) * q
            ic.m2 = conj(grad(u)) dot grad(q), summed over all shots and frequencies.
        """

        # Sanitize the input
        if not np.iterable(frequencies):
            frequencies = [frequencies]

        # Local references
        solver = self.solver
        solver.model_parameters = m0

        mesh = solver.mesh

        d = solver.domain
        source = shot.sources

        # Sanitize the input
        if not np.iterable(frequencies):
            frequencies = [frequencies]

        qhats = dict()

        if 'dWaveOpAdj' in return_parameters:
            dWaveOpAdj = dict()

        # If we are computing the imaging condition, ensure that all of the parts are there.
        if dWaveOp is None and 'imaging_condition' in return_parameters:
            raise ValueError('To compute imaging condition, forward component must be specified.')

        if 'imaging_condition' in return_parameters:
            ic = solver.model_parameters.perturbation(dtype=np.complex)

            if frequency_weights is None:
                frequency_weights = itertools.repeat(1.0)

            freq_weights = {nu: weight for nu,weight in zip(frequencies,frequency_weights)}

        # if we are dealing with variable density, we need to collect the gradient operators, D1 and D2. (note: D2 is the negative adjoint of the leftmost gradient used in our heterogenous laplacian)
        if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
            deltas = [mesh.x.delta,mesh.z.delta]
            sh = mesh.shape(include_bc=True,as_grid=True)
            D1, D2 = build_heterogenous_matrices(sh,deltas)

        if operand_model is not None:
            operand_model = operand_model.with_padding()

        # Time-reversed wave solver
        solver_data = solver.SolverData()
        rhs = solver.WavefieldVector(mesh,dtype=solver.dtype)
        for nu in frequencies:
            
            # If we are dealing with variable density, we will need these values computed for the imagining condition in terms of m2.
            if hasattr(m0, 'kappa') and hasattr(m0,'rho'):
                uhat = wavefield[nu]
                uhat = mesh.pad_array(uhat)
                D1u, D2u = np.conj(D1[0]*uhat), np.conj(D2[0]*uhat)  # Need the conj. of grad (uhat)

            # Compute the rhs array.
            rhs_ = mesh.pad_array(shot.receivers.extend_data_to_array(data=operand_simdata[nu])) # for primary adjoint equation
            if (operand_dWaveOpAdj is not None) and (operand_model is not None):
                dWaveOpAdj_nu = operand_dWaveOpAdj[nu]
                rhs_ += reshape(operand_model*dWaveOpAdj_nu.reshape(operand_model.shape), rhs_.shape) # for secondary adjoint equation

            rhs = solver.build_rhs(rhs_, rhs_wavefieldvector=rhs)

            np.conj(rhs.data, rhs.data)
            result = solver.solve(solver_data, rhs, nu)

            vhat = solver_data.k.primary_wavefield
            # Compute the conjugate in place.
            # After this operation, vhats _is_ conjugated, so its value does not
            # match the mathematics.  This is done to save computation, as computing
            # the conjufation in place requires no further allocation.  vhats should
            # not be used beyond this point, so it is assigned to None.
            qhat = np.conj(vhat, vhat)

            if 'adjointfield' in return_parameters:
                qhats[nu] = mesh.unpad_array(qhat, copy=True)

            if 'dWaveOpAdj' in return_parameters:
                dWaveOpAdj[nu] = solver.compute_dWaveOp('frequency', qhat,nu)

            # If the imaging component needs to be computed, do it
            if 'imaging_condition' in return_parameters:
                weight = freq_weights[nu]
                # if we are dealing with variable density, we compute 2 parts to the imagaing condition seperatly. Otherwise, if it is just constant density- we compute only 1. 
                if hasattr(m0, 'kappa') and hasattr(m0, 'rho'):
                    ic.rho -= weight*((D1u)*(D1[1]*qhat)+(D2u)*(D2[1]*qhat))
                    ic.kappa -= weight*qhat*np.conj(dWaveOp[nu])
                else:
                    ic -= weight*qhat*np.conj(dWaveOp[nu]) # note, no dnu here because the nus are not generally the complete set, so dnu makes little sense, otherwise dnu = 1./(nsteps*dt)

        retval = dict()

        if 'adjointfield' in return_parameters:
            retval['adjointfield'] = qhats

        if 'dWaveOpAdj' in return_parameters:
            retval['dWaveOpAdj'] = dWaveOpAdj

        # If the imaging component needs to be computed, do it
        if 'imaging_condition' in return_parameters:
            retval['imaging_condition'] = ic.without_padding()

        return retval