Пример #1
0
    def _initialize_bcs(self, bcs="damp"):
        # Create dampening field as symbol `damp`
        if self.nbl == 0:
            self.damp = 1 if bcs == "mask" else 0
            return

        # First initialization
        init = self.damp is None
        # Get current Function if alread yinitialized
        self.damp = self.damp or Function(name="damp", grid=self.grid)
        if callable(bcs):
            bcs(self.damp, self.nbl)
        else:
            re_init = ((bcs == "mask" and mmin(self.damp) == 0)
                       or (bcs == "damp" and mmax(self.damp) == 1))
            if init or re_init:
                if re_init and not init:
                    bcs_o = "damp" if bcs == "mask" else "mask"
                    warning("Re-initializing damp profile from %s to %s" %
                            (bcs_o, bcs))
                    warning("Model has to be created with `bcs=\"%s\"`"
                            "for this WaveSolver" % bcs)
                initialize_damp(self.damp,
                                self.padsizes,
                                self.spacing,
                                abc_type=bcs,
                                fs=self.fs)
        self._physical_parameters.update(['damp'])
Пример #2
0
def run(problem, **kwargs):
    """
    A single run with a specific set of performance parameters.
    """
    setup = model_type[problem]['setup']
    options = {}

    time_order = kwargs.pop('time_order')[0]
    space_order = kwargs.pop('space_order')[0]
    autotune = kwargs.pop('autotune')
    block_shapes = as_tuple(kwargs.pop('block_shape'))

    # Should a specific block-shape be used? Useful if one wants to skip
    # the autotuning pass as a good block-shape is already known
    if block_shapes:
        if autotune:
            warning("Skipping autotuning (using explicit block-shape `%s`)"
                    % str(block_shapes))
            autotune = False
        # This is horribly hacky, but it works for now
        for i, bs in enumerate(block_shapes):
            for d, s in zip(['x', 'y', 'z'], bs):
                options['%s0_blk%d_size' % (d, i)] = s

    solver = setup(space_order=space_order, time_order=time_order, **kwargs)
    solver.forward(autotune=autotune, **options)
Пример #3
0
def run(problem, **kwargs):
    """
    A single run with a specific set of performance parameters.
    """
    setup = tti_setup if problem == 'tti' else acoustic_setup
    options = {}

    time_order = kwargs.pop('time_order')[0]
    space_order = kwargs.pop('space_order')[0]
    autotune = kwargs.pop('autotune')

    # Should a specific block-shape be used? Useful if one wants to skip
    # the autotuning pass as a good block-shape is already known
    block_shape = as_tuple(kwargs.pop('block_shape'))
    if all(block_shape):
        if autotune:
            warning("Skipping autotuning (using explicit block-shape `%s`)"
                    % str(block_shape))
            autotune = False
        # This is quite hacky, but it does the trick
        for d, bs in zip(['x', 'y', 'z'], block_shape):
            options['%s0_blk_size' % d] = bs

    solver = setup(space_order=space_order, time_order=time_order, **kwargs)
    solver.forward(autotune=autotune, **options)
Пример #4
0
def run(problem, **kwargs):
    """
    A single run with a specific set of performance parameters.
    """
    setup = tti_setup if problem == 'tti' else acoustic_setup
    options = {}

    time_order = kwargs.pop('time_order')[0]
    space_order = kwargs.pop('space_order')[0]
    autotune = kwargs.pop('autotune')

    # Should a specific block-shape be used? Useful if one wants to skip
    # the autotuning pass as a good block-shape is already known
    block_shape = as_tuple(kwargs.pop('block_shape'))
    if all(block_shape):
        if autotune:
            warning("Skipping autotuning (using explicit block-shape `%s`)" %
                    str(block_shape))
            autotune = False
        # This is quite hacky, but it does the trick
        for d, bs in zip(['x', 'y', 'z'], block_shape):
            options['%s0_blk_size' % d] = bs

    solver = setup(space_order=space_order, time_order=time_order, **kwargs)
    solver.forward(autotune=autotune, **options)
Пример #5
0
 def convert(self, value, param, ctx):
     n_value = len(value)
     n_type = len(self.types)
     if n_value <= n_type:
         warning(f"Processing {n_value} out of expected up to {n_type}")
     else:
         super().convert(value, param, ctx)
     return tuple(self.types[i](value[i], param, ctx)
                  for i in range(n_value))
