\DeclareMathOperator: wrong space when the operator is followed by a binary operator

\DeclareMathOperator makes a \mathop atom, which are designed for use as prefix functions. In contexts where they are not being used as a prefix application, such as the higher order composition here you can always make a \mathord atom by surrounding with braces, {\grad} which will have the same spacing as \mathrm{grad}


If you are sure that \grad will be followed by a fixed (but easily extendable) set of binary operators, this should work:

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\DeclareMathOperator{\gradop}{grad}

\ExplSyntaxOn

% the list of admissible binary operators
\tl_const:Nn \c_denis_grad_ops_tl { \cdot \wedge }

\NewDocumentCommand{\grad}{}
 {
  \gradop
  \peek_after:Nw \denis_grad_check:
 }

\cs_new_protected:Nn \denis_grad_check:
 {
  \tl_map_inline:Nn \c_denis_grad_ops_tl
   {% if the token matches one in the list, issue {\!}
    \token_if_eq_meaning:NNT \l_peek_token ##1 { \tl_map_break:n { {\!} } }
   }
 } 

\ExplSyntaxOff

\begin{document}

\begin{gather*}
\grad F \\
\mathop{\mathrm{grad}} F\\
\grad \cdot F \\
\mathrm{grad}\cdot F \\
\grad \wedge F \\
\mathrm{grad}\wedge F 
\end{gather*}

\end{document}

enter image description here

If a binary operator in the list follows, we issue {\!} that fixes the spacing: the thin space between the operator and the empty atom is nullified by \! and the empty atom will provide the required bit for the spacing around binary operators.

A slightly different version with an interface for adding binary operators; the initial declaration

\OperatorBinary{\cdot,\wedge} % initialize

should go in the class, together with instructions such as

You can define operator names that behave well when followed by binary operation symbols with

\DeclareMathOperatorX{<cs>}{<name>}

for example

\DeclareMathOperatorX{\grad}{grad}

to be used like \grad f or else like \grad\cdot F. The predefined list of admissible binary operators includes \cdot and \wedge, but it can be augmented by saying in the preamble, for instance,

\OperatorBinary{\times}

(the argument can be a list like in the initial declaration which is \OperatorBinary{\cdot,\wedge}. No operator of this type is predefined, use \DeclareMathOperatorX for defining the one you'll be using.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\DeclareMathOperatorX}{mm}
 {
  \NewDocumentCommand{#1}{}
   {
    \operatorname{#2}
    \peek_after:Nw \denis_opx_check:
   }
 }
\NewDocumentCommand{\OperatorBinary}{m}
 {
  \clist_gput_right:Nn \g_denis_opx_binary_clist { #1 }
 }

\clist_new:N \g_denis_opx_binary_clist
\cs_new_protected:Nn \denis_opx_check:
 {
  \clist_map_inline:Nn \g_denis_opx_binary_clist
   {
    \token_if_eq_meaning:NNT \l_peek_token ##1 { \clist_map_break:n { {\!} } }
   }
 } 
\ExplSyntaxOff

\OperatorBinary{\cdot,\wedge} % initialize
\DeclareMathOperatorX{\grad}{grad}

\begin{document}

\begin{gather*}
\grad F \\
\mathop{\mathrm{grad}} F\\
\grad \cdot F \\
\mathrm{grad}\cdot F \\
\grad \wedge F \\
\mathrm{grad}\wedge F 
\end{gather*}

\end{document}