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

PolynomialPathFitter and other utilities for FunctionBasedPath #3581

Merged
merged 56 commits into from
Nov 8, 2023

Conversation

nickbianco
Copy link
Member

@nickbianco nickbianco commented Oct 20, 2023

Fixes issue #3390

Brief summary of changes

This PR introduces PolynomialPathFitter, a new utility class for creating FunctionBasedPaths by fitting the coefficients of MultivariatePolynomialFunctions to path length and moment arm samples taken from a model with geometry-based paths. Given a model, a table of coordinate values, and a set of (optional) user settings, this class automatically samples path lengths and moment arms from the model based on Latin hypercube sampling and determins the polynomial coefficients using a least-squares fit. In addition to the new class, various other smaller changes are included to make everything work:

  • Added ModelFactor::replacePathsWithFunctionBasedPaths and ModOpReplacePathsWithFunctionBasedPaths.
  • Added calcTermValues and calcTermDerivatives to MultivariatePolynomialFunction.
  • Added Object::getProperty(const std::string& name) and Object::updProperty(const std::string& name).
  • Added TypeHelpers to Property for SimTK::Vec2 types.
  • Added SimulationUtilities::appendCoupledCoordinateValues and TabOpAppendCoupledCoordinateValues.
  • Added SimulationUtilities::appendCoordinateValueDerivatives and TabOpAppendCoordinateValueDerivatives.
  • Minor updates to FunctionBasedPath.
  • Other various minor drive-by fixes that I encountered along the way.

Testing I've completed

Added testPolynomialPathFitter.cpp.

Looking for feedback on...

Anything and everything.

CHANGELOG.md (choose one)

  • updated.

This change is Reviewable

@nickbianco nickbianco changed the title [WIP] PolynomialPathFitter and other utilities for FunctionBasedPath PolynomialPathFitter and other utilities for FunctionBasedPath Oct 20, 2023
Copy link
Member

@aymanhab aymanhab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 5 of 29 files at r1, all commit messages.
Reviewable status: 5 of 29 files reviewed, 1 unresolved discussion (waiting on @adamkewley and @nickbianco)

a discussion (no related file):
I think this will be extremely useful to make the adoption easier 👍 . Some questions that may have been addressed or documented elsewhere that I found myself asking with every call so would be good to clear up or if you can point to existing documentation if exists already:

  1. Do you have to do the replacement for all paths or you can be selective and do mix of GeometryBased and FunctionBased?
  2. Are there checks that the functions passed-in correspond and map to components that are actually GeometryBased paths, nothing extra, and nothing missing (depending on previous item on this list).
  3. Assuming both paths exist in the model at some point, using _path as a suffix seems more confusing when we call both classes "*Path", I'd use something that includes "function" or "fit" unless I'm not clear on what's being named/renamed.
    Will proceed with the review once I understand the context based on the above. Thanks

Copy link
Member Author

@nickbianco nickbianco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 5 of 29 files reviewed, 1 unresolved discussion (waiting on @adamkewley and @aymanhab)

a discussion (no related file):

Previously, aymanhab (Ayman Habib) wrote…

I think this will be extremely useful to make the adoption easier 👍 . Some questions that may have been addressed or documented elsewhere that I found myself asking with every call so would be good to clear up or if you can point to existing documentation if exists already:

  1. Do you have to do the replacement for all paths or you can be selective and do mix of GeometryBased and FunctionBased?
  2. Are there checks that the functions passed-in correspond and map to components that are actually GeometryBased paths, nothing extra, and nothing missing (depending on previous item on this list).
  3. Assuming both paths exist in the model at some point, using _path as a suffix seems more confusing when we call both classes "*Path", I'd use something that includes "function" or "fit" unless I'm not clear on what's being named/renamed.
    Will proceed with the review once I understand the context based on the above. Thanks
  1. You can be selective. We save the fitted polynomial paths to an XML file, so you can modify the XML file or load specific paths in to the model with the API. We could also add an exclude_paths option on ModOpReplacePathWithFunctionBasedPaths.
  2. This class will only fit paths that derive from AbstractPath and (currently) will throw an exception if the model passed in already has FunctionBasedPaths. We could be more granular and only fit paths that are GeometryPath (or other future geometry-based paths), but for now I just assume that users do not intend to pass in a model with a FunctionBasedPath.
  3. By both paths, do you mean supporting both a GeometryPath and a FunctionBasedPath for the same force object? Then yes that name convention might be confusing. But that could be handled in a future PR I think.