Пример #6
0
 def new_src(self, name='src', src_type='self'):
     if self.src_type is None or src_type is None:
         warning("No surce type defined, returning uninistiallized (zero) source")
         return PointSource(name=name, grid=self.grid,
                            time_range=self.time_axis, npoint=self.nsrc,
                            coordinates=self.src_positions)
     else:
         return sources[self.src_type](name=name, grid=self.grid, f0=self.f0,
                                       time_range=self.time_axis, npoint=self.nsrc,
                                       coordinates=self.src_positions,
                                       t0=self._t0w, a=self._a)
Пример #7
0
 def adj_src(self):
     if self.src_type is None:
         warning("No source type defined, returning uninitiallized (zero) shot record")
         return self.new_rec()
     adj_src = sources[self.src_type](name='rec', grid=self.grid, f0=self.f0,
                                      time_range=self.time_axis, npoint=self.nrec,
                                      coordinates=self.rec_positions,
                                      t0=self._t0w, a=self._a)
     # Revert time axis to have a proper shot record and not compute on zeros
     for i in range(self.nrec):
         adj_src.data[:, i] = adj_src.wavelet[::-1]
     return adj_src
Пример #8
0
    def __init__(self, model, geometry, space_order=4, **kwargs):
        self.model = model
        self.geometry = geometry

        if space_order % 2 != 0:
            raise ValueError("space_order must be even but got %s" %
                             space_order)

        if space_order % 4 != 0:
            warning("It is recommended for space_order to be a multiple of 4" +
                    "but got %s" % space_order)

        self.space_order = space_order

        # Cache compiler options
        self._kwargs = kwargs
Пример #9
0
 def config_autotuning(ctx, param, value):
     """Setup auto-tuning to run in ``{basic,aggressive,...}+preemptive`` mode."""
     if value != 'off':
         # Sneak-peek at the `block-shape` -- if provided, keep auto-tuning off
         if ctx.params['block_shape']:
             warning("Skipping autotuning (using explicit block-shape `%s`)"
                     % str(ctx.params['block_shape']))
             level = False
         else:
             # Make sure to always run in preemptive mode
             configuration['autotuning'] = [value, 'preemptive']
             # We apply blocking to all parallel loops, including the innermost ones
             configuration['dle-options']['blockinner'] = True
             level = value
     else:
         level = False
     return level
Пример #10
0
 def config_autotuning(ctx, param, value):
     """Setup auto-tuning to run in ``{basic,aggressive,...}+preemptive`` mode."""
     if value != 'off':
         # Sneak-peek at the `block-shape` -- if provided, keep auto-tuning off
         if ctx.params['block_shape']:
             warning("Skipping autotuning (using explicit block-shape `%s`)"
                     % str(ctx.params['block_shape']))
             level = False
         else:
             # Make sure to always run in preemptive mode
             configuration['autotuning'] = [value, 'preemptive']
             # We apply blocking to all parallel loops, including the innermost ones
             # Note: see https://github.com/devitocodes/devito/issues/320 for why
             # we use blockinner=True only if the backend compiler is Intel
             flag = isinstance(configuration['compiler'], IntelCompiler)
             configuration['opt-options']['blockinner'] = flag
             level = value
     else:
         level = False
     return level
Пример #11
0
    def __init__(self,
                 model,
                 geometry,
                 space_order=4,
                 kernel='centered',
                 **kwargs):
        self.model = model
        self.model._initialize_bcs(bcs="damp")
        self.geometry = geometry
        self.kernel = kernel

        if space_order % 2 != 0:
            raise ValueError("space_order must be even but got %s" %
                             space_order)

        if space_order % 4 != 0:
            warning("It is recommended for space_order to be a multiple of 4" +
                    "but got %s" % space_order)

        self.space_order = space_order

        # Cache compiler options
        self._kwargs = kwargs
Пример #12
0
 def model(self):
     warning("Model is kept for backward compatibility but should not be"
             "obtained from the geometry")
     return self._model
Пример #13
0
from devito import warning

warning(
    """The location of Devito's checkpointing has changed. This location will be
           deprecated soon. Please change your imports to 'from devito import
           DevitoCheckpoint, CheckpointOperato'""")

