Could you post the code of RigidityExp, since self.eta is being used only there?
I guess you might detach it somewhere in your function.
A simple dummy model without your custom function works fine:
class ANN_Pytorch(nn.Module):
    def __init__(self, D_in, H1, H2, D_out, cutoff, beta):
        super(ANN_Pytorch, self).__init__()
        self.w0 = torch.rand(1, D_in, requires_grad=True)
        self.eta = nn.Parameter(self.w0)
        self.linear1 = nn.Linear(D_in, H1)
        self.linear2 = nn.Linear(H1, H2)
        self.linear3 = nn.Linear(H2, D_out)


    def forward(self, x):
        x = x + self.eta
        z1 = self.linear1(x)
        f1 = torch.sigmoid(z1)
        z2 = self.linear2(f1)
        f2 = torch.sigmoid(z2)
        y_hat = self.linear3(f2)
        return y_hat


model = ANN_Pytorch(5, 10, 10, 10, 0, 0)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr = 1e-2, momentum = 0.9, weight_decay = 1e-2)
epochs = 300

data = torch.randn(1, 5)
ytrain = torch.randn(1, 10)
#===================================Trianing Model==============================
for epoch in range(epochs):
    model.train()
    y_pred  = model(data)
    loss = criterion(y_pred, ytrain)
    optimizer.zero_grad()
    loss.backward()
    print(model.eta.grad)
    optimizer.step()