//  [python]
def up_sample(self, rays_o, rays_d, z_vals, sdf, n_importance, inv_s, step):
        """
        Up sampling give a fixed inv_s
        """
        device = sdf.device
        batch_size, n_samples = z_vals.shape
        pts = (
            rays_o[:, None, :] + rays_d[:, None, :] * z_vals[..., :, None]
        )  # n_rays, n_samples, 3
        radius = torch.linalg.norm(pts, ord=2, dim=-1, keepdim=False)
        inside_sphere = (radius[:, :-1] < 1.0) | (radius[:, 1:] < 1.0)
        sdf = sdf.reshape(batch_size, n_samples)
        prev_sdf, next_sdf = sdf[:, :-1], sdf[:, 1:]
        prev_z_vals, next_z_vals = z_vals[:, :-1], z_vals[:, 1:]
        mid_sdf = (prev_sdf + next_sdf) * 0.5
        cos_val = (next_sdf - prev_sdf) / (next_z_vals - prev_z_vals + 1e-5)

        # ----------------------------------------------------------------------------------------------------------
        # Use min value of [ cos, prev_cos ]
        # Though it makes the sampling (not rendering) a little bit biased, this strategy can make the sampling more
        # robust when meeting situations like below:
        #
        # SDF
        # ^
        # |\          -----x----...
        # | \        /
        # |  x      x
        # |---\----/-------------> 0 level
        # |    \  /
        # |     \/
        # |
        # ----------------------------------------------------------------------------------------------------------
        prev_cos_val = torch.cat(
            [torch.zeros([batch_size, 1], device=device), cos_val[:, :-1]], dim=-1
        )
        cos_val = torch.stack([prev_cos_val, cos_val], dim=-1)
        cos_val, _ = torch.min(cos_val, dim=-1, keepdim=False)
        cos_val = cos_val.clip(-1e3, 0.0) * inside_sphere

        dist = next_z_vals - prev_z_vals
        prev_esti_sdf = mid_sdf - cos_val * dist * 0.5
        next_esti_sdf = mid_sdf + cos_val * dist * 0.5
        prev_cdf = torch.sigmoid(prev_esti_sdf * inv_s)
        next_cdf = torch.sigmoid(next_esti_sdf * inv_s)
        alpha = (prev_cdf - next_cdf + 1e-5) / (prev_cdf + 1e-5)
        # transient alpha
        # alpha = alpha_s + alpha_t
        weights = (
            alpha
            * torch.cumprod(
                torch.cat(
                    [torch.ones([batch_size, 1], device=device), 1.0 - alpha + 1e-7], -1
                ),
                -1,
            )[:, :-1]
        )

        z_samples = sample_pdf(z_vals, weights, n_importance, det=True).detach()

        if self.save_step_sample:
            # # start points
            # self.save_samples_step(pts[:, :-1][weights < 0.1].view(-1, 3), f"{step}_{0.0}_{0.1}")
            # # end points
            # self.save_samples_step(pts[:, :-1][((weights > 0.1) & (weights < 0.9))].view(-1, 3), f"{step}_{0.1}_{0.9}")
            # self.save_samples_step(pts[:, :-1].reshape(-1, 3), f"{step}_all")

            # colored
            self.save_samples_step(
                pts[:, :-1].reshape(-1, 3),
                weights.reshape(
                    -1,
                ),
                f"{step}_colored",
            )

            pts_new = rays_o[:, None, :] + rays_d[:, None, :] * z_samples[..., :, None]
            self.save_samples_step(
                pts_new.reshape(-1, 3),
                torch.zeros_like(z_samples).reshape(
                    -1,
                ),
                f"{step}",
                dir_name="new_z",
            )
        return z_samples