from devito.checkpointing import *  # noqa
Пример #14
0
def plot(problem, **kwargs):
    """
    Plotting mode to generate plots for performance analysis.
    """
    backend = kwargs.pop('backend')
    resultsdir = kwargs.pop('resultsdir')
    max_bw = kwargs.pop('max_bw')
    flop_ceils = kwargs.pop('flop_ceil')
    point_runtime = kwargs.pop('point_runtime')

    arch = kwargs['arch']
    space_order = "[%s]" % ",".join(str(i) for i in kwargs['space_order'])
    time_order = kwargs['time_order']
    shape = "[%s]" % ",".join(str(i) for i in kwargs['shape'])

    RooflinePlotter = get_ob_plotter()
    bench = get_ob_bench(problem, resultsdir, kwargs)

    bench.load()
    if not bench.loaded:
        warning("Could not load any results, nothing to plot. Exiting...")
        sys.exit(0)

    gflopss = bench.lookup(params=kwargs, measure="gflopss", event='main')
    oi = bench.lookup(params=kwargs, measure="oi", event='main')
    time = bench.lookup(params=kwargs, measure="timings", event='main')

    # What plot am I?
    modes = [i for i in ['dse', 'dle', 'autotune']
             if len(set(dict(j)[i] for j in gflopss)) > 1]

    # Filename
    figname = "%s_dim%s_so%s_to%s_arch[%s]_bkend[%s].pdf" % (
        problem, shape, space_order, time_order, arch, backend
    )

    # Legend setup. Do not plot a legend if there's no variation in performance
    # options (dse, dle, autotune)
    if modes:
        legend = {'loc': 'upper left', 'fontsize': 7, 'ncol': 4}
    else:
        legend = 'drop'

    avail_colors = ['r', 'g', 'b', 'y', 'k', 'm']
    avail_markers = ['o', 'x', '^', 'v', '<', '>']

    used_colors = {}
    used_markers = {}

    # Find min and max runtimes for instances having the same OI
    min_max = {v: [0, sys.maxsize] for v in oi.values()}
    for k, v in time.items():
        i = oi[k]
        min_max[i][0] = v if min_max[i][0] == 0 else min(v, min_max[i][0])
        min_max[i][1] = v if min_max[i][1] == sys.maxsize else max(v, min_max[i][1])

    with RooflinePlotter(figname=figname, plotdir=resultsdir,
                         max_bw=max_bw, flop_ceils=flop_ceils,
                         fancycolor=True, legend=legend) as plot:
        for k, v in gflopss.items():
            so = dict(k)['space_order']

            oi_value = oi[k]
            time_value = time[k]

            run = tuple(dict(k)[i] for i in modes)
            label = ("<%s>" % ','.join(run)) if run else None

            color = used_colors[run] if run in used_colors else avail_colors.pop(0)
            used_colors.setdefault(run, color)
            marker = used_markers[so] if so in used_markers else avail_markers.pop(0)
            used_markers.setdefault(so, marker)

            oi_loc = 0.076 if len(str(so)) == 1 else 0.09
            oi_annotate = {'s': 'SO=%s' % so, 'size': 6, 'xy': (oi_value, oi_loc)}
            if time_value in min_max[oi_value] and point_runtime:
                # Only annotate min and max runtimes on each OI line, to avoid
                # polluting the plot too much
                point_annotate = {'s': "%.0fs" % time_value, 'xytext': (0.0, 5.5),
                                  'size': 6, 'rotation': 0}
            else:
                point_annotate = None
            oi_line = time_value == min_max[oi_value][0]
            if oi_line:
                perf_annotate = {'size': 6, 'xytext': (-4, 5)}

            plot.add_point(gflops=v, oi=oi_value, marker=marker, color=color,
                           oi_line=oi_line, label=label, perf_annotate=perf_annotate,
                           oi_annotate=oi_annotate, point_annotate=point_annotate)
