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}

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}
{}as in{\grad}\grad{f},\grad{\cdot F}or\grad{\wedge F}?\grad{f}is okay but\grad{\cdot F}and\grad{\wedge F}are conceptually suspect :)\gradis always followed by a certain fixed set of tokens, if not by a variable, it can be arranged, I guess.