//  [python]
def forward(self, batch, out: dict):
        input_xyz = batch[DS.INPUT_PC][0]
        assert input_xyz.ndim == 2, "Can only forward single batch."

        if self.hparams.feature == 'normal':
            assert DS.TARGET_NORMAL in batch.keys(), "normal must be provided in this config!"
            feat = batch[DS.TARGET_NORMAL][0]
        elif self.hparams.feature == 'sensor':
            assert DS.INPUT_SENSOR_POS in batch.keys(), "sensor must be provided in this config!"
            view_dir = batch[DS.INPUT_SENSOR_POS][0] - input_xyz
            view_dir = view_dir / (torch.norm(view_dir, dim=-1, keepdim=True) + 1e-6)
            feat = view_dir
        else:
            feat = None
        out['feat'] = feat

        enc_svh = SparseFeatureHierarchy(
            voxel_size=self.hparams.voxel_size,
            depth=self.hparams.tree_depth,
            device=self.device
        )
        enc_svh.build_point_splatting(input_xyz)

        # Compute density by computing points per voxel.
        if self.hparams.runtime_density:
            q_xyz = torch.unique(torch.div(input_xyz, self.hparams.voxel_size).floor().int(), dim=0)
            density = input_xyz.size(0) / q_xyz.size(0)
            exp.logger.info(f"Density {density}, # pts = {input_xyz.size(0)}")

        if self.hparams.runtime_visualize:
            vis.show_3d([vis.pointcloud(input_xyz, normal=feat)], enc_svh.get_visualization())

        feat = self.network.encoder(input_xyz, feat, enc_svh, 0)
        feat, dec_svh, udf_svh = self.network.unet(
            feat, enc_svh,
            adaptive_depth=self.hparams.adaptive_depth,
            gt_decoder_svh=out.get('gt_svh', None)
        )

        if all([dec_svh.grids[d] is None for d in range(self.hparams.adaptive_depth)]):
            if self.trainer.training or self.trainer.validating:
                # In case training data is corrupted (pd & gt not aligned)...
                exp.logger.warning("Empty grid detected during training/validation.")
                return None

        out.update({'enc_svh': enc_svh, 'dec_svh': dec_svh, 'dec_tmp_svh': udf_svh})
        if self.trainer.training:
            SVH_CACHE.append([enc_svh, dec_svh, udf_svh])

        if self.hparams.geometry == 'kernel':
            output_field = KernelField(
                svh=dec_svh,
                interpolator=self.network.interpolators,
                features=feat.basis_features,
                approx_kernel_grad=False
            )
            if self.hparams.solver_verbose:
                output_field.solver_config['verbose'] = True

            normal_xyz = torch.cat([dec_svh.get_voxel_centers(d) for d in range(self.hparams.adaptive_depth)])
            normal_value = torch.cat([feat.normal_features[d] for d in range(self.hparams.adaptive_depth)])

            normal_weight = self.hparams.solver.normal_weight / normal_xyz.size(0) * \
                (self.hparams.voxel_size ** 2)
            output_field.solve_non_fused(
                pos_xyz=input_xyz,
                normal_xyz=normal_xyz,
                normal_value=-normal_value,
                pos_weight=self.hparams.solver.pos_weight / input_xyz.size(0),
                normal_weight=normal_weight,
                reg_weight=1.0
            )

        elif self.hparams.geometry == 'neural':
            output_field = NeuralField(
                svh=dec_svh,
                decoder=self.network.sdf_decoder,
                features=feat.basis_features
            )

        else:
            raise NotImplementedError

        if self.hparams.udf.enabled:
            mask_field = NeuralField(
                svh=udf_svh,
                decoder=self.network.udf_decoder,
                features=feat.udf_features
            )
            mask_field.set_level_set(2 * self.hparams.voxel_size)
        else:
            mask_field = LayerField(dec_svh, self.hparams.adaptive_depth)
        output_field.set_mask_field(mask_field)

        out.update({
            'structure_features': feat.structure_features,
            'normal_features': feat.normal_features,
            'basis_features': feat.basis_features,
            'field': output_field
        })
        return out