Пример #15
0
    def __init__(self, origin, spacing, shape, space_order, vp, nbpml=20,
                 dtype=np.float32, epsilon=None, delta=None, theta=None, phi=None,
                 subdomains=(), **kwargs):
        super(Model, self).__init__(origin, spacing, shape, space_order, nbpml, dtype,
                                    subdomains)

        # Create square slowness of the wave as symbol `m`
        if isinstance(vp, np.ndarray):
            self.m = Function(name="m", grid=self.grid, space_order=space_order)
        else:
            self.m = Constant(name="m", value=1/vp**2)
        self._physical_parameters = ('m',)
        # Set model velocity, which will also set `m`
        self.vp = vp

        # Create dampening field as symbol `damp`
        self.damp = Function(name="damp", grid=self.grid)
        initialize_damp(self.damp, self.nbpml, self.spacing)

        # Additional parameter fields for TTI operators
        self.scale = 1.

        if epsilon is not None:
            if isinstance(epsilon, np.ndarray):
                self._physical_parameters += ('epsilon',)
                self.epsilon = Function(name="epsilon", grid=self.grid)
                initialize_function(self.epsilon, 1 + 2 * epsilon, self.nbpml)
                # Maximum velocity is scale*max(vp) if epsilon > 0
                if mmax(self.epsilon) > 0:
                    self.scale = np.sqrt(mmax(self.epsilon))
            else:
                self.epsilon = 1 + 2 * epsilon
                self.scale = epsilon
        else:
            self.epsilon = 1

        if delta is not None:
            if isinstance(delta, np.ndarray):
                self._physical_parameters += ('delta',)
                self.delta = Function(name="delta", grid=self.grid)
                initialize_function(self.delta, np.sqrt(1 + 2 * delta), self.nbpml)
            else:
                self.delta = delta
        else:
            self.delta = 1

        if theta is not None:
            if isinstance(theta, np.ndarray):
                self._physical_parameters += ('theta',)
                self.theta = Function(name="theta", grid=self.grid,
                                      space_order=space_order)
                initialize_function(self.theta, theta, self.nbpml)
            else:
                self.theta = theta
        else:
            self.theta = 0

        if phi is not None:
            if self.grid.dim < 3:
                warning("2D TTI does not use an azimuth angle Phi, ignoring input")
                self.phi = 0
            elif isinstance(phi, np.ndarray):
                self._physical_parameters += ('phi',)
                self.phi = Function(name="phi", grid=self.grid, space_order=space_order)
                initialize_function(self.phi, phi, self.nbpml)
            else:
                self.phi = phi
        else:
            self.phi = 0
Пример #16
0
            gflopss, oi, timings, _ = self.func(*args, **kwargs)

            for key in timings.keys():
                self.register(gflopss[key], measure="gflopss", event=key.name)
                self.register(oi[key], measure="oi", event=key.name)
                self.register(timings[key], measure="timings", event=key.name)

    return DevitoExecutor(func)


if __name__ == "__main__":
    # If running with MPI, we emit logging messages from rank0 only
    try:
        MPI.Init()  # Devito starts off with MPI disabled!
        set_log_level('DEBUG', comm=MPI.COMM_WORLD)

        if MPI.COMM_WORLD.size > 1 and not configuration['mpi']:
            warning(
                "It seems that you're running over MPI with %d processes, but "
                "DEVITO_MPI is unset. Setting `DEVITO_MPI=basic`..." %
                MPI.COMM_WORLD.size)
            configuration['mpi'] = 'basic'
    except TypeError:
        # MPI not available
        pass

    # Profiling at max level
    configuration['profiling'] = 'advanced'

    benchmark()
