def adapt_to_grid(x, p_xmin, p_xmax, p_nx, ncells_empty=0):
    """
    Adapt p_xmin and p_xmax, so that they fall exactly on the grid x
    Return the total number of particles, assuming p_nx particles
    per gridpoint

    Parameters
    ----------
    x: 1darray
        The positions of the gridpoints along the x direction

    p_xmin, p_xmax: float
        The minimal and maximal position of the particles
        These may not fall exactly on the grid

    p_nx: int
        Number of particle per gridpoint

    ncells_empty: int
        Number of empty cells at the righthand side of the box
        (Typically used when using a moving window)

    Returns
    -------
    A tuple with:
       - p_xmin: a float that falls exactly on the grid
       - p_xmax: a float that falls exactly on the grid
       - Npx: the total number of particles
    """
    xmin = x.min()
    xmax = x.max()
    dx = x[1] - x[0]
    if p_xmin < xmin - 0.5 * dx:
        p_xmin = xmin - 0.5 * dx
    if p_xmax > xmax + (0.5 - ncells_empty) * dx:
        p_xmax = xmax + (0.5 - ncells_empty) * dx
    x_load = x[(x > p_xmin) & (x < p_xmax)]
    Npx = len(x_load) * p_nx
    if Npx > 0:
        p_xmin = x_load.min() - 0.5 * dx
        p_xmax = x_load.max() + 0.5 * dx
    return p_xmin, p_xmax, Npx
