#ifndef SimTK_SIMBODY_FORCE_IMPL_H_ #define SimTK_SIMBODY_FORCE_IMPL_H_ /* -------------------------------------------------------------------------- * * Simbody(tm) * * -------------------------------------------------------------------------- * * This is part of the SimTK biosimulation toolkit originating from * * Simbios, the NIH National Center for Physics-Based Simulation of * * Biological Structures at Stanford, funded under the NIH Roadmap for * * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. * * * * Portions copyright (c) 2008-12 Stanford University and the Authors. * * Authors: Peter Eastman, Michael Sherman * * Contributors: * * * * Licensed under the Apache License, Version 2.0 (the "License"); you may * * not use this file except in compliance with the License. You may obtain a * * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * * -------------------------------------------------------------------------- */ #include "SimTKcommon.h" #include "simbody/internal/common.h" #include "simbody/internal/Force.h" #include "simbody/internal/Force_BuiltIns.h" namespace SimTK { // This is what a Force handle points to. class ForceImpl : public PIMPLImplementation { public: ForceImpl() : forces(0), defaultDisabled(false) {} ForceImpl(const ForceImpl& clone) {*this = clone;} void setDisabledByDefault(bool shouldBeDisabled) { invalidateTopologyCache(); defaultDisabled = shouldBeDisabled; } bool isDisabledByDefault() const { return defaultDisabled; } virtual ~ForceImpl() {} virtual ForceImpl* clone() const = 0; virtual bool dependsOnlyOnPositions() const { return false; } virtual bool shouldBeParallelIfPossible() const{ return false; } ForceIndex getForceIndex() const {return index;} const GeneralForceSubsystem& getForceSubsystem() const { assert(forces); return *forces; } void setForceSubsystem(GeneralForceSubsystem& frcsub, ForceIndex ix) { forces = &frcsub; index = ix; } void invalidateTopologyCache() const { if (forces) forces->invalidateSubsystemTopologyCache(); } // Every force element must provide the next two methods. Note that // calcForce() must *add in* (+=) its forces to the given arrays. virtual void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const = 0; virtual Real calcPotentialEnergy(const State& state) const = 0; virtual void realizeTopology (State& state) const {} virtual void realizeModel (State& state) const {} virtual void realizeInstance (const State& state) const {} virtual void realizeTime (const State& state) const {} virtual void realizePosition (const State& state) const {} virtual void realizeVelocity (const State& state) const {} virtual void realizeDynamics (const State& state) const {} virtual void realizeAcceleration(const State& state) const {} virtual void realizeReport (const State& state) const {} virtual void calcDecorativeGeometryAndAppend (const State& s, Stage stage, Array_& geom) const {} private: // CONSTRUCTION GeneralForceSubsystem* forces; // just a reference; no delete on destruction ForceIndex index; // TOPOLOGY "STATE" // Changing anything here invalidates the topology of the containing // force Subsystem and thus of the whole System. // This says whether the Instance-stage "disabled" flag for this force // element should be initially on or off. Most force elements are enabled // by default. bool defaultDisabled; // TOPOLOGY "CACHE" // Nothing in the base Impl class. }; //------------------------------------------------------------------------------ // TWO POINT LINEAR SPRING IMPL //------------------------------------------------------------------------------ class Force::TwoPointLinearSpringImpl : public ForceImpl { public: TwoPointLinearSpringImpl(const MobilizedBody& body1, const Vec3& station1, const MobilizedBody& body2, const Vec3& station2, Real k, Real x0); TwoPointLinearSpringImpl* clone() const override { return new TwoPointLinearSpringImpl(*this); } bool dependsOnlyOnPositions() const override { return true; } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; void calcDecorativeGeometryAndAppend(const State& s, Stage stage, Array_& geom) const override; private: const SimbodyMatterSubsystem& matter; const MobilizedBodyIndex body1, body2; Vec3 station1, station2; Real k, x0; }; //------------------------------------------------------------------------------ // TWO POINT LINEAR DAMPER IMPL //------------------------------------------------------------------------------ class Force::TwoPointLinearDamperImpl : public ForceImpl { public: TwoPointLinearDamperImpl(const MobilizedBody& body1, const Vec3& station1, const MobilizedBody& body2, const Vec3& station2, Real damping); TwoPointLinearDamperImpl* clone() const override { return new TwoPointLinearDamperImpl(*this); } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; private: const SimbodyMatterSubsystem& matter; const MobilizedBodyIndex body1, body2; Vec3 station1, station2; Real damping; }; //------------------------------------------------------------------------------ // TWO POINT CONSTANT FORCE IMPL //------------------------------------------------------------------------------ class Force::TwoPointConstantForceImpl : public ForceImpl { public: TwoPointConstantForceImpl(const MobilizedBody& body1, const Vec3& station1, const MobilizedBody& body2, const Vec3& station2, Real force); TwoPointConstantForceImpl* clone() const override { return new TwoPointConstantForceImpl(*this); } bool dependsOnlyOnPositions() const override { return true; } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; private: const SimbodyMatterSubsystem& matter; const MobilizedBodyIndex body1, body2; Vec3 station1, station2; Real force; }; //------------------------------------------------------------------------------ // MOBILITY LINEAR SPRING IMPL //------------------------------------------------------------------------------ class Force::MobilityLinearSpringImpl : public ForceImpl { friend class MobilityLinearSpring; MobilityLinearSpringImpl(const MobilizedBody& mobod, MobilizerQIndex whichQ, Real defaultStiffness, Real defaultQZero); const std::pair& getParams(const State& state) const { return Value< std::pair >::downcast (getForceSubsystem().getDiscreteVariable(state, m_paramsIx)); } std::pair& updParams(State& state) const { return Value< std::pair >::updDowncast (getForceSubsystem().updDiscreteVariable(state, m_paramsIx)); } MobilityLinearSpringImpl* clone() const override { return new MobilityLinearSpringImpl(*this); } bool dependsOnlyOnPositions() const override {return true;} void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; // Allocate the discrete state variable for the parameters. void realizeTopology(State& s) const override { m_paramsIx = getForceSubsystem() .allocateDiscreteVariable(s, Stage::Dynamics, new Value< std::pair > (std::make_pair(m_defaultStiffness, m_defaultQZero))); } //------------------------------------------------------------------------------ // TOPOLOGY STATE const SimbodyMatterSubsystem& m_matter; const MobilizedBodyIndex m_mobodIx; const MobilizerQIndex m_whichQ; Real m_defaultStiffness; Real m_defaultQZero; // TOPOLOGY CACHE (Set only once in realizeTopology(); const thereafter.) // The value is a pair containing k,q0 mutable DiscreteVariableIndex m_paramsIx; }; //------------------------------------------------------------------------------ // MOBILITY LINEAR DAMPER IMPL //------------------------------------------------------------------------------ class Force::MobilityLinearDamperImpl : public ForceImpl { friend class MobilityLinearDamper; MobilityLinearDamperImpl(const MobilizedBody& mobod, MobilizerUIndex whichU, Real defaultDamping); const Real& getDamping(const State& state) const { return Value::downcast (getForceSubsystem().getDiscreteVariable(state, m_dampingIx)); } Real& updDamping(State& state) const { return Value::updDowncast (getForceSubsystem().updDiscreteVariable(state, m_dampingIx)); } MobilityLinearDamperImpl* clone() const override { return new MobilityLinearDamperImpl(*this); } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; // Allocate the discrete state variable for the parameters. void realizeTopology(State& s) const override { m_dampingIx = getForceSubsystem() .allocateDiscreteVariable(s, Stage::Dynamics, new Value(m_defaultDamping)); } //------------------------------------------------------------------------------ // TOPOLOGY STATE const SimbodyMatterSubsystem& m_matter; const MobilizedBodyIndex m_mobodIx; MobilizerUIndex m_whichU; Real m_defaultDamping; // TOPOLOGY CACHE (Set only once in realizeTopology(); const thereafter.) // The value is a Real containing c. mutable DiscreteVariableIndex m_dampingIx; }; //------------------------------------------------------------------------------ // MOBILITY CONSTANT FORCE IMPL //------------------------------------------------------------------------------ // Hidden implementation class for a MobilityConstantForce force element. class Force::MobilityConstantForceImpl : public ForceImpl { friend class MobilityConstantForce; MobilityConstantForceImpl(const MobilizedBody& mobod, MobilizerUIndex whichU, Real defaultForce); Real getForce(const State& state) const { return Value::downcast (getForceSubsystem().getDiscreteVariable(state, m_forceIx)); } Real& updForce(State& state) const { return Value::updDowncast (getForceSubsystem().updDiscreteVariable(state, m_forceIx)); } // Implementation of virtual methods from ForceImpl: MobilityConstantForceImpl* clone() const override { return new MobilityConstantForceImpl(*this); } // Has to wait for Dynamics stage because that's all that gets invalidated // if the constant force is changed. bool dependsOnlyOnPositions() const override {return false;} void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override {return 0;} // Allocate the discrete state variable for the force. void realizeTopology(State& s) const override { m_forceIx = getForceSubsystem() .allocateDiscreteVariable(s, Stage::Dynamics, new Value(m_defaultForce)); } //------------------------------------------------------------------------------ // TOPOLOGY STATE const SimbodyMatterSubsystem& m_matter; const MobilizedBodyIndex m_mobodIx; const MobilizerUIndex m_whichU; Real m_defaultForce; // TOPOLOGY CACHE (Set only once in realizeTopology(); const thereafter.) mutable DiscreteVariableIndex m_forceIx; }; //------------------------------------------------------------------------------ // MOBILITY LINEAR STOP IMPL //------------------------------------------------------------------------------ // Hidden implementation class for a MobilityLinearStop force element. class Force::MobilityLinearStopImpl : public ForceImpl { friend class MobilityLinearStop; // Type of the discrete state variable that holds values for this // stop's changeable parameters in a State. Since these affect only forces // the variable invalidates Dynamics stage and later when it changes. struct Parameters { Parameters(Real defStiffness, Real defDissipation, Real defQLow, Real defQHigh) : k(defStiffness), d(defDissipation), qLow(defQLow), qHigh(defQHigh) {} Parameters() = default; Real k{NaN}, d{NaN}, qLow{NaN}, qHigh{NaN}; }; MobilityLinearStopImpl(const MobilizedBody& mobod, MobilizerQIndex whichQ, Real defaultStiffness, Real defaultDissipation, Real defaultQLow, Real defaultQHigh); // Implementation of virtual methods from ForceImpl: MobilityLinearStopImpl* clone() const override { return new MobilityLinearStopImpl(*this); } bool dependsOnlyOnPositions() const override {return false;} void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; // We're not bothering to cache P.E. -- just recalculate it when asked. Real calcPotentialEnergy(const State& state) const override; // Allocate the state variables and cache entry. void realizeTopology(State& s) const override { // Allocate the discrete variable for dynamics parameters. const Parameters dv(m_defStiffness, m_defDissipation, m_defQLow, m_defQHigh); m_parametersIx = getForceSubsystem() .allocateDiscreteVariable(s, Stage::Dynamics, new Value(dv)); } const Parameters& getParameters(const State& s) const { return Value::downcast (getForceSubsystem().getDiscreteVariable(s,m_parametersIx)); } Parameters& updParameters(State& s) const { return Value::updDowncast (getForceSubsystem().updDiscreteVariable(s,m_parametersIx)); } //------------------------------------------------------------------------------ // TOPOLOGY STATE const SimbodyMatterSubsystem& m_matter; const MobilizedBodyIndex m_mobodIx; const MobilizerQIndex m_whichQ; Real m_defStiffness, m_defDissipation, m_defQLow, m_defQHigh; // TOPOLOGY CACHE (Set only once in realizeTopology(); const thereafter.) mutable DiscreteVariableIndex m_parametersIx; // k, d, qLow, qHigh }; //------------------------------------------------------------------------------ // MOBILITY DISCRETE FORCE IMPL //------------------------------------------------------------------------------ // This Force element allocates a scalar discrete variable and applies its // value as a generalized force at a particular mobility. The value can be // set externally in an event handler or between steps. class Force::MobilityDiscreteForceImpl : public ForceImpl { public: MobilityDiscreteForceImpl(const MobilizedBody& mobod, MobilizerUIndex whichU, Real defaultForce); // Change the force value to be applied. The force will remain at this // value until changed again. void setMobilityForce(State& state, Real f) const; // Get the value of the generalized force to be applied. Real getMobilityForce(const State& state) const; // Override five virtuals from base class: // This is called at Simbody's realize(Dynamics) stage. void calcForce( const State& state, Vector_& /*bodyForces*/, Vector_& /*particleForces*/, Vector& mobilityForces) const override; // This force element does not store potential energy. Real calcPotentialEnergy(const State& state) const override {return 0;} // Allocate the needed state variable and record its index. void realizeTopology(State& state) const override; MobilityDiscreteForceImpl* clone() const override { return new MobilityDiscreteForceImpl(*this); } // Force this to wait for Dynamics stage before calculating, because that's // all that gets invalidated when a new forces is applied. bool dependsOnlyOnPositions() const override {return false;} private: friend class Force::MobilityDiscreteForce; const SimbodyMatterSubsystem& m_matter; const MobilizedBodyIndex m_mobodIx; const MobilizerUIndex m_whichU; Real m_defaultVal; mutable DiscreteVariableIndex m_forceIx; }; //------------------------------------------------------------------------------ // DISCRETE FORCES IMPL //------------------------------------------------------------------------------ // This Force element allocates a Vector discrete variables and applies their // values as generalized and body spatial forces. The values can be // set externally in an event handler or between steps. class Force::DiscreteForcesImpl : public ForceImpl { public: DiscreteForcesImpl(const SimbodyMatterSubsystem& matter); const Vector& getAllMobilityForces(const State& state) const; Vector& updAllMobilityForces(State& state) const; const Vector_& getAllBodyForces(const State& state) const; Vector_& updAllBodyForces(State& state) const; // Override five virtuals from base class: // This is called at Simbody's realize(Dynamics) stage. void calcForce( const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; // This force element does not store potential energy. Real calcPotentialEnergy(const State& state) const override {return 0;} // Allocate the needed state variable and record its index. void realizeTopology(State& state) const override; DiscreteForcesImpl* clone() const override { return new DiscreteForcesImpl(*this); } // Force this to wait for Dynamics stage before calculating, because that's // all that gets invalidated when a new forces is applied. bool dependsOnlyOnPositions() const override {return false;} private: friend class Force::DiscreteForces; const SimbodyMatterSubsystem& m_matter; mutable DiscreteVariableIndex m_mobForcesIx; // Vector(n) mutable DiscreteVariableIndex m_bodyForcesIx; // Vector_(nb) }; //------------------------------------------------------------------------------ // CONSTANT FORCE IMPL //------------------------------------------------------------------------------ class Force::ConstantForceImpl : public ForceImpl { public: ConstantForceImpl(const MobilizedBody& body, const Vec3& station, const Vec3& force); ConstantForceImpl* clone() const override { return new ConstantForceImpl(*this); } bool dependsOnlyOnPositions() const override { return true; } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; private: const SimbodyMatterSubsystem& matter; const MobilizedBodyIndex body; Vec3 station, force; }; //------------------------------------------------------------------------------ // CONSTANT TORQUE IMPL //------------------------------------------------------------------------------ class Force::ConstantTorqueImpl : public ForceImpl { public: ConstantTorqueImpl(const MobilizedBody& body, const Vec3& torque); ConstantTorqueImpl* clone() const override { return new ConstantTorqueImpl(*this); } bool dependsOnlyOnPositions() const override { return true; } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; private: const SimbodyMatterSubsystem& matter; const MobilizedBodyIndex body; Vec3 torque; }; //------------------------------------------------------------------------------ // GLOBAL DAMPER IMPL //------------------------------------------------------------------------------ class Force::GlobalDamperImpl : public ForceImpl { public: GlobalDamperImpl(const SimbodyMatterSubsystem& matter, Real damping); GlobalDamperImpl* clone() const override { return new GlobalDamperImpl(*this); } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; private: const SimbodyMatterSubsystem& matter; Real damping; }; //------------------------------------------------------------------------------ // UNIFORM GRAVITY IMPL //------------------------------------------------------------------------------ class Force::UniformGravityImpl : public ForceImpl { public: UniformGravityImpl(const SimbodyMatterSubsystem& matter, const Vec3& g, Real zeroHeight); UniformGravityImpl* clone() const override { return new UniformGravityImpl(*this); } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; Vec3 getGravity() const { return g; } void setGravity(const Vec3& gravity) { g = gravity; invalidateTopologyCache(); } Real getZeroHeight() const { return zeroHeight; } void setZeroHeight(Real height) { zeroHeight = height; invalidateTopologyCache(); } private: const SimbodyMatterSubsystem& matter; Vec3 g; Real zeroHeight; }; //------------------------------------------------------------------------------ // CUSTOM IMPL //------------------------------------------------------------------------------ class Force::CustomImpl : public ForceImpl { public: CustomImpl(Force::Custom::Implementation* implementation); CustomImpl* clone() const override { return new CustomImpl(*this); } bool dependsOnlyOnPositions() const override { return implementation->dependsOnlyOnPositions(); } void calcForce(const State& state, Vector_& bodyForces, Vector_& particleForces, Vector& mobilityForces) const override; Real calcPotentialEnergy(const State& state) const override; bool shouldBeParallelIfPossible() const override { return implementation->shouldBeParallelIfPossible(); } ~CustomImpl() { delete implementation; } const Force::Custom::Implementation& getImplementation() const { return *implementation; } Force::Custom::Implementation& updImplementation() { return *implementation; } protected: void realizeTopology(State& state) const override { implementation->realizeTopology(state); } void realizeModel(State& state) const override { implementation->realizeModel(state); } void realizeInstance(const State& state) const override { implementation->realizeInstance(state); } void realizeTime(const State& state) const override { implementation->realizeTime(state); } void realizePosition(const State& state) const override { implementation->realizePosition(state); } void realizeVelocity(const State& state) const override { implementation->realizeVelocity(state); } void realizeDynamics(const State& state) const override { implementation->realizeDynamics(state); } void realizeAcceleration(const State& state) const override { implementation->realizeAcceleration(state); } void realizeReport(const State& state) const override { implementation->realizeReport(state); } void calcDecorativeGeometryAndAppend(const State& state, Stage stage, Array_& geom) const override { implementation->calcDecorativeGeometryAndAppend(state,stage,geom); } private: Force::Custom::Implementation* implementation; }; } // namespace SimTK #endif // SimTK_SIMBODY_FORCE_IMPL_H_