Пример #17
0
    def __init__(self,
                 origin,
                 spacing,
                 shape,
                 space_order,
                 vp,
                 nbpml=20,
                 dtype=np.float32,
                 epsilon=None,
                 delta=None,
                 theta=None,
                 phi=None,
                 subdomains=(),
                 **kwargs):
        super(Model, self).__init__(origin, spacing, shape, space_order, nbpml,
                                    dtype, subdomains)

        # Create square slowness of the wave as symbol `m`
        if isinstance(vp, np.ndarray):
            self.m = Function(name="m",
                              grid=self.grid,
                              space_order=space_order)
        else:
            self.m = Constant(name="m", value=1 / vp**2)
        self._physical_parameters = ('m', )
        # Set model velocity, which will also set `m`
        self.vp = vp

        # Create dampening field as symbol `damp`
        self.damp = Function(name="damp", grid=self.grid)
        initialize_damp(self.damp, self.nbpml, self.spacing)

        # Additional parameter fields for TTI operators
        self.scale = 1.

        if epsilon is not None:
            if isinstance(epsilon, np.ndarray):
                self._physical_parameters += ('epsilon', )
                self.epsilon = Function(name="epsilon", grid=self.grid)
                initialize_function(self.epsilon, 1 + 2 * epsilon, self.nbpml)
                # Maximum velocity is scale*max(vp) if epsilon > 0
                if np.max(self.epsilon.data_with_halo[:]) > 0:
                    self.scale = np.sqrt(np.max(
                        self.epsilon.data_with_halo[:]))
            else:
                self.epsilon = 1 + 2 * epsilon
                self.scale = epsilon
        else:
            self.epsilon = 1

        if delta is not None:
            if isinstance(delta, np.ndarray):
                self._physical_parameters += ('delta', )
                self.delta = Function(name="delta", grid=self.grid)
                initialize_function(self.delta, np.sqrt(1 + 2 * delta),
                                    self.nbpml)
            else:
                self.delta = delta
        else:
            self.delta = 1

        if theta is not None:
            if isinstance(theta, np.ndarray):
                self._physical_parameters += ('theta', )
                self.theta = Function(name="theta",
                                      grid=self.grid,
                                      space_order=space_order)
                initialize_function(self.theta, theta, self.nbpml)
            else:
                self.theta = theta
        else:
            self.theta = 0

        if phi is not None:
            if self.grid.dim < 3:
                warning(
                    "2D TTI does not use an azimuth angle Phi, ignoring input")
                self.phi = 0
            elif isinstance(phi, np.ndarray):
                self._physical_parameters += ('phi', )
                self.phi = Function(name="phi",
                                    grid=self.grid,
                                    space_order=space_order)
                initialize_function(self.phi, phi, self.nbpml)
            else:
                self.phi = phi
        else:
            self.phi = 0
Пример #18
0
def plot(problem, **kwargs):
    """
    Plotting mode to generate plots for performance analysis.
    """
    backend = kwargs.pop('backend')
    resultsdir = kwargs.pop('resultsdir')
    max_bw = kwargs.pop('max_bw')
    flop_ceils = kwargs.pop('flop_ceil')
    point_runtime = kwargs.pop('point_runtime')
    autotune = kwargs['autotune']
    arch = kwargs['arch']
    space_order = "[%s]" % ",".join(str(i) for i in kwargs['space_order'])
    time_order = kwargs['time_order']
    shape = "[%s]" % ",".join(str(i) for i in kwargs['shape'])

    section = kwargs.pop('section')
    if not section:
        warning("No `section` provided. Using `%s`'s default `%s`" %
                (problem, model_type[problem]['default-section']))
        section = model_type[problem]['default-section']

    bench = get_ob_bench(problem, resultsdir, kwargs)

    bench.load()
    if not bench.loaded:
        warning("Could not load any results, nothing to plot. Exiting...")
        sys.exit(0)

    gflopss = bench.lookup(params=kwargs, measure="gflopss", event=section)
    oi = bench.lookup(params=kwargs, measure="oi", event=section)
    time = bench.lookup(params=kwargs, measure="timings", event=section)

    # What plot am I?
    modes = [
        i for i in ['dse', 'dle', 'autotune']
        if len(set(dict(j)[i] for j in gflopss)) > 1
    ]

    # Filename
    figname = "%s_shape%s_so%s_to%s_arch[%s]_bkend[%s]_at[%s]" % (
        problem, shape, space_order, time_order, arch, backend, autotune)

    # Legend setup. Do not plot a legend if there's no variation in performance
    # options (dse, dle, autotune)
    if modes:
        legend = {'loc': 'upper left', 'fontsize': 7, 'ncol': 4}
    else:
        legend = 'drop'

    avail_colors = ['r', 'g', 'b', 'y', 'k', 'm']
    avail_markers = ['o', 'x', '^', 'v', '<', '>']

    used_colors = {}
    used_markers = {}

    # Find min and max runtimes for instances having the same OI
    min_max = {v: [0, sys.maxsize] for v in oi.values()}
    for k, v in time.items():
        i = oi[k]
        min_max[i][0] = v if min_max[i][0] == 0 else min(v, min_max[i][0])
        min_max[i][1] = v if min_max[i][1] == sys.maxsize else max(
            v, min_max[i][1])

    with RooflinePlotter(figname=figname,
                         plotdir=resultsdir,
                         max_bw=max_bw,
                         flop_ceils=flop_ceils,
                         fancycolor=True,
                         legend=legend) as plot:
        for k, v in gflopss.items():
            so = dict(k)['space_order']

            oi_value = oi[k]
            time_value = time[k]

            run = tuple(dict(k)[i] for i in modes)
            label = ("<%s>" % ','.join(run)) if run else None

            color = used_colors[
                run] if run in used_colors else avail_colors.pop(0)
            used_colors.setdefault(run, color)
            marker = used_markers[
                so] if so in used_markers else avail_markers.pop(0)
            used_markers.setdefault(so, marker)

            oi_loc = 0.076 if len(str(so)) == 1 else 0.09
            oi_annotate = {
                's': 'SO=%s' % so,
                'size': 6,
                'xy': (oi_value, oi_loc)
            }
            if time_value in min_max[oi_value] and point_runtime:
                # Only annotate min and max runtimes on each OI line, to avoid
                # polluting the plot too much
                point_annotate = {
                    's': "%.0fs" % time_value,
                    'xytext': (0.0, 5.5),
                    'size': 6,
                    'rotation': 0
                }
            else:
                point_annotate = None
            oi_line = time_value == min_max[oi_value][0]
            if oi_line:
                perf_annotate = {'size': 6, 'xytext': (-4, 5)}

            plot.add_point(gflops=v,
                           oi=oi_value,
                           marker=marker,
                           color=color,
                           oi_line=oi_line,
                           label=label,
                           perf_annotate=perf_annotate,
                           oi_annotate=oi_annotate,
                           point_annotate=point_annotate)
