Add elasticity plugins to xml_native_writer_test. Fixes #961.

PiperOrigin-RevId: 545454207
Change-Id: I4e93499eaa53e5bc8c8afa682986657cef3ac20f
This commit is contained in:
Alessio Quaglino
2023-07-04 08:28:56 -07:00
committed by Copybara-Service
parent 2b38148d24
commit 46e06c4cac
5 changed files with 56 additions and 36 deletions
+1 -1
View File
@@ -1390,7 +1390,7 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body) {
// write plugin
if (body->is_plugin) {
OnePlugin(elem, body);
OnePlugin(InsertEnd(elem, "plugin"), body);
}
// write child bodies recursively
+25
View File
@@ -19,6 +19,7 @@
#include <absl/strings/string_view.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mujoco.h>
namespace mujoco {
@@ -65,5 +66,29 @@ const std::string SaveAndReadXml(const mjModel* model);
std::vector<mjtNum> GetCtrlNoise(const mjModel* m, int nsteps,
mjtNum ctrlnoise = 0.01);
// Installs elasticity plugins
// TODO(quaglino): load all plugins with a macro
class PluginTest : public MujocoTest {
public:
// load plugin library
PluginTest() : MujocoTest() {
#if defined(_WIN32) || defined(__CYGWIN__)
mj_loadPluginLibrary((
std::string(std::getenv("MUJOCO_PLUGIN_DIR")) +
std::string("\\elasticity.dll")).c_str());
#else
#if defined(__APPLE__)
mj_loadPluginLibrary((
std::string(std::getenv("MUJOCO_PLUGIN_DIR")) +
std::string("/libelasticity.dylib")).c_str());
#else
mj_loadPluginLibrary((
std::string(std::getenv("MUJOCO_PLUGIN_DIR")) +
std::string("/libelasticity.so")).c_str());
#endif
#endif
}
};
} // namespace mujoco
#endif // MUJOCO_TEST_FIXTURE_H_
-22
View File
@@ -29,28 +29,6 @@
namespace mujoco {
namespace {
class PluginTest : public MujocoTest {
public:
// load plugin library
PluginTest() : MujocoTest() {
#if defined(_WIN32) || defined(__CYGWIN__)
mj_loadPluginLibrary((
std::string(std::getenv("MUJOCO_PLUGIN_DIR")) +
std::string("\\elasticity.dll")).c_str());
#else
#if defined(__APPLE__)
mj_loadPluginLibrary((
std::string(std::getenv("MUJOCO_PLUGIN_DIR")) +
std::string("/libelasticity.dylib")).c_str());
#else
mj_loadPluginLibrary((
std::string(std::getenv("MUJOCO_PLUGIN_DIR")) +
std::string("/libelasticity.so")).c_str());
#endif
#endif
}
};
// -------------------------------- solid -----------------------------------
TEST_F(PluginTest, ElasticEnergy) {
static constexpr char cantilever_xml[] = R"(
+6 -1
View File
@@ -18,7 +18,12 @@ target_link_libraries(xml_api_test fixture gmock)
mujoco_test(xml_native_reader_test)
target_link_libraries(xml_native_reader_test fixture gmock)
mujoco_test(xml_native_writer_test)
mujoco_test(
xml_native_writer_test
PROPERTIES
ENVIRONMENT
"MUJOCO_PLUGIN_DIR=$<TARGET_FILE_DIR:elasticity>"
)
target_link_libraries(
xml_native_writer_test
fixture
+24 -12
View File
@@ -14,6 +14,7 @@
// Tests for xml/xml_native_writer.cc.
#include <type_traits>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
@@ -959,17 +960,23 @@ static constexpr int kFieldSize = 500;
// The maximum spacing between a normalised floating point number x and an
// adjacent normalised number is 2 epsilon |x|; a factor 10 is added accounting
// for losses during non-idempotent operations such as vector normalizations.
template<typename T = mjtNum> T Compare(T val1, T val2) {
T error;
template <typename T>
auto Compare(T val1, T val2) {
using ReturnType =
std::conditional_t<std::is_same_v<T, float>, float, double>;
ReturnType error;
if (mju_abs(val1) <= 1 || mju_abs(val2) <= 1) {
// Asbolute precision for small numbers
// Absolute precision for small numbers
error = mju_abs(val1-val2);
} else {
// Relative precision for larger numbers
T magnitude = mju_max(mju_abs(val1), mju_abs(val2));
ReturnType magnitude = mju_abs(val1) + mju_abs(val2);
error = mju_abs(val1/magnitude - val2/magnitude) / magnitude;
}
return error < 2*10*std::numeric_limits<T>::epsilon() ? 0 : error;
ReturnType safety_factor = 10;
return error < safety_factor * std::numeric_limits<ReturnType>::epsilon()
? 0
: error;
}
mjtNum CompareModel(const mjModel* m1, const mjModel* m2,
@@ -1015,7 +1022,7 @@ mjtNum CompareModel(const mjModel* m1, const mjModel* m2,
return maxdif;
}
TEST_F(XMLWriterTest, WriteReadCompare) {
TEST_F(PluginTest, WriteReadCompare) {
FullFloatPrecision increase_precision;
// Loop over all xml files in data
std::vector<std::string> paths = {GetTestDataFilePath("."),
@@ -1028,7 +1035,7 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
// if file is meant to fail, skip it
if (absl::StrContains(p.path().string(), "malformed_") ||
absl::StrContains(p.path().string(), "plugin")) {
absl::StrContains(p.path().string(), "touch_grid")) {
continue;
}
@@ -1051,12 +1058,17 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
// if failing because assets are missing, accept the test
ASSERT_THAT(error.data(), HasSubstr("file")) << error.data();
} else {
// for a particularly difficult example, relax the tolerance
mjtNum tol =
absl::StrContains(p.path().string(), "belt.xml") ? 1e-13 : 0;
// compare and delete
char field[kFieldSize] = "";
mjtNum result = CompareModel(m, mtemp, field);
EXPECT_LE(result, 0) << "Loaded and saved models are different!\n"
<< "Affected file " << p.path().string() << '\n'
<< "Different field: " << field << '\n';
char field[kFieldSize] = "";
mjtNum result = CompareModel(m, mtemp, field);
EXPECT_LE(result, tol)
<< "Loaded and saved models are different!\n"
<< "Affected file " << p.path().string() << '\n'
<< "Different field: " << field << '\n';
mj_deleteModel(mtemp);
}