Copy link
Member

@aymanhab aymanhab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 21 of 29 files at r1.
Reviewable status: 26 of 29 files reviewed, 10 unresolved discussions (waiting on @adamkewley and @nickbianco)

a discussion (no related file):

Previously, nickbianco (Nicholas Bianco) wrote…
  1. You can be selective. We save the fitted polynomial paths to an XML file, so you can modify the XML file or load specific paths in to the model with the API. We could also add an exclude_paths option on ModOpReplacePathWithFunctionBasedPaths.
  2. This class will only fit paths that derive from AbstractPath and (currently) will throw an exception if the model passed in already has FunctionBasedPaths. We could be more granular and only fit paths that are GeometryPath (or other future geometry-based paths), but for now I just assume that users do not intend to pass in a model with a FunctionBasedPath.
  3. By both paths, do you mean supporting both a GeometryPath and a FunctionBasedPath for the same force object? Then yes that name convention might be confusing. But that could be handled in a future PR I think.

Thanks for the clarification @nickbianco now I can proceed.



OpenSim/Actuators/ModelFactory.cpp line 318 at r1 (raw file):

            
        // Get the force component associated with this path.
        OPENSIM_THROW_IF(!model.hasComponent<Force>(path.getName()), 

Have you considered using Path or AbstractPath instead of Force? If so we use more specific class in updComponent<> here so we don't end up catching unrelated components? I think there're methods to go from Path to the owner component safely and you're only using Object level methods anyway.


OpenSim/Actuators/PolynomialPathFitter.h line 46 at r1 (raw file):

    OpenSim_DECLARE_PROPERTY(coordinate_path, std::string,
            "The path to the bounded coordinate in the model.")
    OpenSim_DECLARE_PROPERTY(bounds, SimTK::Vec2,

coordinate may not be "bounded" in the model, would it be clearer to say path to the coordinate to be bounded during fitting?


OpenSim/Actuators/PolynomialPathFitter.h line 80 at r1 (raw file):

 * `setMomentArmsThreshold` method determines whether or not a path depends on a
 * model coordinate. In other words, the moment arm of a path with respect to a
 * particular coordinate must be greater than this value to be included during

using absolute value?


OpenSim/Actuators/PolynomialPathFitter.h line 97 at r1 (raw file):

 * the Latin hypercube sampling algorithm used to sample coordinate values for
 * path fitting.
 *

With all these tolerances I'm assuming the model is assumed to be in meters so a model of a small bird or a dinosaur may not work out of the box? Is this something to account for or include a disclaimer?


OpenSim/Actuators/PolynomialPathFitter.h line 400 at r1 (raw file):

    void setParallel(int numThreads);
    /// @copydoc setParallel()
    int getParallel() const;

The name setParallel is a bit unexpected naming convention, I'd expect either a bool or numParallelThreads(number) but if this convention is used elsewhere in Moco then would be fine to keep


OpenSim/Common/MultivariatePolynomialFunction.h line 103 at r1 (raw file):

    /// Get a vector of the terms in the polynomial function.
    SimTK::Vector getTermValues(const SimTK::Vector& x) const;
    /// Get a vector of the derivatives of the terms in the polynomial function.

Do you think returning a fresh copy could be a performance bottleneck? Is this exercised during simulation?


OpenSim/Common/Object.h line 347 at r1 (raw file):

    template <class T> const Property<T>&
    getProperty(const std::string& name) const;

For consistency with naming of other methods in the file I'd call this method getPropertyByName. Same for other new methods that do name lookup. Curious why this was needed now


OpenSim/Common/Object.h line 355 at r1 (raw file):

    /** @copydoc updProperty(const PropertyIndex&) **/
    template <class T> Property<T>&
    updProperty(const std::string& name);

This looks fine but I'm wondering if this is part of the drive-by changes or are relevant to this PR functionality. Thank you @nickbianco


OpenSim/Simulation/SimulationUtilities.h line 401 at r1 (raw file):

        TimeSeriesTable& table, const Model& model,
        bool overwriteExistingColumns = true);

Should the method name include "speeds" vs "derivatives"? Trying to consolidate the comment with the naming

@adamkewley
Copy link
Contributor

Apologies for the delay on looking at this, but I've been a bit busy, and now I'm away

I'd nominate @pepbos to have a quick look, given that he's interested in similar ideas etc. and knows his way around C++ 😉

@nickbianco nickbianco requested review from pepbos and removed request for adamkewley October 25, 2023 20:04
Copy link
Member Author

@nickbianco nickbianco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 20 of 29 files reviewed, 10 unresolved discussions (waiting on @aymanhab and @pepbos)

a discussion (no related file):

Previously, aymanhab (Ayman Habib) wrote…

Thanks for the clarification @nickbianco now I can proceed.

Thanks @aymanhab, ready for review again.



OpenSim/Actuators/ModelFactory.cpp line 318 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

Have you considered using Path or AbstractPath instead of Force? If so we use more specific class in updComponent<> here so we don't end up catching unrelated components? I think there're methods to go from Path to the owner component safely and you're only using Object level methods anyway.

The problem is that AbstractPaths usually do not have unique names/paths in the model. So I could search through all AbstractPaths in the model, but I'd end up having to check the Force name anyway. The subsequent line checks if the path property exists in the component.


OpenSim/Actuators/PolynomialPathFitter.h line 46 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

coordinate may not be "bounded" in the model, would it be clearer to say path to the coordinate to be bounded during fitting?

Done.


OpenSim/Actuators/PolynomialPathFitter.h line 80 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

using absolute value?

Done.


OpenSim/Actuators/PolynomialPathFitter.h line 97 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

With all these tolerances I'm assuming the model is assumed to be in meters so a model of a small bird or a dinosaur may not work out of the box? Is this something to account for or include a disclaimer?

Done.


OpenSim/Actuators/PolynomialPathFitter.h line 400 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

The name setParallel is a bit unexpected naming convention, I'd expect either a bool or numParallelThreads(number) but if this convention is used elsewhere in Moco then would be fine to keep

Done.


OpenSim/Common/MultivariatePolynomialFunction.h line 103 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

Do you think returning a fresh copy could be a performance bottleneck? Is this exercised during simulation?

I'm returning a temporary SimTK::Vector constructed inside this method, so returning it like this should be optimized by the compiler.

https://stackoverflow.com/questions/4986673/is-it-necessary-to-stdmove-in-a-return-statement-and-should-you-return-rvalue


OpenSim/Common/Object.h line 347 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

For consistency with naming of other methods in the file I'd call this method getPropertyByName. Same for other new methods that do name lookup. Curious why this was needed now

Done.


OpenSim/Common/Object.h line 355 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

This looks fine but I'm wondering if this is part of the drive-by changes or are relevant to this PR functionality. Thank you @nickbianco

They are used in PolynomialPathFitter and ModelFactory::replacePathsWithFunctionBasedPaths.


OpenSim/Simulation/SimulationUtilities.h line 401 at r1 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

Should the method name include "speeds" vs "derivatives"? Trying to consolidate the comment with the naming

Added the "AsSpeeds" suffix for clarity.

Copy link
Member

@aymanhab aymanhab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @nickbianco Just a couple clarifications and then it's ready to go 👍

Reviewed 9 of 9 files at r2, all commit messages.
Reviewable status: all files reviewed, 8 unresolved discussions (waiting on @nickbianco and @pepbos)


OpenSim/Actuators/PolynomialPathFitter.h line 157 at r2 (raw file):

 *
 * It is highly recommended to use the files printed to the output directory to
 * evaluate the quality of the fitted paths. Depending on the quality of the

Maybe documented else where but a pointer to what files are printed and how to use would be helpful.


OpenSim/Actuators/PolynomialPathFitter.h line 190 at r2 (raw file):

     * the model to contain at least one path object derived from `AbstractPath`
     * and does not already contain any `FunctionBasedPath` objects. The bounds
     * for clamped coordinates are obeyed during the fitting process. Locked

To clarify, is the implication that users can't convert to FunctionBasedPaths one muscle/path at a time? Definitely this is cleaner/easier for book-keeping and they can iteratively add and refit, just confirming supported workflow.


OpenSim/Actuators/PolynomialPathFitter.h line 309 at r2 (raw file):

    /**
     * The global bounds (in degrees) that determine the minimum and maximum

Anything to say here about translational coordinates?


OpenSim/Actuators/PolynomialPathFitter.cpp line 2 at r2 (raw file):

/* -------------------------------------------------------------------------- *
 *                    OpenSim:  PolynomialPathFitter.h                        *

.cpp :)


OpenSim/Actuators/Test/testPolynomialPathFitter.cpp line 131 at r2 (raw file):

        REQUIRE_THROWS_WITH(fitter.run(), Catch::Contains(
            "Expected 'maximum_polynomial_order' to be at most 9"));
    }

Very nicely done/tested @nickbianco

Copy link
Member Author

@nickbianco nickbianco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aymanhab ready for review.

Reviewable status: 27 of 29 files reviewed, 8 unresolved discussions (waiting on @aymanhab and @pepbos)


OpenSim/Actuators/PolynomialPathFitter.h line 157 at r2 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

Maybe documented else where but a pointer to what files are printed and how to use would be helpful.

Done.


OpenSim/Actuators/PolynomialPathFitter.h line 190 at r2 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

To clarify, is the implication that users can't convert to FunctionBasedPaths one muscle/path at a time? Definitely this is cleaner/easier for book-keeping and they can iteratively add and refit, just confirming supported workflow.

Yes, that correct. However, every path is fitted independently, so it would be easy enough for users to remove muscles from the model that they do not want in the fitting process.


OpenSim/Actuators/PolynomialPathFitter.h line 309 at r2 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

Anything to say here about translational coordinates?

Done.


OpenSim/Actuators/PolynomialPathFitter.cpp line 2 at r2 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

.cpp :)

Done.


OpenSim/Actuators/Test/testPolynomialPathFitter.cpp line 131 at r2 (raw file):

Previously, aymanhab (Ayman Habib) wrote…

Very nicely done/tested @nickbianco

Done.

Copy link
Contributor

@pepbos pepbos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting stuff!
The different uses of "path" become a bit confusing, especially if things are done at runtime, rather than checked at compile time.

@@ -309,3 +309,26 @@ void ModelFactory::createReserveActuators(Model& model, double optimalForce,
}
}

void ModelFactory::replacePathsWithFunctionBasedPaths(Model& model,
const Set<FunctionBasedPath>& functionBasedPaths) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The different meanings of "path" becomes confusing in this function...

OPENSIM_THROW_IF(!model.hasComponent<Force>(path.getName()),
Exception, "Model does not contain a Force at path {}.",
path.getName());
auto& force = model.updComponent<Force>(path.getName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd that the name of the FunctionBasedPath is equal to the path of a component in a model. Is there a reason for this?

// Get the force component associated with this path.
OPENSIM_THROW_IF(!model.hasComponent<Force>(path.getName()),
Exception, "Model does not contain a Force at path {}.",
path.getName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Throw is already performed at updComponent calls, with similar message. Perhaps add info if needed, or remove the throw here?

// Check that the force has a path property.
OPENSIM_THROW_IF(
!force.hasProperty("path"), Exception,
"Force {} does not have a path property.", path.getName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throw does add new info, but does not use the throw from updProperty. Perhaps use try?

"Force {} does not have a path property.", path.getName());

// Update the path.
path.setName(fmt::format("{}_path", force.getName()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "path" to a PathActuator as the name of a FunctionBasedPath's name, postfixed with _path seems confusing.
If there are more functions out there that work like this it might become more confusing.
Is this postfix with _path a fix or feature? Why not use the name the user gave to this FunctionBasedPath?


// Update the path.
path.setName(fmt::format("{}_path", force.getName()));
force.updPropertyByName<AbstractPath>("path").setValue(path);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use try here?


// Update the path.
path.setName(fmt::format("{}_path", force.getName()));
force.updPropertyByName<AbstractPath>("path").setValue(path);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this name never change? Since "path" seems to be pretty ambiguous, someone might change the name of the property "path".

Copy link
Contributor

@pepbos pepbos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more general question: In MultivariatePolynomialFunction, why are you inheriting from the templated Function, and not Function_<double>?

OPENSIM_THROW_IF(coefficients.size() != coeff_nr, Exception,
"Expected {} coefficients but got {}.", coeff_nr,
coefficients.size());
}
T calcValue(const SimTK::Vector& x) const override {

SimTK::Vector_<T> calcTermValues(const SimTK::Vector& x) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid heap allocation.

std::array<int, 6> nq{{0, 0, 0, 0, 0, 0}};
T value = static_cast<T>(0);
SimTK::Vector_<T> derivatives = static_cast<SimTK::Vector_<T>>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this doesnt simply cast the Vector, not the data?

@@ -163,57 +169,57 @@ class SimTKMultivariatePolynomial : public SimTK::Function_<T> {
if (derivComponent[0] == 0) {
nqNonNegative = nq[0] - 1;
if (nqNonNegative < 0) nqNonNegative = 0;
T valueP = nq[0] * std::pow(x[0], nqNonNegative);
T derivP = nq[0] * std::pow(x[0], nqNonNegative);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code above looks a lot like the code at the constructor: Perhaps worth reusing it...


SimTK::Vector MultivariatePolynomialFunction::getTermValues(
const SimTK::Vector& x) const {
if (_function == nullptr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!_function) {

}
value += valueP * coefficients[coeff_nr];
derivatives[coeff_nr] = derivP;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments would really help reading this large piece of code.

Copy link
Member Author

@nickbianco nickbianco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 25 of 30 files reviewed, 20 unresolved discussions (waiting on @aymanhab and @pepbos)


OpenSim/Common/MultivariatePolynomialFunction.cpp line 79 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

Avoid heap allocation.

values is initialized the function so it should be allocated on the stack. Unless static casting (which is now removed, see below) does funny things with stack vs heap allocation that I'm not aware of.


OpenSim/Common/MultivariatePolynomialFunction.cpp line 134 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

Are you sure this doesnt simply cast the Vector, not the data?

Updated to avoid the static_cast.


OpenSim/Common/MultivariatePolynomialFunction.cpp line 172 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

The code above looks a lot like the code at the constructor: Perhaps worth reusing it...

Done.


OpenSim/Common/MultivariatePolynomialFunction.cpp line 222 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

A few comments would really help reading this large piece of code.

Done.


OpenSim/Common/MultivariatePolynomialFunction.cpp line 268 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

if (!_function) {

Done.

Copy link
Member Author

@nickbianco nickbianco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pepbos! Ready for review again. I tried to make replacePathsWithFunctionBasedPaths less confusing, but happy to make additional changes if necessary.

In MultivariatePolynomialFunction, why are you inheriting from the templated Function, and not Function_<double>?

I didn't originally write this code, but it looks like inheriting from Function_<T> is the intended interface in Simbody.

Reviewable status: 23 of 30 files reviewed, 20 unresolved discussions (waiting on @aymanhab and @pepbos)


OpenSim/Actuators/ModelFactory.cpp line 313 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

The different meanings of "path" becomes confusing in this function...

I tried to clarify in the doc comments.


OpenSim/Actuators/ModelFactory.cpp line 320 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

This Throw is already performed at updComponent calls, with similar message. Perhaps add info if needed, or remove the throw here?

Done.


OpenSim/Actuators/ModelFactory.cpp line 321 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

Seems odd that the name of the FunctionBasedPath is equal to the path of a component in a model. Is there a reason for this?

See my comment below.


OpenSim/Actuators/ModelFactory.cpp line 326 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

This throw does add new info, but does not use the throw from updProperty. Perhaps use try?

Done.


OpenSim/Actuators/ModelFactory.cpp line 329 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

The "path" to a PathActuator as the name of a FunctionBasedPath's name, postfixed with _path seems confusing.
If there are more functions out there that work like this it might become more confusing.
Is this postfix with _path a fix or feature? Why not use the name the user gave to this FunctionBasedPath?

Naming the FunctionBasedPath is actually unnecessary since all path-based forces will store it using a property named 'path'. When the force is serialized it will then have the name 'path'. The names for each FunctionBasedPath in the XML file is so we know which Force to assign the FunctionBasedPath to. It might be more proper to have a small helper class that stores the FunctionBasedPath and the name of the Force it is assigned to, but this implementation seemed reasonable to me.


OpenSim/Actuators/ModelFactory.cpp line 330 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

Use try here?

Done.


OpenSim/Actuators/ModelFactory.cpp line 330 at r3 (raw file):

Previously, pepbos (pepbos) wrote…

Does this name never change? Since "path" seems to be pretty ambiguous, someone might change the name of the property "path".

As I said in the other comment, all path-based forces (i.e., PathSpring, PathActuator, Ligament, and Blankevoort1991Ligament) store paths using a property named 'path'. I guess someone could create a new path-based force object that uses a different property name, which would cause a failure here. As an alternative, since the force should only have one path, I could somehow look up the PropertyIndex for the path and update it that way.

Copy link
Member

@aymanhab aymanhab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 2 of 2 files at r3, 1 of 3 files at r4, 1 of 3 files at r5, all commit messages.
Reviewable status: 26 of 30 files reviewed, 12 unresolved discussions (waiting on @pepbos)

@nickbianco
Copy link
Member Author

Merging with @aymanhab's approval and passing tests. I have a few other fixes, but they will come in a follow up PR.

@nickbianco nickbianco merged commit eec8be7 into main Nov 8, 2023
5 of 7 checks passed
@nickbianco nickbianco deleted the function_based_path_utils branch November 8, 2023 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants