Program Listing for File NonlinearContactForce.h

Program Listing for File NonlinearContactForce.h#

Return to documentation for file (include/Karana/Collision/NonlinearContactForce.h)

/*
 * Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
 *
 * NOTICE TO USER:
 *
 * This source code and/or documentation (the "Licensed Materials") is
 * the confidential and proprietary information of Karana Dynamics Inc.
 * Use of these Licensed Materials is governed by the terms and conditions
 * of a separate software license agreement between Karana Dynamics and the
 * Licensee ("License Agreement"). Unless expressly permitted under that
 * agreement, any reproduction, modification, distribution, or disclosure
 * of the Licensed Materials, in whole or in part, to any third party
 * without the prior written consent of Karana Dynamics is strictly prohibited.
 *
 * THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
 * KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
 * INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
 * OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
 *
 * U.S. Government End Users: The Licensed Materials are a "commercial item"
 * as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
 * only as a commercial end item under the terms of this license.
 *
 * Any use of the Licensed Materials in individual or commercial software must
 * include, in the user documentation and internal source code comments,
 * this Notice, Disclaimer, and U.S. Government Use Provision.
 */

/**
 * @file NonlinearContactForce.h
 * @brief Nonlinear Collision contact-force model.
 * @author Codex, instructed by kelly
 * @date 2026-07-24
 */

#pragma once

#include "Karana/Collision/ContactForce.h"
#include <optional>

namespace Karana::Collision {

    namespace kc = Karana::Core;
    namespace kd = Karana::Dynamics;
    namespace km = Karana::Math;

    /**
     * @class NonlinearContactForce
     * @brief Nonlinear penalty contact force with smoothly regularized damping
     * and friction.
     *
     * With penetration \f$\delta\f$, signed closing speed \f$v_n\f$, stiffness
     * \f$k_p\f$, exponent \f$n\f$, damping \f$k_c\f$, and full-damping depth
     * \f$d_{\max}\f$, the normal force magnitude is
     * \f[
     * F_n=\max\left(0,k_p\delta^n+k_c\,S(\delta/d_{\max})v_n\right).
     * \f]
     * By default, \f$S(a)=a^2(3-2a)\f$ with \f$a\f$ clipped to \f$[0,1]\f$.
     * Tangential friction follows a cubic, velocity-regularized Coulomb curve.
     * Its magnitude rises smoothly from zero to \f$\mu_s F_n\f$ at the
     * stiction-transition speed \f$v_s\f$, falls smoothly to
     * \f$\mu_d F_n\f$ at the friction-transition speed \f$v_d\f$, and remains
     * on that dynamic plateau thereafter.
     *
     * The damping and friction transitions use a cubic polynomial approximation
     * to a step function.
     */
    class NonlinearContactForce : public ContactForceBase {
      public:
        /// Use the ContactForceBase constructor.
        using ContactForceBase::ContactForceBase;

        /** @brief Parameters used by the nonlinear contact-force model. */
        struct NonlinearContactForceParams {
            /// Penalty stiffness in N/m^n.
            double kp = km::notReadyNaN;
            /// Maximum normal damping coefficient in N s/m.
            double kc = km::notReadyNaN;
            /// Positive penetration exponent.
            double n = km::notReadyNaN;
            /// Static coefficient, or nullopt to use mu_dynamic.
            std::optional<double> mu_static = std::nullopt;
            /// Dynamic friction coefficient.
            double mu_dynamic = km::notReadyNaN;
            /// Penetration at full damping in meters.
            double damping_ramp_depth = km::notReadyNaN;
            /// Slip speed of the static-friction peak in m/s.
            double stiction_transition_speed = km::notReadyNaN;
            /// Slip speed at the end of the friction transition in m/s.
            double friction_transition_speed = km::notReadyNaN;
        };

        /** @brief Scratch data populated by the latest force computation. */
        struct NonlinearContactForceScratch {
            /// Positive penetration depth in meters.
            double penetration = km::notReadyNaN;
            /// Relative contact-point velocity expressed in frame 1.
            km::Vec3 linvel;
            /// Total force on object 1 expressed in frame 1.
            km::Vec3 f;
        };

        /**
         * @brief Create a nonlinear contact-force model.
         * @param name Object name.
         * @return Newly allocated model.
         */
        // codechecker_suppress [cppcheck-duplInheritedMember]
        static kc::ks_ptr<NonlinearContactForce> create(std::string_view name);

        /**
         * @brief Compute the force on contact object 1.
         * @param contact Collision-native contact geometry.
         * @param nd_1 Contact node on object 1.
         * @param nd_2 Contact node on object 2.
         * @return Spatial force on object 1 expressed in frame 1.
         */
        km::SpatialForce computeForce(const FrameContact &contact,
                                      const kc::ks_ptr<kd::Node> &nd_1,
                                      const kc::ks_ptr<kd::Node> &nd_2) final;

        /** @brief Contact-law parameters. */
        NonlinearContactForceParams params;
        /** @brief Latest computed contact values. */
        NonlinearContactForceScratch scratch;

        /**
         * @brief Validate the model parameters.
         * @return True when all parameters are usable.
         */
        bool isReady() const final;

        /**
         * @brief Return model parameters and latest scratch values.
         * @param prefix Prefix applied to each output line.
         * @param options Dump options.
         * @return Formatted diagnostic text.
         */
        std::string dumpString(std::string_view prefix,
                               const kc::Base::DumpOptions *options = nullptr) const override;

      private:
        /**
         * @brief Evaluate the selected clipped transition.
         * @param ratio Interpolation coordinate.
         * @return Smooth value in [0, 1].
         */
        static double _smoothStep(double ratio);

        /**
         * @brief Evaluate the clamped normal-force magnitude.
         * @param normal_velocity Signed closing velocity.
         * @return Nonnegative normal-force magnitude.
         */
        double _getNonlinearContactForceNormalForce(double normal_velocity) const;

        /**
         * @brief Evaluate the smoothed Coulomb tangential force.
         * @param force_direction Repulsive normal-force direction.
         * @param normal_velocity Signed closing velocity.
         * @param normal_force Nonnegative normal-force magnitude.
         * @return Tangential force on contact object 1.
         */
        km::Vec3 _getCoulombFrictionTangentForce(const km::Vec3 &force_direction,
                                                 double normal_velocity,
                                                 double normal_force) const;
    };
} // namespace Karana::Collision