Skip to content

Latest commit

 

History

History
123 lines (100 loc) · 8.9 KB

DEVELOPING.md

File metadata and controls

123 lines (100 loc) · 8.9 KB

Guidelines for Developers of OpenSim-Core

OpenSim is a community resource that is housed in the opensim-core repository.

This specific document addresses issues that come up during development and maintenance of the OpenSim code base, that may not be relevant to end users because it's not in release code yet or of historical relevance only but would be valuable to new developers or advanced users. For general contribution guidelines please consult CONTRIBUTING.md

Contents:

Backward Compatibility of File Formats

OpenSim stores models and analysis-tools/objects in xml files (with extension .osim or .xml). Since the first release of OpenSim in 2007, these files have mostly survived many major format changes which is quite important considering that thousands of these files are around both in earlier OpenSim distributions and also in models created by users and/or collaborators. This section describes how backward compatibility is maintained so that if a format change is introduced, users do not lose valuable work and/or models in the process. In what follows we use the term serialization to mean writing and deserialization to mean reading.

One key piece of information that is kept with each file is a version number (usually in the header section e.g. <OpenSimDocument Version="NNNNN">. The same is true for .sto files that contain a header as well, though we will not discuss .sto files here. The version number encodes what the code was expecting the format to be when the file was produced/written. Keeping this number consistent with the content of the file is the fundamental invariant that needs to be maintained. In code, the version number is maintained in XMLDocument.cpp and is updated as version numbers are incremented. The actual numbers do not matter much, as long as they are monotonically increasing. OpenSim 3.2 shipped with OpenSimDocument Version number 30000. OpenSim 4.0 development started at version 30500 and will ship with 40000).

Deserialization of Objects and their Properties (primitives that are read-from/written-to files) is mostly transparent to users/developers. Once you use the property macros (e.g. OpenSim_DECLARE_UNNAMED_PROPERTY) in the header and perform the necessary wiring/calls, these properties know how to serialize themselves to XML elements and back. This serialization/deserialization, however, assumes a fixed XML layout/schema. If this assumption fails (because of a property name change, layout change or addition of new properties) then deserialization will not work out of the box and the developer making the change is responsible for changing the version number and the deserialization code to be in sync with the latest code. This is very important since if the developer does not update the version number then the code and the XML files will not be in sync and it will be very hard to go back to find out what changes should have been made.

Keep in mind that deserialization methods are practically called during the construction of the top level object/Model so typically you cannot rely on having any member variables or pointers populated unless done by default constructors, instead deserialization methods operate on the XML structure only. This helps isolate the backward compatibility code from the rest of the modeling/simulation or initialization functions.

Deserialization works by making the following calls:

  1. A user constructs an object that is a subclass of OpenSim::Object by using a constructor that takes the file name of a .osim or .xml file (only some classes have such a constructor). The construction call sequence ends with calling OpenSim::Object's constructor with the file name as an argument. This constructor invokes code that parses the XML file.
  2. The parsing code looks for XML tags that correspond to names of subclasses of OpenSim::Object; when it finds one of the them it performs the next 2 steps.
  3. An Object of the appropriate type is instantiated from the registry of available types using a lookup by the XML tag.
  4. The method updateFromXMLNode(SimTK::Xml::Element& node, int versionNumber) is invoked on the object and is passed the XML element corresponding to the object along with the version number from the XML document/file. That’s exactly the hook to deserialization that developers can use to correct for deserialization changes by manipulating the passed in node to match what the latest code expects.

If an object has never undergone format changes then it should not implement the method updateFromXMLNode() altogether. If the function needs to be implemented, it would have the following form:

void XXX::updateFromXMLNode(SimTK::Xml::Element& node, int versionNumber)
{
       // XMLDocument::getLatestVersion() is a number that changes with upgrades
       // Guarding with this condition makes sure that files already converted 
       // do not get penalized or converted again.
       if ( versionNumber < XMLDocument::getLatestVersion()) {
              if (versionNumber <= 20301) {
		         // convert node from version 20301 or prior to the next version
		          ……
              }
              if (versionNumber < 30500) {
	             // Convert versions before 30500 
              }
        }
        // At this point of the code, node is on the latest XML format
        // Call base class, now assuming node has been corrected for current version
        // This call will end up being made on Object, which will do the actual population of Property values
        Super::updateFromXMLNode(node, versionNumber);
}

CMake options for packaging a binary distribution

When packaging opensim-core for distribution, it is important to set certain CMake options correctly so that the distribution contains the necessary files from dependencies. The variables to set depend on how opensim-core is being distributed. Currently, opensim-core binaries are distributed through the OpenSim GUI distribution. In this case, the following settings should be used:

WITH_BTK=ON                                 non-default
OPENSIM_COPY_DEPENDENCIES=ON                default
OPENSIM_PYTHON_STANDALONE=OFF               default
on Windows: OPENSIM_INSTALL_UNIX_FHS=OFF    default
on UNIX:    OPENSIM_INSTALL_UNIX_FHS=ON     default
OPENSIM_SIMBODY_DOXYGEN_LOCATION=https://simtk.org/api_docs/simbody/<version>/

The last variable causes OpenSim's doxygen documentation to link to Simbody's documentation online.

The layout of the distribution on Windows is as follows:

  • bin/ OpenSim, SimTK, and BTK DLLs, opensim-cmd.exe, simbody-visualizer.exe
  • cmake/ OpenSimConfig.cmake, etc.
  • sdk/
    • APIExamples/: C++ examples.
    • doc/ API doxygen documentation.
    • include/ OpenSim (and Lepton) headers.
    • Java/ Source files for Java interface, and org-opensim-modeling.jar.
    • lib/ OpenSim "import" libraries, used during linking.
    • Scripts/ MATLAB and Python examples/utilities.
    • Simbody/ A copy of the Simbody installation.
      • bin/ SimTK DLLs.
      • cmake/ SimbodyConfig.cmake, etc.
      • include/ Simbody headers.
      • lib/ SimTK "import" libraries, used during linking.
    • python/ OpenSim Python bindings.
    • OpenSim_buildinfo.txt Describes the compiler used to build OpenSim.

The layout of the distribution on macOS (and Linux) is as follows:

  • bin/ opensim-cmd
  • etc/OpenSim_buildinfo.txt Describes the compiler used to build OpenSim.
  • include/
    • OpenSim/ OpenSim headers.
    • simbody/ Simbody headers.
    • Vendors/ Lepton headers.
  • lib/ (on some Linux variants, lib/<arch>/) OpenSim, SimTK, and BTK shared libraries.
    • cmake/ OpenSimConfig.cmake, SimbodyConfig.cmake, etc.
    • python2.7/site-packages/ OpenSim Python bindings.
  • libexec/simbody/simbody-visualizer
  • share/
    • doc/OpenSim/ API doxygen documentation.
      • APIExamles/ C++ examples.
      • Scripts/ MATLAB and Python examples/utilities.
    • java/org-opensim-modeling.jar
    • OpenSim/java/ Source files for Java interface.

This layout is intended to follow the UNIX Filesystem Hierarchy Standard.

We hope to distribute opensim-core binaries through common package managers (we already have some progress for Ubuntu, macOS/Homebrew, and Conda). In these cases, the dependencies should be installed via their own packages so that the OpenSim installation does not need to contain the dependencies.

WITH_BTK=ON                                 non-default
OPENSIM_COPY_DEPENDENCIES=OFF               non-default
OPENSIM_PYTHON_STANDALONE=OFF               default
on Windows: OPENSIM_INSTALL_UNIX_FHS=OFF    default
on UNIX:    OPENSIM_INSTALL_UNIX_FHS=ON     default