Dear Lixing,
I think you are right. In Newton's method, the entries of the residual vector which correspond to degrees of freedom that are constrained through a Dirichlet boundary condition should vanish. In step-57, this is achieved using the member variable AffineConstraints<double> zero_constraints and the constraints are enforced during the assembly process by calling AffineConstraints<double>::distribute_local_to_global. Since inhomogeneous boundary conditions are applied in step-57, a switch between the inhomogeneous and homogeneous constraints is necessary. The inhomogeneous constraints are actually only used in the very first assembly of the linear system (system matrix and right-hand side vector). In all subsequent assemblies of the residual vector and the linear system, the homogeneous constraints are used. This is achieved by
const AffineConstraints<double> &constraints_used =
initial_step ? nonzero_constraints : zero_constraints;
In step-15, Dirichlet boundary conditions and hanging node constraints are not treated using a joint AffineConstraints<double> object. The hanging node constraints are enforced through an explicit condensation after assembling the system matrix and right-hand side vector by means of an AffineConstraints<double> object. The Dirichlet boundary conditions are then enforced by calling MatrixTools::apply_boundary_values and by explicitly setting them to zero in the residual vector. In step-15, only homogeneous constraints are applied because the solution automatically satisfies them due to the call to the method set_boundary_values.
Both approaches are equivalent, but should not be confused or mixed. I prefer the way of step-57, but this is a personal flavor. I hope my comments are helpful.
Best wishes,
Sebastian