```python
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.cpp_extension import load_inline

# Custom CUDA kernel that scatters the reduced (stride-1) convolution result
# into the full strided output tensor, filling all other positions with the
# bias value (or zero). This fuses "zeros init + bias fill + strided copy"
# into a single pass over the output.
cuda_src = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda_runtime.h>

__global__ void tconv_scatter_kernel(
    const float* __restrict__ tmp,   // [B, Co, MDc, MHc, MWc] contiguous
    const float* __restrict__ bias,  // [Co] or null
    float* __restrict__ out,         // [B, Co, Od, Oh, Ow]
    int Co, int Od, int Oh, int Ow,
    int MDc, int MHc, int MWc,
    int md0, int mh0, int mw0,
    int mdn, int mhn, int mwn,
    int sd, int sh, int sw,
    int pd, int ph, int pw,
    long long total)
{
    const long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;

    const int ow = (int)(idx % Ow);
    const long long t1 = idx / Ow;
    const int oh = (int)(t1 % Oh);
    const long long t2 = t1 / Oh;
    const int od = (int)(t2 % Od);
    const long long t3 = t2 / Od;
    const int co = (int)(t3 % Co);
    const int b  = (int)(t3 / Co);

    float v = (bias != nullptr) ? __ldg(bias + co) : 0.f;
    const int td = od + pd, th = oh + ph, tw = ow + pw;
    if ((td % sd == 0) && (th % sh == 0) && (tw % sw == 0)) {
        const int md = td / sd - md0;
        const int mh = th / sh - mh0;
        const int mw = tw / sw - mw0;
        if ((unsigned)md < (unsigned)mdn &&
            (unsigned)mh < (unsigned)mhn &&
            (unsigned)mw < (unsigned)mwn) {
            v += tmp[(((long long)b * Co + co) * MDc + md) * ((long long)MHc * MWc)
                     + (long long)mh * MWc + mw];
        }
    }
    out[idx] = v;
}

torch::Tensor tconv_scatter(
    torch::Tensor tmp, torch::Tensor bias,
    int64_t B, int64_t Co,
    int64_t Od, int64_t Oh, int64_t Ow,
    int64_t md0, int64_t mh0, int64_t mw0,
    int64_t mdn, int64_t mhn, int64_t mwn,
    int64_t sd, int64_t sh, int64_t sw,
    int64_t pd, int64_t ph, int64_t pw)
{
    auto out = torch::empty({B, Co, Od, Oh, Ow}, tmp.options());
    const long long total = (long long)out.numel();
    if (total == 0) return out;

    const bool has_bias = bias.defined() && bias.numel() > 0;
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    const int threads = 256;
    const long long blocks = (total + threads - 1) / threads;

    tconv_scatter_kernel<<<(unsigned)blocks, threads, 0, stream>>>(
        tmp.data_ptr<float>(),
        has_bias ? bias.data_ptr<float>() : nullptr,
        out.data_ptr<float>(),
        (int)Co, (int)Od, (int)Oh, (int)Ow,
        (int)tmp.size(2), (int)tmp.size(3), (int)tmp.size(4),
        (int)md0, (int)mh0, (int)mw0,
        (int)mdn, (int)mhn, (int)mwn,
        (int)sd, (int)sh, (int)sw,
        (int)pd, (int)ph, (int)pw,
        total);
    return out;
}
"""

cpp_src = r"""
torch::Tensor tconv_scatter(
    torch::Tensor tmp, torch::Tensor bias,
    int64_t B, int64_t Co,
    int64_t Od, int64_t Oh, int64_t Ow,
    int64_t md0, int64_t mh0, int64_t mw0,
    int64_t mdn, int64_t mhn, int64_t mwn,
    int64_t sd, int64_t sh, int64_t sw,
    int64_t pd, int64_t ph, int64_t pw);
"""

tconv_scatter = load_inline(
    name="tconv_scatter_v3",
    cpp_sources=cpp_src,
    cuda_sources=cuda_src,
    functions=["tconv_scatter"],
    verbose=False,
    extra_cuda_cflags=["-O3"],
)


class ModelNew(nn.Module):
    """
    ConvTranspose3d implemented (when dilation is an integer multiple of
    stride per dimension) as:
      1) a regular stride-1 cuDNN conv3d on a cropped view of the input in a
         "reduced" coordinate space (kernel flipped, dilation = dil/stride),
         which runs on tensor cores with TF32, followed by
      2) a custom fused CUDA kernel that scatters the reduced result into the
         strided output positions and fills everything else with bias/zero.
    Falls back to the PyTorch op for unsupported configurations.
    """

    def __init__(self, in_channels: int, out_channels: int, kernel_size: int,
                 stride: int = 1, padding: int = 0, dilation: int = 1,
                 bias: bool = False):
        super(ModelNew, self).__init__()
        self.conv_transpose3d = nn.ConvTranspose3d(
            in_channels, out_channels,
            kernel_size=(kernel_size, kernel_size, kernel_size),
            stride=stride, padding=padding, dilation=dilation, bias=bias)
        s = self.conv_transpose3d.stride
        p = self.conv_transpose3d.padding
        d = self.conv_transpose3d.dilation
        self.sd, self.sh, self.sw = int(s[0]), int(s[1]), int(s[2])
        self.pd, self.ph, self.pw = int(p[0]), int(p[1]), int(p[2])
        self.dd, self.dh, self.dw = int(d[0]), int(d[1]), int(d[2])
        self.kernel_size = int(kernel_size)

        self._fast = (self.dd % self.sd == 0 and
                      self.dh % self.sh == 0 and
                      self.dw % self.sw == 0)
        self.rd = self.dd // self.sd
        self.rh = self.dh // self.sh
        self.rw = self.dw // self.sw

        self._wf = None
        self._wf_ver = -1
        self._geom = {}
        self._empty_bias = None

    def _geometry(self, D, H, W):
        key = (D, H, W)
        g = self._geom.get(key)
        if g is not None:
            return g
        K = self.kernel_size

        def axis(n, s, p, dil, r):
            O = (n - 1) * s - 2 * p + dil * (K - 1) + 1
            m0 = (p + s - 1) // s
            mn = (O - 1 + p) // s - m0 + 1
            rK = r * (K - 1)
            i0 = m0 - rK
            i1 = m0 + mn - 1
            c0 = i0 if i0 > 0 else 0
            c1 = i1 + 1
            if c1 > n:
                c1 = n
            pp = rK + c0 - m0
            return O, m0, mn, c0, c1, pp

        g = (axis(D, self.sd, self.pd, self.dd, self.rd),
             axis(H, self.sh, self.ph, self.dh, self.rh),
             axis(W, self.sw, self.pw, self.dw, self.rw))
        self._geom[key] = g
        return g

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        if (not self._fast) or (not x.is_cuda) or x.dtype != torch.float32:
            return self.conv_transpose3d(x)

        w = self.conv_transpose3d.weight
        bias = self.conv_transpose3d.bias

        wf = self._wf
        if wf is None or self._wf_ver != w._version or wf.device != w.device:
            # [Ci, Co, Kd, Kh, Kw] -> flip kernel -> [Co, Ci, Kd, Kh, Kw]
            wf = w.detach().flip([2, 3, 4]).permute(1, 0, 2, 3, 4).contiguous()
            self._wf = wf
            self._wf_ver = w._version

        B = x.shape[0]
        D, H, W = int(x.shape[2]), int(x.shape[3]), int(x.shape[4])
        (Od, md0, mdn, dc0, dc1, pdp), \
            (Oh, mh0, mhn, hc0, hc1, php), \
            (Ow, mw0, mwn, wc0, wc1, pwp) = self._geometry(D, H, W)

        Co = self.conv_transpose3d.out_channels

        if mdn <= 0 or mhn <= 0 or mwn <= 0 or \
                dc1 <= dc0 or hc1 <= hc0 or wc1 <= wc0 or \
                Od <= 0 or Oh <= 0 or Ow <= 0:
            out = torch.empty((B, Co, Od, Oh, Ow), device=x.device, dtype=x.dtype)
            if out.numel() > 0:
                if bias is None:
                    out.zero_()
                else:
                    out.copy_(bias.view(1, -1, 1, 1, 1))
            return out

        xc = x[:, :, dc0:dc1, hc0:hc1, wc0:wc1]
        if not xc.is_contiguous():
            xc = xc.contiguous()

        tmp = F.conv3d(xc, wf, bias, stride=1,
                       padding=(pdp, php, pwp),
                       dilation=(self.rd, self.rh, self.rw))

        if bias is None:
            eb = self._empty_bias
            if eb is None or eb.device != x.device:
                eb = torch.empty(0, device=x.device, dtype=x.dtype)
                self._empty_bias = eb
            bias = eb

        return tconv_scatter.tconv_scatter(
            tmp, bias, B, Co, Od, Oh, Ow,
            md0, mh0, mw0, mdn, mhn, mwn,
            self.sd, self.sh, self.sw,
            self.pd, self.ph, self.pw)
```