|
| 1 | +#include <gtest/gtest.h> |
| 2 | +#include <sofa/core/behavior/BaseForceField.h> |
| 3 | +#include <sofa/core/behavior/BaseInteractionForceField.h> |
| 4 | +#include <sofa/core/behavior/BaseMass.h> |
| 5 | +#include <sofa/simpleapi/SimpleApi.h> |
| 6 | +#include <sofa/simulation/MechanicalVisitor.h> |
| 7 | +#include <sofa/simulation/Node.h> |
| 8 | + |
| 9 | +namespace sofa |
| 10 | +{ |
| 11 | + |
| 12 | +namespace |
| 13 | +{ |
| 14 | +/** |
| 15 | + * A visitor counting the number of force fields, mappings and masses visited. |
| 16 | + * This visitor considers interaction force fields as BaseForceField, so they are counted as force |
| 17 | + * fields. |
| 18 | + */ |
| 19 | +class TestVisitorWithoutInteractionForceField : public simulation::MechanicalVisitor |
| 20 | +{ |
| 21 | +public: |
| 22 | + using simulation::MechanicalVisitor::MechanicalVisitor; |
| 23 | + |
| 24 | + Result fwdMechanicalMapping(simulation::Node* /*node*/, sofa::core::BaseMapping* map) override |
| 25 | + { |
| 26 | + ++m_mappingCount; |
| 27 | + return Result::RESULT_CONTINUE; |
| 28 | + } |
| 29 | + |
| 30 | + Result fwdForceField(simulation::Node*, sofa::core::behavior::BaseForceField*) override |
| 31 | + { |
| 32 | + ++m_forceFieldCount; |
| 33 | + return Result::RESULT_CONTINUE; |
| 34 | + } |
| 35 | + |
| 36 | + Result fwdMass(simulation::Node*, sofa::core::behavior::BaseMass*) override |
| 37 | + { |
| 38 | + ++m_massCount; |
| 39 | + return Result::RESULT_CONTINUE; |
| 40 | + } |
| 41 | + |
| 42 | + std::size_t m_forceFieldCount {}; |
| 43 | + std::size_t m_interactionForceFieldCount {}; |
| 44 | + std::size_t m_massCount {}; |
| 45 | + std::size_t m_mappingCount {}; |
| 46 | +}; |
| 47 | + |
| 48 | +/** |
| 49 | + * A visitor counting the number of force fields, interaction force fields, mappings and masses visited. |
| 50 | + * This visitor considers interaction force fields as BaseInteractionForceField, so they are counted |
| 51 | + * as interaction force fields, not force fields. |
| 52 | + */ |
| 53 | +class TestVisitorWithInteractionForceField : public simulation::MechanicalVisitor |
| 54 | +{ |
| 55 | +public: |
| 56 | + using simulation::MechanicalVisitor::MechanicalVisitor; |
| 57 | + |
| 58 | + Result fwdMechanicalMapping(simulation::Node*, sofa::core::BaseMapping* map) override |
| 59 | + { |
| 60 | + ++m_mappingCount; |
| 61 | + return Result::RESULT_CONTINUE; |
| 62 | + } |
| 63 | + |
| 64 | + Result fwdForceField(simulation::Node*, sofa::core::behavior::BaseForceField*) override |
| 65 | + { |
| 66 | + ++m_forceFieldCount; |
| 67 | + return Result::RESULT_CONTINUE; |
| 68 | + } |
| 69 | + |
| 70 | + Result fwdInteractionForceField(simulation::Node*, sofa::core::behavior::BaseInteractionForceField*) override |
| 71 | + { |
| 72 | + ++m_interactionForceFieldCount; |
| 73 | + return Result::RESULT_CONTINUE; |
| 74 | + } |
| 75 | + |
| 76 | + Result fwdMass(simulation::Node*, sofa::core::behavior::BaseMass*) override |
| 77 | + { |
| 78 | + ++m_massCount; |
| 79 | + return Result::RESULT_CONTINUE; |
| 80 | + } |
| 81 | + |
| 82 | + std::size_t m_forceFieldCount {}; |
| 83 | + std::size_t m_interactionForceFieldCount {}; |
| 84 | + std::size_t m_massCount {}; |
| 85 | + std::size_t m_mappingCount {}; |
| 86 | +}; |
| 87 | + |
| 88 | +class TestForceField : public core::behavior::BaseForceField |
| 89 | +{ |
| 90 | +public: |
| 91 | + void addForce(const core::MechanicalParams* mparams, core::MultiVecDerivId fId) override {} |
| 92 | + void addDForce(const core::MechanicalParams* mparams, core::MultiVecDerivId dfId) override {} |
| 93 | + SReal getPotentialEnergy(const core::MechanicalParams* mparams) const override { return {}; } |
| 94 | + void addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override {} |
| 95 | +}; |
| 96 | + |
| 97 | +class TestMass : public core::behavior::BaseMass |
| 98 | +{ |
| 99 | +public: |
| 100 | + void addMDx(const core::MechanicalParams* mparams, core::MultiVecDerivId fid, SReal factor) override {} |
| 101 | + void accFromF(const core::MechanicalParams* mparams, core::MultiVecDerivId aid) override {} |
| 102 | + void addGravityToV(const core::MechanicalParams* mparams, core::MultiVecDerivId vid) override {} |
| 103 | + SReal getKineticEnergy(const core::MechanicalParams* mparams) const override { return {}; } |
| 104 | + SReal getPotentialEnergy(const core::MechanicalParams* mparams) const override { return {}; } |
| 105 | + type::Vec6 getMomentum(const core::MechanicalParams* mparams) const override { return {}; } |
| 106 | + void addMToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override {} |
| 107 | + void initGnuplot(const std::string path) override {} |
| 108 | + void exportGnuplot(const core::MechanicalParams* mparams, SReal time) override {} |
| 109 | + SReal getElementMass(sofa::Index index) const override { return {}; } |
| 110 | + void getElementMass(sofa::Index index, linearalgebra::BaseMatrix* m) const override {} |
| 111 | + bool isDiagonal() const override { return {}; } |
| 112 | +}; |
| 113 | + |
| 114 | +class TestInteractionForceField : public core::behavior::BaseInteractionForceField |
| 115 | +{ |
| 116 | +public: |
| 117 | + void addForce(const core::MechanicalParams* mparams, core::MultiVecDerivId fId) override {} |
| 118 | + void addDForce(const core::MechanicalParams* mparams, core::MultiVecDerivId dfId) override {} |
| 119 | + SReal getPotentialEnergy(const core::MechanicalParams* mparams) const override { return {}; } |
| 120 | +}; |
| 121 | + |
| 122 | +class TestMapping : public core::BaseMapping |
| 123 | +{ |
| 124 | +public: |
| 125 | + void apply(const core::MechanicalParams* mparams, core::MultiVecCoordId outPos, core::ConstMultiVecCoordId inPos) override {} |
| 126 | + void applyJ(const core::MechanicalParams* mparams, core::MultiVecDerivId outVel, core::ConstMultiVecDerivId inVel) override {} |
| 127 | + type::vector<core::BaseState*> getFrom() override { return {} ;} |
| 128 | + type::vector<core::BaseState*> getTo() override { return {} ;} |
| 129 | + void applyJT(const core::MechanicalParams* mparams, core::MultiVecDerivId inForce, core::ConstMultiVecDerivId outForce) override {} |
| 130 | + void applyDJT(const core::MechanicalParams* mparams, core::MultiVecDerivId inForce, core::ConstMultiVecDerivId outForce) override {} |
| 131 | + void applyJT(const core::ConstraintParams* mparams, core::MultiMatrixDerivId inConst, core::ConstMultiMatrixDerivId outConst) override {} |
| 132 | + void computeAccFromMapping(const core::MechanicalParams* mparams, core::MultiVecDerivId outAcc, core::ConstMultiVecDerivId inVel, core::ConstMultiVecDerivId inAcc) override {} |
| 133 | + type::vector<core::behavior::BaseMechanicalState*> getMechFrom() override { return {} ;} |
| 134 | + type::vector<core::behavior::BaseMechanicalState*> getMechTo() override { return {} ;} |
| 135 | + void disable() override {} |
| 136 | +}; |
| 137 | + |
| 138 | +sofa::simulation::Node::SPtr makeSceneGraph() |
| 139 | +{ |
| 140 | + const sofa::simulation::Node::SPtr root = sofa::simpleapi::createRootNode(sofa::simulation::getSimulation(), "root"); |
| 141 | + |
| 142 | + const auto ff_0 = sofa::core::objectmodel::New<TestForceField>(); |
| 143 | + root->addObject(ff_0); |
| 144 | + |
| 145 | + const auto mass_0 = sofa::core::objectmodel::New<TestMass>(); |
| 146 | + root->addObject(mass_0); |
| 147 | + |
| 148 | + const auto iff_0 = sofa::core::objectmodel::New<TestInteractionForceField>(); |
| 149 | + root->addObject(iff_0); |
| 150 | + |
| 151 | + const auto mapping_0 = sofa::core::objectmodel::New<TestMapping>(); |
| 152 | + root->addObject(mapping_0); |
| 153 | + |
| 154 | + auto child_0 = root->createChild("child_0"); |
| 155 | + |
| 156 | + // mapping_0 is in two different nodes |
| 157 | + child_0->addObject(mapping_0); |
| 158 | + |
| 159 | + auto child_1 = root->createChild("child_1"); |
| 160 | + |
| 161 | + auto child_2 = child_1->createChild("child_2"); |
| 162 | + // diamond graph |
| 163 | + child_0->addChild(child_2); |
| 164 | + |
| 165 | + // the two components are in a Node with two parents. We need to check that they are visited |
| 166 | + // only once |
| 167 | + const auto mass_1 = sofa::core::objectmodel::New<TestMass>(); |
| 168 | + child_2->addObject(mass_1); |
| 169 | + |
| 170 | + const auto iff_1 = sofa::core::objectmodel::New<TestInteractionForceField>(); |
| 171 | + child_2->addObject(iff_1); |
| 172 | + |
| 173 | + return root; |
| 174 | +} |
| 175 | + |
| 176 | +} |
| 177 | + |
| 178 | +TEST(Visitor, ComplexGraph_visitorWithoutInteractionForceField) |
| 179 | +{ |
| 180 | + const auto root = makeSceneGraph(); |
| 181 | + |
| 182 | + TestVisitorWithoutInteractionForceField visitor( |
| 183 | + sofa::core::MechanicalParams::defaultInstance()); |
| 184 | + |
| 185 | + static constexpr bool precomputedOrder = false; |
| 186 | + root->executeVisitor(&visitor, precomputedOrder); |
| 187 | + |
| 188 | + // the visitor considers the interaction force field as a BaseForceField |
| 189 | + EXPECT_EQ(visitor.m_forceFieldCount, 3); |
| 190 | + EXPECT_EQ(visitor.m_interactionForceFieldCount, 0); |
| 191 | + EXPECT_EQ(visitor.m_massCount, 2); |
| 192 | + EXPECT_EQ(visitor.m_mappingCount, 1); |
| 193 | +} |
| 194 | + |
| 195 | +TEST(Visitor, ComplexGraph_visitorWithInteractionForceField) |
| 196 | +{ |
| 197 | + const auto root = makeSceneGraph(); |
| 198 | + |
| 199 | + TestVisitorWithInteractionForceField visitor(sofa::core::MechanicalParams::defaultInstance()); |
| 200 | + |
| 201 | + static constexpr bool precomputedOrder = false; |
| 202 | + root->executeVisitor(&visitor, precomputedOrder); |
| 203 | + |
| 204 | + EXPECT_EQ(visitor.m_forceFieldCount, 1); |
| 205 | + EXPECT_EQ(visitor.m_interactionForceFieldCount, 2); |
| 206 | + EXPECT_EQ(visitor.m_massCount, 2); |
| 207 | + EXPECT_EQ(visitor.m_mappingCount, 1); |
| 208 | +} |
| 209 | + |
| 210 | +} |
0 commit comments