Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MeshSpringForceField Does Not Support Index-Based Spring Creation #5135

Closed
antennae opened this issue Nov 21, 2024 · 6 comments
Closed

MeshSpringForceField Does Not Support Index-Based Spring Creation #5135

antennae opened this issue Nov 21, 2024 · 6 comments
Assignees
Labels
issue: bug (minor) Bug affecting only some users or with no major impact on the framework

Comments

@antennae
Copy link

Problem

Description
MeshSpringForceField fails to create springs based on specified indices. Instead, it always derives the springs directly from the topology.

This behavior appears to have changed following PR #4649.

Previously:

  • MeshSpringForceField::init() created springs based on topology, then called StiffSpringForceField::init().
  • StiffSpringForceField::init() replaced these topology-based springs with new ones derived from the specified indices.

Currently:

  • MeshSpringForceField::init() still creates springs based on topology, but now calls SpringForceField::init().
  • SpringForceField::init() keeps the topology-based springs, basically ignoring any specified indices.

As a result, it is no longer possible to create springs based on indices using MeshSpringForceField. However, SOFA does not provide a warning or error when indices are specified but unused, which can lead to confusion.

Steps to reproduce
Attempt to add springs using MeshSpringForceField by specifying indices and lengths.

Expected behavior
Springs should be created using the specified indices and lengths.


Example

Context

  • System: Ubuntu 24.04
  • Version of SOFA:master branch at commit 4bfb1d5

Below are screenshots comparing the behavior of MeshSpringForceField and SpringForceField when trying to create a spring between points 1 and 3.

using MeshSpringForceField:
Screenshot from 2024-11-21 16-28-44

using SpringForceField:
Screenshot from 2024-11-21 16-28-34

Example Scene:

import Sofa


def createScene(rootNode):
    # required plugins:
    rootNode.addObject("RequiredPlugin", name="Sofa.Component.AnimationLoop")
    rootNode.addObject(
        "RequiredPlugin",
        name="Sofa.Component.Constraint.Lagrangian.Correction",
    )
    rootNode.addObject(
        "RequiredPlugin", name="Sofa.Component.Constraint.Lagrangian.Solver"
    )
    rootNode.addObject("RequiredPlugin", name="Sofa.Component.IO.Mesh")
    rootNode.addObject(
        "RequiredPlugin", name="Sofa.Component.LinearSolver.Direct"
    )
    rootNode.addObject(
        "RequiredPlugin", name="Sofa.Component.ODESolver.Backward"
    )
    rootNode.addObject(
        "RequiredPlugin", name="Sofa.Component.SolidMechanics.Spring"
    )
    rootNode.addObject("RequiredPlugin", name="Sofa.Component.StateContainer")
    rootNode.addObject(
        "RequiredPlugin", name="Sofa.Component.Topology.Container.Constant"
    )
    rootNode.findData("gravity").value = [0, 0, 0]
    rootNode.addObject(
        "VisualStyle",
        displayFlags="showVisualModels showBehaviorModels showCollisionModels \
            hideBoundingCollisionModels showForceFields \
                showInteractionForceFields hideWireframe",
    )
    rootNode.findData("dt").value = 0.01
    rootNode.addObject("FreeMotionAnimationLoop")
    rootNode.addObject(
        "GenericConstraintSolver",
        name="ConstraintSolver",
        tolerance=1e-10,
        maxIterations=2000,
        multithreading=True,
    )

    solver_node = rootNode.addChild("solver_node")
    solver_node.addObject(
        "EulerImplicitSolver",
        firstOrder=False,
        rayleighStiffness=0.05,
        rayleighMass=0.02,
    )
    solver_node.addObject(
        "SparseLDLSolver",
        name="Solver",
        template="CompressedRowSparseMatrixd",
    )
    solver_node.addObject(
        "GenericConstraintCorrection",
        name="ConstraintCorrection",
        linearSolver=solver_node.Solver.getLinkPath(),
    )
    solver_node.addObject(
        "MeshSTLLoader",
        name="SurfaceMeshLoader",
        filename="./test.stl",
    )
    solver_node.addObject(
        "MeshTopology", name="topology", src="@SurfaceMeshLoader"
    )
    solver_node.addObject(
        "MechanicalObject",
        name="states",
        showObject=True,
        showIndices=True,
        showIndicesScale=0.1,
    )
    solver_node.addObject(
        "MeshSpringForceField",
        name="spring",
        stiffness=1000,
        indices1=1,
        indices2=3,
    )
    # solver_node.addObject(
    #     "SpringForceField",
    #     name="spring",
    #     stiffness=1000,
    #     indices1=1,
    #     indices2=3,
    #     lengths=2.82843,
    # )

Example Mesh:

solid 
facet normal 0 0 1
 outer loop
  vertex -1 -1 0
  vertex 1 -1 0
  vertex 1 1 0
 endloop
endfacet
facet normal -0 0 1
 outer loop
  vertex -1 -1 0
  vertex 1 1 0
  vertex -1 1 0
 endloop
endfacet
endsolid 
Loading

Proposed fix

  • Restore support for creating springs using indices in MeshSpringForceField.
  • Or introduce a warning when indices1 and indices2 are specified but ignored.
@antennae antennae added the issue: bug (minor) Bug affecting only some users or with no major impact on the framework label Nov 21, 2024
@bakpaul
Copy link
Contributor

bakpaul commented Nov 21, 2024

Hello,

It seems like the SpringForceField is doing what you expect from the MeshSpringForcefield. The MeshSpringForcefield, as its name states, is meant to add springs on edges based on the mesh. The mesh is its input data to create the springs. If you don't want to use the mesh to define the springs and want to define them by hand, then you should use the SpringForceField.

Maybe I didn't understand your need well, if so, please tell me why you can't use the SpringForceField and need to use the MeshSpringForcefield but without taking the mesh information into account ?

@damienmarchal
Copy link
Contributor

Hello,

It seems like the SpringForceField is doing what you expect from the MeshSpringForcefield. The MeshSpringForcefield, as its name states, is meant to add springs on edges based on the mesh. The mesh is its input data to create the springs. If you don't want to use the mesh to define the springs and want to define them by hand, then you should use the SpringForceField.

Maybe I didn't understand your need well, if so, please tell me why you can't use the SpringForceField and need to use the MeshSpringForcefield but without taking the mesh information into account ?

I think the point is that the behavior changed on MeshSpringForceField between the old and new version without a warning to users.

@antennae
Copy link
Author

antennae commented Nov 21, 2024

Hi,

Yes, SpringForceField is doing what i expected and it works well.

I was actually working with a scene that was created in an older SOFA version, which creates springs using indices with MeshSpringForceField. And in the newer SOFA version, the behaviour of MeshSpringForceField changed and the scene doesn't work anymore. I have to do some research to find out why.
So in my opinion, this behaviour change can be confusing for users working with older scenes and a clearer guidance could be very useful.

@bakpaul
Copy link
Contributor

bakpaul commented Nov 21, 2024

OK I get it, we can add a warning message in the init of MeshSpringForcefield to warn when the data is set. Thank you !

@bakpaul
Copy link
Contributor

bakpaul commented Nov 21, 2024

TEll me if that solves you issue : #5136

@antennae
Copy link
Author

Thanks for the updates! I just tested and it did solve my issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue: bug (minor) Bug affecting only some users or with no major impact on the framework
Projects
None yet
Development

No branches or pull requests

3 participants