Пример #19
0
def wri_func(model,
             src_coords,
             wavelet,
             rec_coords,
             recin,
             yin,
             space_order=8,
             isic=False,
             ws=None,
             t_sub=1,
             grad="m",
             grad_corr=False,
             alpha_op=False,
             w_fun=None,
             eps=0,
             freq_list=[],
             wfilt=None):
    """
    Time domain wavefield reconstruction inversion wrapper
    """
    if freq_list is not None:
        if grad_corr or grad in ["all", "y"]:
            warning("On-the-fly DFT is not supported with gradient correction")
        dft = True
    else:
        dft = False
        freq_list = None
        wfilt = wavelet

    # F(m0) * q if y is not an input and compute y = r(m0)
    if yin is None or grad_corr:
        y, u0, _ = forward(model,
                           src_coords,
                           rec_coords,
                           wavelet,
                           save=grad_corr,
                           space_order=space_order,
                           ws=ws)
        ydat = recin[:] - y.data[:]
    else:
        ydat = yin

    # Compute wavefield vy = adjoint(F(m0))*y and norm on the fly
    srca, v, norm_v, _ = adjoint(model,
                                 ydat,
                                 src_coords,
                                 rec_coords,
                                 norm_v=True,
                                 w_fun=w_fun,
                                 freq_list=freq_list,
                                 save=not (grad is None or dft))
    c1 = 1 / (recin.shape[1])
    c2 = np.log(np.prod(model.shape))
    # <PTy, d-F(m)*f> = <PTy, d>-<adjoint(F(m))*PTy, f>
    ndt = np.sqrt(model.critical_dt)
    PTy_dot_r = ndt**2 * (np.dot(ydat.reshape(-1), recin.reshape(-1)) -
                          np.dot(srca.data.reshape(-1), wavelet.reshape(-1)))
    norm_y = ndt * np.linalg.norm(ydat)

    # alpha
    α = compute_optalpha(c2 * norm_y, c1 * norm_v, eps, comp_alpha=alpha_op)

    # Lagrangian evaluation
    fun = -.5 * c1 * α**2 * norm_v + c2 * α * PTy_dot_r - eps * np.abs(
        α) * norm_y

    gradm = grady = None
    if grad is not None:
        w = weight_fun(w_fun, model, src_coords)
        w = c1 * α / w**2 if w is not None else c1 * α
        Q = wf_as_src(v, w=w, freq_list=freq_list)
        rcv, gradm, _ = forward_grad(model,
                                     src_coords,
                                     rec_coords,
                                     c2 * wfilt,
                                     freq=freq_list,
                                     q=Q,
                                     v=v)

        # Compute gradient wrt y
        if grad_corr or grad in ["all", "y"]:
            grady = c2 * recin - rcv.data[:]
            if norm_y != 0:
                grady -= np.abs(eps) * ydat / norm_y

        # Correcting for reduced gradient
        if not grad_corr:
            gradm = gradm.data
        else:
            gradm_corr, _ = gradient(model, grady, rec_coords, u0)
            # Reduced gradient post-processing
            gradm = gradm.data + gradm_corr.data

    return fun, gradm if gradm is None else α * gradm, grady