From ea5adecfbcbf565b8383345bd8c04fff0a914399 Mon Sep 17 00:00:00 2001
From: Reinhard Resch <mbdyn-user@a1.net>
Date: Sun, 11 May 2025 18:54:23 +0200
Subject: [PATCH 1/2] Enable restarts for modal joints

---
 libraries/libmbmath/dgeequtest.cc       |    3 +
 libraries/libmbmath/epetraspmh.h        |    3 +
 libraries/libmbmath/epetravh.h          |    4 +
 libraries/libmbmath/itertest.cc         |    3 +
 libraries/libmbmath/matmultest.cc       |    3 +
 libraries/libmbmath/matvec3n.cc         |   29 +
 libraries/libmbmath/matvec3n.h          |    5 +
 libraries/libmbmath/sp_gradient_test.cc |    5 +
 libraries/libmbutil/restart_data.cc     |   44 +-
 libraries/libmbutil/restart_data.h      |   24 +-
 libraries/libmbwrap/aztecoowrap.cc      |    5 +
 mbdyn/base/noxsolver.cc                 |    5 +
 mbdyn/struct/drvj.cc                    |    7 +-
 mbdyn/struct/drvj.h                     |    2 +
 mbdyn/struct/modal.cc                   |    9 +
 mbdyn/struct/modal.h                    |    4 +-
 tests/restart/beam/beam_stage1_run.m    |   28 +
 tests/restart/modal/modal_stage1.mbd    |   46 +
 tests/restart/modal/modal_stage1_run.m  | 1202 +++++++++++++++++++++++
 19 files changed, 1397 insertions(+), 34 deletions(-)
 create mode 100644 tests/restart/modal/modal_stage1.mbd
 create mode 100644 tests/restart/modal/modal_stage1_run.m

diff --git a/libraries/libmbmath/dgeequtest.cc b/libraries/libmbmath/dgeequtest.cc
index fbacf962b..5f3be8b00 100644
--- a/libraries/libmbmath/dgeequtest.cc
+++ b/libraries/libmbmath/dgeequtest.cc
@@ -50,8 +50,11 @@
 
 #ifdef USE_TRILINOS
 #undef HAVE_BLAS
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include "epetraspmh.h"
 #include <Epetra_SerialComm.h>
+#pragma GCC diagnostic pop
 #endif
 
 static doublereal mat[5][5] = {
diff --git a/libraries/libmbmath/epetraspmh.h b/libraries/libmbmath/epetraspmh.h
index 60cda7257..6abb33d07 100644
--- a/libraries/libmbmath/epetraspmh.h
+++ b/libraries/libmbmath/epetraspmh.h
@@ -46,9 +46,12 @@
 #include "spmh.h"
 #include "cscmhtpl.h"
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include <Epetra_Comm.h>
 #include <Epetra_Map.h>
 #include <Epetra_CrsMatrix.h>
+#pragma GCC diagnostic pop
 
 class EpetraSparseMatrixHandler: public SparseMatrixHandler {
 public:
diff --git a/libraries/libmbmath/epetravh.h b/libraries/libmbmath/epetravh.h
index e5de7b50b..4b928c86a 100644
--- a/libraries/libmbmath/epetravh.h
+++ b/libraries/libmbmath/epetravh.h
@@ -43,9 +43,13 @@
 
 #ifdef USE_TRILINOS
 #include "vh.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include <Epetra_Comm.h>
 #include <Epetra_Map.h>
 #include <Epetra_Vector.h>
+#pragma GCC diagnostic pop
 
 class EpetraVectorHandler: public VectorHandler {
 public:
diff --git a/libraries/libmbmath/itertest.cc b/libraries/libmbmath/itertest.cc
index 8c3764a2c..554d32ddf 100644
--- a/libraries/libmbmath/itertest.cc
+++ b/libraries/libmbmath/itertest.cc
@@ -46,8 +46,11 @@
 #include "cscmhtpl.h"
 #ifdef USE_TRILINOS
 #undef HAVE_BLAS
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include "epetraspmh.h"
 #include "Epetra_SerialComm.h"
+#pragma GCC diagnostic pop
 #endif
 
 static constexpr doublereal mat[5][5] = {
diff --git a/libraries/libmbmath/matmultest.cc b/libraries/libmbmath/matmultest.cc
index 6787b12f1..b5d0678cd 100644
--- a/libraries/libmbmath/matmultest.cc
+++ b/libraries/libmbmath/matmultest.cc
@@ -46,9 +46,12 @@
 #include "cscmhtpl.h"
 #ifdef USE_TRILINOS
 #undef HAVE_BLAS
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include "epetraspmh.h"
 #include "epetravh.h"
 #include <Epetra_SerialComm.h>
+#pragma GCC diagnostic pop
 #endif
 
 static constexpr doublereal mat[5][5] = {
diff --git a/libraries/libmbmath/matvec3n.cc b/libraries/libmbmath/matvec3n.cc
index 68e2bbd3b..fedefe6d9 100644
--- a/libraries/libmbmath/matvec3n.cc
+++ b/libraries/libmbmath/matvec3n.cc
@@ -33,6 +33,7 @@
 
 #include <utility>
 #include <matvec3n.h>
+#include "binary_conversion.h"
 
 /* VecN - begin */
 
@@ -1179,6 +1180,34 @@ operator << (std::ostream& out, const MatNxN& m)
 
 	return out;
 }
+namespace BinaryConversion {
+     std::istream& ReadBinary(std::istream& is, VecN& v)
+     {
+          integer iNumRows;
 
+          ReadBinary(is, iNumRows);
+
+          v.Resize(iNumRows);
+
+          for (integer i = 1; i <= iNumRows; ++i) {
+               ReadBinary(is, v(i));
+          }
+
+          return is;
+     }
+
+     std::ostream& WriteBinary(std::ostream& os, const VecN& v)
+     {
+          const integer iNumRows = v.iGetNumRows();
+
+          WriteBinary(os, iNumRows);
+
+          for (integer i = 1; i <= iNumRows; ++i) {
+               WriteBinary(os, v(i));
+          }
+
+          return os;
+     }
+}
 /* MatNxN - end */
 
diff --git a/libraries/libmbmath/matvec3n.h b/libraries/libmbmath/matvec3n.h
index b68d319a1..48efa379b 100644
--- a/libraries/libmbmath/matvec3n.h
+++ b/libraries/libmbmath/matvec3n.h
@@ -688,6 +688,11 @@ std::ostream& operator << (std::ostream&, const MatNxN&);
 
 /* MatNxN - end */
 
+namespace BinaryConversion {
+     std::istream& ReadBinary(std::istream& is, VecN& v);
+
+     std::ostream& WriteBinary(std::ostream& os, const VecN& v);
+}
 
 #endif /* MATVEC3N_H */
 
diff --git a/libraries/libmbmath/sp_gradient_test.cc b/libraries/libmbmath/sp_gradient_test.cc
index 66cedf34a..12aa5e8f5 100644
--- a/libraries/libmbmath/sp_gradient_test.cc
+++ b/libraries/libmbmath/sp_gradient_test.cc
@@ -81,10 +81,15 @@
 #include "restart_data.h"
 
 #ifdef USE_TRILINOS
+
+
 #undef HAVE_BLAS
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include "epetravh.h"
 #include "epetraspmh.h"
 #include <Epetra_SerialComm.h>
+#pragma GCC diagnostic pop
 #endif
 
 #include "sp_gradient_test_func.h"
diff --git a/libraries/libmbutil/restart_data.cc b/libraries/libmbutil/restart_data.cc
index 73a9599b9..d678a1da9 100644
--- a/libraries/libmbutil/restart_data.cc
+++ b/libraries/libmbutil/restart_data.cc
@@ -1,5 +1,5 @@
-/* 
- * MBDyn (C) is a multibody analysis code. 
+/*
+ * MBDyn (C) is a multibody analysis code.
  * http://www.mbdyn.org
  *
  * Copyright (C) 1996-2025
@@ -16,7 +16,7 @@
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation (version 2 of the License).
- * 
+ *
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -46,7 +46,7 @@ void RestartData::WriteFile(const std::string& filename)
 void RestartData::ReadFile(const std::string& filename)
 {
      std::ifstream is(filename, std::ios::binary);
-     
+
      ReadFile(is);
 }
 
@@ -61,7 +61,7 @@ void RestartData::WriteFile(std::ostream& os)
      const size_t uNumItems = m_Map.size();
 
      DEBUGCERR("Number of items to write: " << uNumItems << "\n");
-     
+
      WriteBinary(os, uNumItems);
 
 #ifdef DEBUG
@@ -81,7 +81,7 @@ void RestartData::WriteFile(std::ostream& os)
 void RestartData::ReadFile(std::istream& is)
 {
      is.exceptions(std::ios::badbit | std::ios::failbit);
-     
+
      using namespace BinaryConversion;
 
      std::string strHeader;
@@ -89,7 +89,7 @@ void RestartData::ReadFile(std::istream& is)
      std::getline(is, strHeader, '\n');
 
      constexpr size_t nHeaderLength = sizeof(szHeaderString) / sizeof(szHeaderString[0]) - 1;
-     
+
      if (0 == strHeader.compare(0, nHeaderLength, szHeaderString)) {
           throw ErrInvalidFileFormat(MBDYN_EXCEPT_ARGS);
      }
@@ -99,15 +99,15 @@ void RestartData::ReadFile(std::istream& is)
      ReadBinary(is, uNumItems);
 
      DEBUGCERR("Number of items to read: " << uNumItems << "\n");
-     
+
      Key oKey;
      std::string strData;
 
      m_Map.clear();
-     
+
      for (size_t i = 0; i < uNumItems; ++i) {
           DEBUGCERR("Reading item " << i << "\n");
-          
+
           unsigned uEntity;
           ReadBinary(is, uEntity);
           oKey.eEntity = static_cast<RestartEntity>(uEntity);
@@ -120,3 +120,27 @@ void RestartData::ReadFile(std::istream& is)
           DEBUGCERR("Insert item " << i << " " << uEntity << " " << oKey.uLabel << " " << oKey.iIndex << " " << oKey.strName << "\n");
      }
 }
+
+bool RestartData::Sync(const Key& oKey, std::string& strData, RestartAction eAction)
+{
+     auto it = m_Map.find(oKey);
+
+     if (eAction == RESTART_SAVE) {
+          if (it != m_Map.end()) {
+               throw ErrDuplicateEntry(MBDYN_EXCEPT_ARGS);
+          }
+
+          m_Map.insert(std::make_pair(oKey, strData));
+     } else {
+          ASSERT(eAction == RESTART_RESTORE);
+
+          if (it != m_Map.end()) {
+               strData = it->second;
+               return true;
+          } else {
+               // Do not alter strData
+          }
+     }
+
+     return false;
+}
diff --git a/libraries/libmbutil/restart_data.h b/libraries/libmbutil/restart_data.h
index 6879a2f21..1d7b6d7b0 100644
--- a/libraries/libmbutil/restart_data.h
+++ b/libraries/libmbutil/restart_data.h
@@ -41,6 +41,7 @@
 #include "binary_conversion.h"
 #include "matvec3.h"
 #include "matvec6.h"
+#include "matvec3n.h"
 #include "sp_matrix_base.h"
 
 class RestartData {
@@ -172,28 +173,7 @@ private:
           BinaryConversion::ReadBinary(is, oData);
      }
      
-     bool Sync(const Key& oKey, std::string& strData, RestartAction eAction) {
-          auto it = m_Map.find(oKey);
-
-          if (eAction == RESTART_SAVE) {
-               if (it != m_Map.end()) {
-                    throw ErrDuplicateEntry(MBDYN_EXCEPT_ARGS);
-               } 
-
-               m_Map.insert(std::make_pair(oKey, strData));
-          } else {
-               ASSERT(eAction = RESTART_RESTORE);
-
-               if (it != m_Map.end()) {
-                    strData = it->second;
-                    return true;
-               } else {
-                    // Do not alter strData
-               }
-          }
-
-          return false;
-     }
+     bool Sync(const Key& oKey, std::string& strData, RestartAction eAction);
 
      std::map<Key, std::string> m_Map;
 
diff --git a/libraries/libmbwrap/aztecoowrap.cc b/libraries/libmbwrap/aztecoowrap.cc
index 3ccfb0e86..7495241ad 100644
--- a/libraries/libmbwrap/aztecoowrap.cc
+++ b/libraries/libmbwrap/aztecoowrap.cc
@@ -47,6 +47,9 @@
 #include "mbcomm.h"
 #endif
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
+
 // FIXME: Compiler fails with -Werror if HAVE_BLAS is redefined inside a Trilinos header
 #define HAVE_BLAS_SAVE HAVE_BLAS
 #define HAVE_BOOL_SAVE HAVE_BOOL
@@ -72,6 +75,8 @@
 #undef HAVE_BLAS_SAVE
 #undef HAVE_BOOL_SAVE
 
+#pragma GCC diagnostic pop
+
 class AmesosSolver {
 public:
      AmesosSolver(Epetra_RowMatrix* pA, Epetra_Vector* pX, Epetra_Vector* pB, unsigned uFlags);
diff --git a/mbdyn/base/noxsolver.cc b/mbdyn/base/noxsolver.cc
index 7003d788a..adc139406 100644
--- a/mbdyn/base/noxsolver.cc
+++ b/mbdyn/base/noxsolver.cc
@@ -49,6 +49,9 @@
 #include "mbcomm.h"
 #endif
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
+
 // FIXME: Compiler fails with -Werror if HAVE_BLAS is redefined inside a Trilinos header
 #define HAVE_BLAS_SAVE HAVE_BLAS
 #define HAVE_BOOL_SAVE HAVE_BOOL
@@ -83,6 +86,8 @@
 #undef HAVE_BLAS_SAVE
 #undef HAVE_BOOL_SAVE
 
+#pragma GCC diagnostic pop
+
 #ifdef DEBUG_JACOBIAN
 #include "sp_gradient_spmh.h"
 #endif
diff --git a/mbdyn/struct/drvj.cc b/mbdyn/struct/drvj.cc
index 2e90023ef..3d97abd7f 100644
--- a/mbdyn/struct/drvj.cc
+++ b/mbdyn/struct/drvj.cc
@@ -347,7 +347,12 @@ std::ostream& AngularVelocityJoint::Restart(std::ostream& out) const
      Dir.Write(out, ", ") << ", ";
    return pGetDriveCaller()->Restart(out) << ';' << std::endl;
 }
-   
+
+void AngularVelocityJoint::Restart(RestartData& oData, RestartData::RestartAction eAction)
+{
+     oData.Sync(RestartData::ELEM_JOINTS, GetLabel(), "dM", dM, eAction);
+}
+
 /* dati privati */
 unsigned int
 AngularVelocityJoint::iGetNumPrivData(void) const
diff --git a/mbdyn/struct/drvj.h b/mbdyn/struct/drvj.h
index ea0b3ad5c..ad157f8d1 100644
--- a/mbdyn/struct/drvj.h
+++ b/mbdyn/struct/drvj.h
@@ -171,6 +171,8 @@ class AngularVelocityJoint
    /* Contributo al file di restart */
    virtual std::ostream& Restart(std::ostream& out) const;
 
+   virtual void Restart(RestartData& oData, RestartData::RestartAction eAction) override;
+     
    virtual unsigned int iGetNumDof(void) const { 
       return 1;
    };
diff --git a/mbdyn/struct/modal.cc b/mbdyn/struct/modal.cc
index b0c4bb932..e74452c02 100644
--- a/mbdyn/struct/modal.cc
+++ b/mbdyn/struct/modal.cc
@@ -224,6 +224,14 @@ Modal::Restart(std::ostream& out) const
 	return out << "modal; # not implemented yet" << std::endl;
 }
 
+void Modal::Restart(RestartData& oData, RestartData::RestartAction eAction)
+{
+     oData.Sync(RestartData::ELEM_JOINTS, GetLabel(), "a", a, eAction);
+     oData.Sync(RestartData::ELEM_JOINTS, GetLabel(), "aPrime", aPrime, eAction);
+     oData.Sync(RestartData::ELEM_JOINTS, GetLabel(), "b", b, eAction);
+     oData.Sync(RestartData::ELEM_JOINTS, GetLabel(), "bPrime", bPrime, eAction);
+}
+
 unsigned int
 Modal::iGetNumDof(void) const
 {
@@ -2132,6 +2140,7 @@ Modal::SetValue(DataManager *pDM,
 
 		/* derivatives of modal multipliers */
 		X.PutCoef(iFlexIndex + NModes + iCnt, b(iCnt));
+                XP.PutCoef(iFlexIndex + NModes + iCnt, bPrime(iCnt));
 		XP.PutCoef(iFlexIndex + iCnt, b(iCnt));
 	}
 
diff --git a/mbdyn/struct/modal.h b/mbdyn/struct/modal.h
index 48e5c277d..c07a02778 100644
--- a/mbdyn/struct/modal.h
+++ b/mbdyn/struct/modal.h
@@ -263,7 +263,9 @@ public:
 
 	/* Contributo al file di restart */
 	virtual std::ostream& Restart(std::ostream& out) const;
-
+     
+        virtual void Restart(RestartData& oData, RestartData::RestartAction eAction) override;
+     
 	virtual unsigned int iGetNumDof(void) const;
 	virtual std::ostream&
 	DescribeDof(std::ostream& out,
diff --git a/tests/restart/beam/beam_stage1_run.m b/tests/restart/beam/beam_stage1_run.m
index e08470abb..85736489f 100644
--- a/tests/restart/beam/beam_stage1_run.m
+++ b/tests/restart/beam/beam_stage1_run.m
@@ -1,3 +1,31 @@
+# MBDyn (C) is a multibody analysis code.
+# http://www.mbdyn.org
+#
+# Copyright (C) 1996-2025
+#
+# Pierangelo Masarati	<masarati@aero.polimi.it>
+# Paolo Mantegazza	<mantegazza@aero.polimi.it>
+#
+# Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
+# via La Masa, 34 - 20156 Milano, Italy
+# http://www.aero.polimi.it
+#
+# Changing this copyright notice is forbidden.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
 try
   clear all;
   close all;
diff --git a/tests/restart/modal/modal_stage1.mbd b/tests/restart/modal/modal_stage1.mbd
new file mode 100644
index 000000000..7610df2a7
--- /dev/null
+++ b/tests/restart/modal/modal_stage1.mbd
@@ -0,0 +1,46 @@
+# MBDyn (C) is a multibody analysis code.
+# http://www.mbdyn.org
+#
+# Copyright (C) 1996-2025
+#
+# Pierangelo Masarati	<masarati@aero.polimi.it>
+# Paolo Mantegazza	<mantegazza@aero.polimi.it>
+#
+# Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
+# via La Masa, 34 - 20156 Milano, Italy
+# http://www.aero.polimi.it
+#
+# Changing this copyright notice is forbidden.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+## Dummy input file needed by simple_testsuite.sh
+## Real intput files will be generated by modal_stage1_run.m
+
+begin: data;
+problem: initial value;
+end: data;
+
+begin: initial value;
+end: initial value;
+
+begin: control data;
+end: control data;
+
+begin: nodes;
+end: nodes;
+
+begin: elements;
+end: elements;
\ No newline at end of file
diff --git a/tests/restart/modal/modal_stage1_run.m b/tests/restart/modal/modal_stage1_run.m
new file mode 100644
index 000000000..9506fa962
--- /dev/null
+++ b/tests/restart/modal/modal_stage1_run.m
@@ -0,0 +1,1202 @@
+# MBDyn (C) is a multibody analysis code.
+# http://www.mbdyn.org
+#
+# Copyright (C) 1996-2025
+#
+# Pierangelo Masarati	<masarati@aero.polimi.it>
+# Paolo Mantegazza	<mantegazza@aero.polimi.it>
+#
+# Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
+# via La Masa, 34 - 20156 Milano, Italy
+# http://www.aero.polimi.it
+#
+# Changing this copyright notice is forbidden.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+clear all;
+
+try
+  options.f_plot = false;
+
+  if (options.f_plot)
+    close all;
+  endif
+
+  pkg_prefix = getenv("OCT_PKG_INSTALL_PREFIX");
+
+  if (~isempty(pkg_prefix))
+    pkg("local_list", fullfile(pkg_prefix, "octave_packages"));
+  endif
+
+  pkg load mboct-fem-pkg;
+
+  function R2j = norm_ref_frame(R2)
+    e1 = R2(:, 1);
+    e3 = [0; 0; 1];
+    e2 = cross(e3, e1);
+    e1 = cross(e2, e3);
+    R2j = [e1 / norm(e1), e2 / norm(e2), e3 / norm(e3)];
+  endfunction
+
+  function write_modal_file(input_filename, output_filename, istage)
+    fd = -1;
+    unwind_protect
+      fd = fopen(input_filename, "w");
+      if (fd == -1)
+        error("failed to open file \"%s\"", input_filename);
+      endif
+      fputs(fd, "include: \"${MBDYN_ROTOR_DYN_CMS_PARAM_FILE}\";\n");
+      fputs(fd, "set: integer ref_id_ground = 1001;\n");
+      fputs(fd, "set: integer ref_id_shaft = 1002;\n");
+      fputs(fd, "set: integer ref_id_rotor = 1003;\n");
+      fputs(fd, "set: integer ref_id_bearing1 = 1004;\n");
+      fputs(fd, "set: integer ref_id_bearing2 = 1005;\n");
+      fputs(fd, "set: integer node_id_rotor = 2002;\n");
+      fputs(fd, "set: integer node_id_bearing1 = 2003;\n");
+      fputs(fd, "set: integer node_id_bearing2 = 2004;\n");
+      fputs(fd, "set: integer body_id_unbalance = 3000;\n");
+      fputs(fd, "set: integer elem_id_rotor = 3005;\n");
+      fputs(fd, "set: integer joint_id_bearing1 = 3006;\n");
+      fputs(fd, "set: integer joint_id_bearing2 = 3007;\n");
+      fputs(fd, "set: integer joint_id_drive = 3008;\n");
+      fputs(fd, "set: integer elem_id_inertia = 3010;\n");
+      fputs(fd, "set: integer drive_id_rotor_speed = 5001;\n");
+      fputs(fd, "set: integer drive_id_time_step = 5002;\n");
+      fputs(fd, "set: real initial_time = 0.;\n");
+      fputs(fd, "set: real final_time = 2. * pi * n / (abs(omega1 + omega0) / 2.);\n");
+      fputs(fd, "begin: data;\n");
+      fputs(fd, "        problem: initial value;\n");
+      fputs(fd, "end: data;\n");
+      fputs(fd, "begin: initial value;\n");
+      fputs(fd, "        initial time: initial_time;\n");
+      fputs(fd, "        final time: final_time;\n");
+      fputs(fd, "        time step: 2. * pi / (360. * abs(omega0));\n");
+      fputs(fd, "        strategy: change, postponed, drive_id_time_step;\n");
+      fputs(fd, "        method: ms, 0.6;\n");
+      fputs(fd, "        tolerance: 1e-6, test, norm, 1e-6, test, norm;\n");
+      fputs(fd, "        derivatives tolerance: 1e-4, 1e-4;\n");
+      fputs(fd, "        max iterations: 100;\n");
+      fputs(fd, "        derivatives max iterations: 10;\n");
+      fputs(fd, "        derivatives coefficient: auto;\n");
+      fputs(fd, "        output: iterations;\n");
+      fputs(fd, "        linear solver: naive, colamd, scale, row max column max, always, max iterations, 100;\n");
+      fputs(fd, "        nonlinear solver: nox, modified, 10,\n");
+      fputs(fd, "             keep jacobian matrix,\n");
+      fputs(fd, "             inner iterations before assembly, 6,\n");
+      fputs(fd, "             jacobian operator, newton krylov,\n");
+      fputs(fd, "             solver, line search based,\n");
+      fputs(fd, "             forcing term, type 2,\n");
+      fputs(fd, "             direction, newton,\n");
+      fputs(fd, "             weighted rms absolute tolerance, 0.,\n");
+      fputs(fd, "             weighted rms relative tolerance, 0.,\n");
+      fputs(fd, "             linear solver, gmres,\n");
+      fputs(fd, "             linear solver max iterations, 12,\n");
+      fputs(fd, "             krylov subspace size, 12;\n");
+      fputs(fd, "        enforce constraint equations: constraint violations;\n");
+      fputs(fd, "        threads: assembly, 1;\n");
+      fputs(fd, "end: initial value;\n");
+      fputs(fd, "begin: control data;\n");
+      switch (istage)
+        case 1
+          fputs(fd, "     make restart file: times, 1, 0.5 * (final_time - initial_time) + initial_time, format, binary;\n");
+        case 2
+          fprintf(fd, "    load restart file: \"%s.0.rst\";\n", output_filename);
+          fputs(fd, "     skip initial joint assembly;\n");
+      endswitch
+      fputs(fd, "       default output: none, structural nodes;\n");
+      fputs(fd, "       default orientation: euler123;\n");
+      fputs(fd, "       output precision: 16;\n");
+      fputs(fd, "       max iterations: 0;\n");
+      fputs(fd, "        structural nodes:\n");
+      fputs(fd, "                +1           # modal\n");
+      fputs(fd, "                +1           # interface 1\n");
+      fputs(fd, "                +1              # interface 2\n");
+      fputs(fd, "        ;\n");
+      fputs(fd, "        joints:\n");
+      fputs(fd, "                +1           # modal\n");
+      fputs(fd, "                +1           # bearing1\n");
+      fputs(fd, "                +1           # bearing2\n");
+      fputs(fd, "                +1              # drive\n");
+      fputs(fd, "        ;\n");
+      fputs(fd, "        rigid bodies: 1;\n");
+      fputs(fd, "        gravity;\n");
+      fputs(fd, "end: control data;\n");
+      fputs(fd, "reference: ref_id_ground,\n");
+      fputs(fd, "        reference, global, null,\n");
+      fputs(fd, "        reference, global, eye,\n");
+      fputs(fd, "        reference, global, null,\n");
+      fputs(fd, "        reference, global, null;\n");
+      fputs(fd, "reference: ref_id_shaft,\n");
+      fputs(fd, "        reference, ref_id_ground, null,\n");
+      fputs(fd, "        reference, ref_id_ground, eye,\n");
+      fputs(fd, "        reference, ref_id_ground, null,\n");
+      fputs(fd, "        reference, ref_id_ground, 0., 0., omega0;\n");
+      fputs(fd, "reference: ref_id_rotor,\n");
+      fputs(fd, "        reference, ref_id_shaft, 0., 0., o,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_bearing1,\n");
+      fputs(fd, "        reference, ref_id_shaft,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   -0.5 * l,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_bearing2,\n");
+      fputs(fd, "        reference, ref_id_shaft,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.5 * l,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "begin: nodes;\n");
+      fputs(fd, "        structural: node_id_rotor, modal,\n");
+      fputs(fd, "                reference, ref_id_rotor, null,\n");
+      fputs(fd, "                reference, ref_id_rotor, eye,\n");
+      fputs(fd, "                reference, ref_id_rotor, null,\n");
+      fputs(fd, "                reference, ref_id_rotor, null;\n");
+      fputs(fd, "        structural: node_id_bearing1, static,\n");
+      fputs(fd, "                reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                reference, ref_id_bearing1, null;\n");
+      fputs(fd, "        structural: node_id_bearing2, static,\n");
+      fputs(fd, "                reference, ref_id_bearing2, null,\n");
+      fputs(fd, "                reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "                reference, ref_id_bearing2, null,\n");
+      fputs(fd, "                reference, ref_id_bearing2, null;\n");
+      fputs(fd, "end: nodes;\n");
+      fputs(fd, "begin: elements;\n");
+      fputs(fd, "       drive caller: drive_id_rotor_speed, string, \"(omega1 - omega0) / (final_time - initial_time) * Time + omega0\", output, yes;\n");
+      fputs(fd, "       drive caller: drive_id_time_step, string, \"2. * pi / (max(1., 360. * abs(model::drive(drive_id_rotor_speed, Time))))\";\n");
+      fputs(fd, "       body: body_id_unbalance,\n");
+      fputs(fd, "             node_id_rotor,\n");
+      fputs(fd, "                dm,\n");
+      fputs(fd, "                reference, ref_id_rotor, dr, 0., 0.,\n");
+      fputs(fd, "                diag, 0., 0., 0.;\n");
+      fputs(fd, "        joint: joint_id_bearing1, total pin joint,\n");
+      fputs(fd, "                node_id_bearing1,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "               position constraint,\n");
+      fputs(fd, "                        active, active, active,\n");
+      fputs(fd, "                        null,\n");
+      fputs(fd, "               orientation constraint,\n");
+      fputs(fd, "                        inactive, inactive, inactive,\n");
+      fputs(fd, "                        null;\n");
+      fputs(fd, "        joint: joint_id_bearing2, total pin joint,\n");
+      fputs(fd, "               node_id_bearing2,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing2, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               position, reference, ref_id_bearing2, null,\n");
+      fputs(fd, "               position orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               rotation orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               position constraint,\n");
+      fputs(fd, "                        active, active, inactive,\n");
+      fputs(fd, "                        null,\n");
+      fputs(fd, "               orientation constraint,\n");
+      fputs(fd, "                        inactive, inactive, inactive,\n");
+      fputs(fd, "                        null;\n");
+      fputs(fd, "     joint: joint_id_drive, angular velocity,\n");
+      fputs(fd, "             # node label\n");
+      fputs(fd, "             node_id_rotor, \n");
+      fputs(fd, "             # direction\n");
+      fputs(fd, "             0.,0.,1.,\n");
+      fputs(fd, "             # angular velocity\n");
+      fputs(fd, "             reference, drive_id_rotor_speed;\n");
+      fputs(fd, "        include: \"${MBDYN_ROTOR_DYN_CMS_ELEM_FILE}\";\n");
+      fputs(fd, "        inertia: elem_id_inertia,\n");
+      fputs(fd, "                 position, reference, ref_id_rotor, null,\n");
+      fputs(fd, "                 orientation, reference, ref_id_rotor, eye,\n");
+      fputs(fd, "                 body, all, joint, all, loadable, all,\n");
+      fputs(fd, "                 output, both;\n");
+      fputs(fd, "        gravity: uniform, 0., 0., -1., g;\n");
+      fputs(fd, "end: elements;\n");
+    unwind_protect_cleanup
+      if (fd ~= -1)
+        fclose(fd);
+      endif
+    end_unwind_protect
+  endfunction
+
+  function write_beam_file(input_filename, output_filename, istage, options)
+    fd = -1;
+    unwind_protect
+      fd = fopen(input_filename, "w");
+      if (fd == -1)
+        error("failed to open file \"%s\"", input_filename);
+      endif
+      fputs(fd, "include: \"${MBDYN_ROTOR_DYN_CMS_PARAM_FILE}\";\n");
+      fputs(fd, "set: integer ref_id_ground = 1001;\n");
+      fputs(fd, "set: integer ref_id_shaft = 1002;\n");
+      fputs(fd, "set: integer ref_id_rotor = 1003;\n");
+      fputs(fd, "set: integer ref_id_bearing1 = 1004;\n");
+      fputs(fd, "set: integer ref_id_bearing2 = 1005;\n");
+      fputs(fd, "set: integer ref_id_shaft_section = 1006;\n");
+      fputs(fd, "set: integer ref_id_bearing1_center = 1007;\n");
+      fputs(fd, "set: integer ref_id_bearing2_center = 1008;\n");
+      fputs(fd, "set: integer node_id_rotor = 2001;\n");
+      fputs(fd, "set: integer node_id_bearing1 = 2002;\n");
+      fputs(fd, "set: integer node_id_bearing1_center = 2003;\n");
+      fputs(fd, "set: integer node_id_bearing2 = 2004;\n");
+      fputs(fd, "set: integer node_id_bearing2_center = 2005;\n");
+      fputs(fd, "set: integer body_id_rotor = 3001;\n");
+      fputs(fd, "set: integer body_id_bearing1 = 3002;\n");
+      fputs(fd, "set: integer body_id_bearing1_center = 3003;\n");
+      fputs(fd, "set: integer body_id_bearing2 = 3004;\n");
+      fputs(fd, "set: integer body_id_bearing2_center = 3005;\n");
+      fputs(fd, "set: integer beam_id_shaft1 = 4001;\n");
+      fputs(fd, "set: integer beam_id_shaft2 = 4002;\n");
+      fputs(fd, "set: integer joint_id_bearing1 = 3006;\n");
+      fputs(fd, "set: integer joint_id_bearing2 = 3007;\n");
+      fputs(fd, "set: integer joint_id_drive = 3008;\n");
+      fputs(fd, "set: integer elem_id_inertia = 4001;\n");
+      fputs(fd, "set: integer drive_id_rotor_speed = 5001;\n");
+      fputs(fd, "set: integer drive_id_time_step = 5002;\n");
+      fputs(fd, "set: real l1 = 0.5 * l + o - 0.5 * w;\n");
+      fputs(fd, "set: real l2 = 0.5 * l - o - 0.5 * w;\n");
+      fputs(fd, "set: real rotor_m = rho * D^2 * pi / 4. * w;\n");
+      fputs(fd, "set: real rotor_Jx = rotor_m * ((0.5 * D)^2 + w^2 / 3.) / 4.;\n");
+      fputs(fd, "set: real rotor_Jy = rotor_Jx;\n");
+      fputs(fd, "set: real rotor_Jz = rotor_m * (0.5 * D)^2 / 2.;\n");
+      fputs(fd, "set: real shaft_rotor1_m = rho * d^2 * pi / 4. * (l1 / 4.);\n");
+      fputs(fd, "set: real shaft_rotor1_Jx = shaft_rotor1_m * ((0.5 * d)^2 + (l1 / 4.)^2 / 3.) / 4.;\n");
+      fputs(fd, "set: real shaft_rotor1_Jy = shaft_rotor1_Jx;\n");
+      fputs(fd, "set: real shaft_rotor1_Jz = shaft_rotor1_m * (0.5 * d)^2 / 2.;\n");
+      fputs(fd, "set: real shaft_rotor2_m = rho * d^2 * pi / 4. * (l2 / 4.);\n");
+      fputs(fd, "set: real shaft_rotor2_Jx = shaft_rotor2_m * ((0.5 * d)^2 + (l2 / 4.)^2 / 3.) / 4.;\n");
+      fputs(fd, "set: real shaft_rotor2_Jy = shaft_rotor2_Jx;\n");
+      fputs(fd, "set: real shaft_rotor2_Jz = shaft_rotor2_m * (0.5 * d)^2 / 2.;\n");
+      fputs(fd, "set: real shaft_bearing1_m = rho * d^2 * pi / 4. * (l1 / 4.);\n");
+      fputs(fd, "set: real shaft_bearing1_Jx = shaft_bearing1_m * ((0.5 * d)^2 + (l1 / 4.)^2 / 3.) / 4.;\n");
+      fputs(fd, "set: real shaft_bearing1_Jy = shaft_bearing1_Jx;\n");
+      fputs(fd, "set: real shaft_bearing1_Jz = shaft_bearing1_m * (0.5 * d)^2 / 2.;\n");
+      fputs(fd, "set: real shaft_bearing1_center_m = rho * d^2 * pi / 4. * (l1 / 2.);\n");
+      fputs(fd, "set: real shaft_bearing1_center_Jx = shaft_bearing1_center_m * ((0.5 * d)^2 + (l1 / 2.)^2 / 3.) / 4.;\n");
+      fputs(fd, "set: real shaft_bearing1_center_Jy = shaft_bearing1_center_Jx;\n");
+      fputs(fd, "set: real shaft_bearing1_center_Jz = shaft_bearing1_center_m * ((0.5 * d)^2) / 2.;\n");
+      fputs(fd, "set: real shaft_bearing2_m = rho * d^2 * pi / 4. * (l2 / 4.);\n");
+      fputs(fd, "set: real shaft_bearing2_Jx = shaft_bearing2_m * ((0.5 * d)^2 + (l2 / 4.)^2 / 3.) / 4.;\n");
+      fputs(fd, "set: real shaft_bearing2_Jy = shaft_bearing2_Jx;\n");
+      fputs(fd, "set: real shaft_bearing2_Jz = shaft_bearing2_m * (0.5 * d)^2 / 2.;\n");
+      fputs(fd, "set: real shaft_bearing2_center_m = rho * d^2 * pi / 4. * (l2 / 2.);\n");
+      fputs(fd, "set: real shaft_bearing2_center_Jx = shaft_bearing2_center_m * ((0.5 * d)^2 + (l2 / 2.)^2 / 3.) / 4.;\n");
+      fputs(fd, "set: real shaft_bearing2_center_Jy = shaft_bearing2_center_Jx;\n");
+      fputs(fd, "set: real shaft_bearing2_center_Jz = shaft_bearing2_center_m * ((0.5 * d)^2) / 2.;\n");
+      fputs(fd, "set: real shaft_A = d^2 * pi / 4;\n");
+      fputs(fd, "set: real shaft_As = 9. / 10. * shaft_A;\n");
+      fputs(fd, "set: real shaft_Iy = d^4 * pi / 64.;\n");
+      fputs(fd, "set: real shaft_Iz = shaft_Iy;\n");
+      fputs(fd, "set: real shaft_Ip = shaft_Iy + shaft_Iz;\n");
+      fputs(fd, "set: real shaft_It = shaft_Ip;\n");
+      fputs(fd, "set: real shaft_E = E;\n");
+      fputs(fd, "set: real shaft_nu = nu;\n");
+      fputs(fd, "set: real shaft_G = shaft_E / (2 * (1 + shaft_nu));\n");
+      fputs(fd, "set: real shaft_rho = rho;\n");
+      fputs(fd, "set: real shaft_damping_ratio = beta;\n");
+      fputs(fd, "set: real initial_time = 0.;\n");
+      fputs(fd, "set: real final_time = 2. * pi * n / (abs(omega1 + omega0) / 2.);\n");
+      fputs(fd, "begin: data;\n");
+      fputs(fd, "        problem: initial value;\n");
+      fputs(fd, "end: data;\n");
+      fputs(fd, "begin: initial value;\n");
+      fputs(fd, "        initial time: initial_time;\n");
+      fputs(fd, "        final time: final_time;\n");
+      fputs(fd, "        time step: 2. * pi / (360. * abs(omega0));\n");
+      fputs(fd, "        strategy: change, postponed, drive_id_time_step;\n");
+      fputs(fd, "        method: ms, 0.6;\n");
+      fputs(fd, "        tolerance: 1e-6, test, norm, 1e-6, test, norm;\n");
+      fputs(fd, "        derivatives tolerance: 1e-4, 1e-4;\n");
+      fputs(fd, "        max iterations: 100;\n");
+      fputs(fd, "        derivatives max iterations: 10;\n");
+      fputs(fd, "        derivatives coefficient: auto;\n");
+      fputs(fd, "        output: iterations;\n");
+      fputs(fd, "        linear solver: naive, colamd, scale, row max column max, always, max iterations, 100;\n");
+      fputs(fd, "        enforce constraint equations: constraint violations;\n");
+      fputs(fd, "        threads: assembly, 1;\n");
+      fputs(fd, "        nonlinear solver: nox, modified, 10,\n");
+      fputs(fd, "             keep jacobian matrix,\n");
+      fputs(fd, "             inner iterations before assembly, 6,\n");
+      fputs(fd, "             jacobian operator, newton krylov,\n");
+      fputs(fd, "             solver, line search based,\n");
+      fputs(fd, "             forcing term, type 2,\n");
+      fputs(fd, "             direction, newton,\n");
+      fputs(fd, "             weighted rms absolute tolerance, 0.,\n");
+      fputs(fd, "             weighted rms relative tolerance, 0.,\n");
+      fputs(fd, "             linear solver, gmres,\n");
+      fputs(fd, "             linear solver max iterations, 12,\n");
+      fputs(fd, "             krylov subspace size, 12;\n");
+      fputs(fd, "end: initial value;\n");
+      fputs(fd, "begin: control data;\n");
+      switch (istage)
+        case 1
+          fputs(fd, "     make restart file: times, 1, 0.5 * (final_time - initial_time) + initial_time, format, binary;\n");
+        case 2
+          fprintf(fd, "    load restart file: \"%s.0.rst\";\n", output_filename);
+          fputs(fd, "     skip initial joint assembly;\n");
+      endswitch
+      fputs(fd, "       use automatic differentiation;\n");
+      fputs(fd, "       default output: none, structural nodes;\n");
+      fputs(fd, "       default orientation: euler123;\n");
+      fputs(fd, "       output precision: 16;\n");
+      fputs(fd, "       max iterations: 0;\n");
+      fputs(fd, "        structural nodes: 5;\n");
+      fputs(fd, "        joints: 3;\n");
+      fputs(fd, "        beams: 2;\n");
+      fputs(fd, "        rigid bodies: 5;\n");
+      if (options.f_rbk)
+        fputs(fd, "        rigid body kinematics: drive, angular velocity,\n");
+        fputs(fd, "        component,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           array, 2,\n");
+        fputs(fd, "             mult, time, const, (omega1 - omega0) / (final_time - initial_time),\n");
+        fputs(fd, "             const, omega0,\n");
+        fputs(fd, "        angular acceleration,\n");
+        fputs(fd, "        component,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           const, (omega1 - omega0) / (final_time - initial_time);\n");
+      endif
+      fputs(fd, "          gravity;\n");
+      fputs(fd, "end: control data;\n");
+      fputs(fd, "reference: ref_id_ground,\n");
+      fputs(fd, "        reference, global, null,\n");
+      fputs(fd, "        reference, global, eye,\n");
+      fputs(fd, "        reference, global, null,\n");
+      fputs(fd, "        reference, global, null;\n");
+      fputs(fd, "reference: ref_id_shaft,\n");
+      fputs(fd, "        reference, ref_id_ground, null,\n");
+      fputs(fd, "        reference, ref_id_ground, eye,\n");
+      fputs(fd, "        reference, ref_id_ground, null,\n");
+      if (options.f_rbk)
+        fputs(fd, "        reference, ref_id_ground, null;\n");
+      else
+        fputs(fd, "        reference, ref_id_ground, 0., 0., omega0;\n");
+      endif
+      fputs(fd, "reference: ref_id_rotor,\n");
+      fputs(fd, "        reference, ref_id_shaft, 0., 0., o,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_shaft_section,\n");
+      fputs(fd, "           reference, ref_id_shaft, null,\n");
+      fputs(fd, "           reference, ref_id_shaft, 1, 0., 0., 1.,\n");
+      fputs(fd, "                                    2, 0., 1., 0.,\n");
+      fputs(fd, "           reference, ref_id_shaft, null,\n");
+      fputs(fd, "           reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_bearing1,\n");
+      fputs(fd, "        reference, ref_id_shaft,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   -0.5 * l,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_bearing1_center,\n");
+      fputs(fd, "        reference, ref_id_bearing1,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.5 * l1,\n");
+      fputs(fd, "        reference, ref_id_rotor, eye,\n");
+      fputs(fd, "        reference, ref_id_rotor, null,\n");
+      fputs(fd, "        reference, ref_id_rotor, null;\n");
+      fputs(fd, "reference: ref_id_bearing2,\n");
+      fputs(fd, "        reference, ref_id_shaft,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.5 * l,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_bearing2_center,\n");
+      fputs(fd, "        reference, ref_id_bearing2,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   -0.5 * l2,\n");
+      fputs(fd, "        reference, ref_id_rotor, eye,\n");
+      fputs(fd, "        reference, ref_id_rotor, null,\n");
+      fputs(fd, "        reference, ref_id_rotor, null;\n");
+      fputs(fd, "begin: nodes;\n");
+      fputs(fd, "        structural: node_id_rotor, dynamic,\n");
+      fputs(fd, "                reference, ref_id_rotor, null,\n");
+      fputs(fd, "                reference, ref_id_rotor, eye,\n");
+      fputs(fd, "                reference, ref_id_rotor, null,\n");
+      fputs(fd, "                reference, ref_id_rotor, null;\n");
+      fputs(fd, "        structural: node_id_bearing1, dynamic,\n");
+      fputs(fd, "                reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                reference, ref_id_bearing1, null;\n");
+      fputs(fd, "        structural: node_id_bearing1_center, dynamic,\n");
+      fputs(fd, "                reference, ref_id_bearing1_center, null,\n");
+      fputs(fd, "                reference, ref_id_bearing1_center, eye,\n");
+      fputs(fd, "                reference, ref_id_bearing1_center, null,\n");
+      fputs(fd, "                reference, ref_id_bearing1_center, null;\n");
+      fputs(fd, "        structural: node_id_bearing2, dynamic,\n");
+      fputs(fd, "                reference, ref_id_bearing2, null,\n");
+      fputs(fd, "                reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "                reference, ref_id_bearing2, null,\n");
+      fputs(fd, "                reference, ref_id_bearing2, null;\n");
+      fputs(fd, "        structural: node_id_bearing2_center, dynamic,\n");
+      fputs(fd, "                reference, ref_id_bearing2_center, null,\n");
+      fputs(fd, "                reference, ref_id_bearing2_center, eye,\n");
+      fputs(fd, "                reference, ref_id_bearing2_center, null,\n");
+      fputs(fd, "                reference, ref_id_bearing2_center, null;\n");
+      fputs(fd, "end: nodes;\n");
+      fputs(fd, "begin: elements;\n");
+      fputs(fd, "       drive caller: drive_id_rotor_speed, string, \"(omega1 - omega0) / (final_time - initial_time) * Time + omega0\", output, yes;\n");
+      fputs(fd, "       drive caller: drive_id_time_step, string, \"2. * pi / (max(1., 360. * abs(model::drive(drive_id_rotor_speed, Time))))\";\n");
+      fputs(fd, "       body: body_id_rotor,\n");
+      fputs(fd, "             node_id_rotor,\n");
+      fputs(fd, "             condense, 4,\n");
+      fputs(fd, "                rotor_m,\n");
+      fputs(fd, "                  reference, node, null,\n");
+      fputs(fd, "                  diag, rotor_Jx, rotor_Jy, rotor_Jz,\n");
+      fputs(fd, "                shaft_rotor1_m,\n");
+      fputs(fd, "                  reference, node, 0., 0., -w / 2. - l1 / 8.,\n");
+      fputs(fd, "                  diag, shaft_rotor1_Jx, shaft_rotor1_Jy, shaft_rotor1_Jz,\n");
+      fputs(fd, "                shaft_rotor2_m,\n");
+      fputs(fd, "                  reference, node, 0., 0., w / 2. + l2 / 8.,\n");
+      fputs(fd, "                  diag, shaft_rotor2_Jx, shaft_rotor2_Jy, shaft_rotor2_Jz,                \n");
+      fputs(fd, "                dm,\n");
+      fputs(fd, "                  reference, node, dr, 0., 0.,\n");
+      fputs(fd, "                  diag, 0., 0., 0.;\n");
+      fputs(fd, "        body: body_id_bearing1,\n");
+      fputs(fd, "              node_id_bearing1,\n");
+      fputs(fd, "              shaft_bearing1_m,\n");
+      fputs(fd, "              reference, node, 0., 0., l1 / 8.,\n");
+      fputs(fd, "              diag, shaft_bearing1_Jx, shaft_bearing1_Jy, shaft_bearing1_Jz;\n");
+      fputs(fd, "        body: body_id_bearing1_center,\n");
+      fputs(fd, "              node_id_bearing1_center,\n");
+      fputs(fd, "              shaft_bearing1_center_m,\n");
+      fputs(fd, "              reference, node, null,\n");
+      fputs(fd, "              diag, shaft_bearing1_center_Jx, shaft_bearing1_center_Jy, shaft_bearing1_center_Jz;\n");
+      fputs(fd, "        body: body_id_bearing2,\n");
+      fputs(fd, "              node_id_bearing2,\n");
+      fputs(fd, "              shaft_bearing2_m,\n");
+      fputs(fd, "              reference, node, 0., 0., -l2 / 8.,\n");
+      fputs(fd, "              diag, shaft_bearing2_Jx, shaft_bearing2_Jy, shaft_bearing2_Jz;\n");
+      fputs(fd, "        body: body_id_bearing2_center,\n");
+      fputs(fd, "              node_id_bearing2_center,\n");
+      fputs(fd, "              shaft_bearing2_center_m,\n");
+      fputs(fd, "              reference, node, null,\n");
+      fputs(fd, "              diag, shaft_bearing2_center_Jx, shaft_bearing2_center_Jy, shaft_bearing2_center_Jz;\n");
+      fputs(fd, "    beam3: beam_id_shaft1,\n");
+      fputs(fd, "                # node 1\n");
+      fputs(fd, "                node_id_bearing1, position, reference, node, null,\n");
+      fputs(fd, "                orientation, reference, ref_id_shaft_section, eye,\n");
+      fputs(fd, "                # node 2\n");
+      fputs(fd, "                node_id_bearing1_center, position, reference, node, null,\n");
+      fputs(fd, "                orientation, reference, ref_id_shaft_section, eye,\n");
+      fputs(fd, "                # node 3,\n");
+      fputs(fd, "                node_id_rotor, position, reference, node, 0., 0., -0.5 * w,\n");
+      fputs(fd, "                orientation, reference, ref_id_shaft_section, eye,\n");
+      fputs(fd, "                # orientation matrix section I\n");
+      fputs(fd, "                reference, ref_id_shaft_section, eye,\n");
+      fputs(fd, "                # constitutive law section I\n");
+      fputs(fd, "                linear viscoelastic generic,\n");
+      fputs(fd, "                diag, shaft_E * shaft_A , shaft_G * shaft_As, shaft_G * shaft_As,\n");
+      fputs(fd, "                      shaft_G * shaft_It, shaft_E * shaft_Iy, shaft_E * shaft_Iz,\n");
+      fputs(fd, "                proportional, shaft_damping_ratio,\n");
+      fputs(fd, "                # orientation matrix section II\n");
+      fputs(fd, "                same,\n");
+      fputs(fd, "                # constitutive law section II\n");
+      fputs(fd, "                same;\n");
+      fputs(fd, "    beam3: beam_id_shaft2,\n");
+      fputs(fd, "                # node 1\n");
+      fputs(fd, "                node_id_rotor, position, reference, node, 0., 0., 0.5 * w,\n");
+      fputs(fd, "                orientation, reference, ref_id_shaft_section, eye,                \n");
+      fputs(fd, "                # node 2\n");
+      fputs(fd, "                node_id_bearing2_center, position, reference, node, null,\n");
+      fputs(fd, "                orientation, reference, ref_id_shaft_section, eye,\n");
+      fputs(fd, "                # node 3,\n");
+      fputs(fd, "                node_id_bearing2, position, reference, node, null,\n");
+      fputs(fd, "                orientation, reference, ref_id_shaft_section, eye,\n");
+      fputs(fd, "                # orientation matrix section I\n");
+      fputs(fd, "                reference, ref_id_shaft_section, eye,\n");
+      fputs(fd, "                # constitutive law section I\n");
+      fputs(fd, "                linear viscoelastic generic,\n");
+      fputs(fd, "                diag, shaft_E * shaft_A , shaft_G * shaft_As, shaft_G * shaft_As,\n");
+      fputs(fd, "                      shaft_G * shaft_It, shaft_E * shaft_Iy, shaft_E * shaft_Iz,\n");
+      fputs(fd, "                proportional, shaft_damping_ratio,\n");
+      fputs(fd, "                # orientation matrix section II\n");
+      fputs(fd, "                same,\n");
+      fputs(fd, "                # constitutive law section II\n");
+      fputs(fd, "                same;\n");
+      fputs(fd, "        joint: joint_id_bearing1, total pin joint,\n");
+      fputs(fd, "                node_id_bearing1,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "               position constraint,\n");
+      fputs(fd, "                        active, active, active,\n");
+      fputs(fd, "                        null,\n");
+      fputs(fd, "               orientation constraint,\n");
+      fputs(fd, "                        inactive, inactive, inactive,\n");
+      fputs(fd, "                        null;\n");
+      fputs(fd, "        joint: joint_id_bearing2, total pin joint,\n");
+      fputs(fd, "               node_id_bearing2,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing2, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               position, reference, ref_id_bearing2, null,\n");
+      fputs(fd, "               position orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               rotation orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               position constraint,\n");
+      fputs(fd, "                        active, active, inactive,\n");
+      fputs(fd, "                        null,\n");
+      fputs(fd, "               orientation constraint,\n");
+      fputs(fd, "                        inactive, inactive, inactive,\n");
+      fputs(fd, "                        null;\n");
+      fputs(fd, "     joint: joint_id_drive, angular velocity,\n");
+      fputs(fd, "             # node label\n");
+      fputs(fd, "             node_id_rotor, \n");
+      fputs(fd, "             # direction\n");
+      fputs(fd, "             0.,0.,1.,\n");
+      fputs(fd, "             # angular velocity\n");
+      if (options.f_rbk)
+        fputs(fd, "           null;\n");
+      else
+        fputs(fd, "           reference, drive_id_rotor_speed;\n");
+      endif
+      fputs(fd, "        inertia: elem_id_inertia,\n");
+      fputs(fd, "                 position, reference, ref_id_rotor, null,\n");
+      fputs(fd, "                 orientation, reference, ref_id_rotor, eye,\n");
+      fputs(fd, "                 body, all, joint, all, loadable, all,\n");
+      fputs(fd, "                 output, both;\n");
+      fputs(fd, "        gravity: uniform, 0., 0., -1., g;\n");
+      fputs(fd, "end: elements;\n");
+    unwind_protect_cleanup
+      if (fd ~= -1)
+        fclose(fd);
+      endif
+    end_unwind_protect
+  endfunction
+
+  function mesh = generate_mesh(filename, options, param)
+    geometry_file = [filename, ".geo"];
+    unwind_protect
+      [fd, msg] = fopen(geometry_file, "w");
+      if (fd == -1)
+        error("failed to open file \"%s\"", geometry_file);
+      endif
+      fn = fieldnames(param);
+      for i=1:length(fn)
+        fprintf(fd, "%s = %g;\n", fn{i}, getfield(param, fn{i}));
+      endfor
+      fputs(fd, "SetFactory(\"Built-in\");\n");
+      fputs(fd, "Point(1) = {0, 0, -0.5 * l};\n");
+      fputs(fd, "Point(2) = {0.5 * d, 0, -0.5 * l};\n");
+      fputs(fd, "Point(3) = {0.5 * d, 0, -0.5 * w + o};\n");
+      fputs(fd, "Point(4) = {0.5 * D, 0, -0.5 * w + o};\n");
+      fputs(fd, "Point(5) = {0.5 * D, 0, 0.5 * w + o};\n");
+      fputs(fd, "Point(6) = {0.5 * d, 0, 0.5 * w + o};\n");
+      fputs(fd, "Point(7) = {0.5 * d, 0, 0.5 * l};\n");
+      fputs(fd, "Point(8) = {0, 0, 0.5 * l};\n");
+      fputs(fd, "Point(9) = {0, 0, 0.5 * w + o};\n");
+      fputs(fd, "Point(10) = {0, 0, -0.5 * w + o};\n");
+      fputs(fd, "Line(1) = {1, 2};\n");
+      fputs(fd, "Line(2) = {2, 3};\n");
+      fputs(fd, "Line(3) = {3, 4};\n");
+      fputs(fd, "Line(4) = {4, 5};\n");
+      fputs(fd, "Line(5) = {5, 6};\n");
+      fputs(fd, "Line(6) = {6, 7};\n");
+      fputs(fd, "Line(7) = {7, 8};\n");
+      fputs(fd, "Line(8) = {8, 9};\n");
+      fputs(fd, "Line(9) = {9, 10};\n");
+      fputs(fd, "Line(10) = {10, 1};\n");
+      fputs(fd, "Line(11) = {10,3};\n");
+      fputs(fd, "Line(12) = {3,6};\n");
+      fputs(fd, "Line(13) = {6,9};\n");
+      fputs(fd, "Transfinite Curve(1) = Max(1, Round(0.5 * d / h1)) + 1;\n");
+      fputs(fd, "Transfinite Curve(2) = Max(1, Round((0.5 * l - 0.5 * w + o) / h1)) + 1;\n");
+      fputs(fd, "Transfinite Curve(3) = Max(1, Round((0.5 * (D - d)) / h2)) + 1;\n");
+      fputs(fd, "Transfinite Curve(4) = Max(1, Round(w / h2)) + 1;\n");
+      fputs(fd, "Transfinite Curve(5) = Max(1, Round((0.5 * (D - d)) / h2)) + 1;\n");
+      fputs(fd, "Transfinite Curve(6) = Max(1, Round((0.5 * l - 0.5 * w - o) / h1)) + 1;\n");
+      fputs(fd, "Transfinite Curve(7) = Max(1, Round(0.5 * d / h1)) + 1;\n");
+      fputs(fd, "Transfinite Curve(8) = Max(1, Round((0.5 * l - 0.5 * w - o) / h1)) + 1;\n");
+      fputs(fd, "Transfinite Curve(9) = Max(1, Round(w / h2)) + 1;\n");
+      fputs(fd, "Transfinite Curve(10) = Max(1, Round((0.5 * l - 0.5 * w + o) / h1)) + 1;\n");
+      fputs(fd, "Transfinite Curve(11) = Max(1, Round(0.5 * d / h1)) + 1;\n");
+      fputs(fd, "Transfinite Curve(12) = Max(1, Round(w / h2)) + 1;\n");
+      fputs(fd, "Transfinite Curve(13) = Max(1, Round(0.5 * d / h1)) + 1;\n");
+      fputs(fd, "Line Loop(1) = {1, 2, -11, 10};\n");
+      fputs(fd, "Line Loop(2) = {11, 12, 13, 9};\n");
+      fputs(fd, "Line Loop(3) = {3, 4, 5, -12};\n");
+      fputs(fd, "Line Loop(4) = {-13, 6, 7, 8};\n");
+      fputs(fd, "Plane Surface(1) = {1};\n");
+      fputs(fd, "Plane Surface(2) = {2};\n");
+      fputs(fd, "Plane Surface(3) = {3};\n");
+      fputs(fd, "Plane Surface(4) = {4};\n");
+      fputs(fd, "Transfinite Surface(1) = {1, 2, 3, 10};\n");
+      fputs(fd, "Transfinite Surface(2) = {10, 3, 6, 9};\n");
+      fputs(fd, "Transfinite Surface(3) = {3, 4, 5, 6};\n");
+      fputs(fd, "Transfinite Surface(4) = {9, 6, 7, 8};\n");
+      fputs(fd, "vol11[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{1}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol21[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol11[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol31[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol21[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol41[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol31[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "Recombine Surface{1, vol11[0]};\n");
+      fputs(fd, "Recombine Surface{vol11[0], vol21[0]};\n");
+      fputs(fd, "Recombine Surface{vol21[0], vol31[0]};\n");
+      fputs(fd, "Recombine Surface{vol31[0], vol41[0]};\n");
+      fputs(fd, "vol12[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{2}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol22[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol12[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol32[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol22[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol42[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol32[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "Recombine Surface{2, vol12[0]};\n");
+      fputs(fd, "Recombine Surface{vol12[0], vol22[0]};\n");
+      fputs(fd, "Recombine Surface{vol22[0], vol32[0]};\n");
+      fputs(fd, "Recombine Surface{vol32[0], vol42[0]};\n");
+      fputs(fd, "vol13[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{3}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol23[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol13[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol33[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol23[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol43[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol33[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "Recombine Surface{3, vol13[0]};\n");
+      fputs(fd, "Recombine Surface{vol13[0], vol23[0]};\n");
+      fputs(fd, "Recombine Surface{vol23[0], vol33[0]};\n");
+      fputs(fd, "Recombine Surface{vol33[0], vol43[0]};\n");
+      fputs(fd, "vol14[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{4}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol24[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol14[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol34[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol24[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "vol44[] = Extrude{{0, 0, 1},{0, 0, 0}, Pi/2}{ Surface{vol34[0]}; Layers{Max(2,Round(d * Pi / (4. * h1)))}; Recombine; };\n");
+      fputs(fd, "Recombine Surface{4, vol14[0]};\n");
+      fputs(fd, "Recombine Surface{vol14[0], vol24[0]};\n");
+      fputs(fd, "Recombine Surface{vol24[0], vol34[0]};\n");
+      fputs(fd, "Recombine Surface{vol34[0], vol44[0]};\n");
+      fputs(fd, "Coherence;\n");
+      switch (options.elem_type)
+        case "iso8"
+          fputs(fd, "Mesh.SecondOrderIncomplete = 0;\n");
+          fputs(fd, "Mesh.ElementOrder = 1;");
+        case "iso20"
+          fputs(fd, "Mesh.SecondOrderIncomplete = 1;\n");
+          fputs(fd, "Mesh.ElementOrder = 2;");
+      endswitch
+      fputs(fd, "Mesh 3;\n");
+      fputs(fd, "Coherence Mesh;\n");
+      fputs(fd, "Physical Surface(\"bearing1\", 1) = {21, 38, 55, 72};\n");
+      fputs(fd, "Physical Surface(\"bearing2\", 2) = {260, 243, 277, 294};\n");
+      fputs(fd, "Physical Surface(\"rotor\", 3) = {202, 180, 224, 158};\n");
+      fputs(fd, "Physical Volume(\"shaft\", 1) = {1, 2, 4, 3, 14, 13, 15, 16, 5, 6, 8, 7};\n");
+      fputs(fd, "Physical Volume(\"disc\", 2) = {9, 10, 12, 11};\n");
+      fputs(fd, "Mesh.Format = 1;\n");
+      fprintf(fd, "Save \"%s.msh\";\n", filename);
+    unwind_protect_cleanup
+      if (fd ~= -1)
+        fclose(fd);
+      endif
+    end_unwind_protect
+    ##spawn_wait(spawn("gmsh", {geometry_file})); return;
+    fprintf(stderr, "meshing ...\n");
+    pid = spawn("gmsh", {"-format", "msh2", "-0",geometry_file});
+    status = spawn_wait(pid);
+    if (status ~= 0)
+      warning("gmsh failed with status %d", status);
+    endif
+    fprintf(stderr, "loading mesh ...\n");
+    switch (options.elem_type)
+      case "iso20"
+        opt_mesh.elem_type = {"iso20", "penta15", "quad8", "tria6h"};
+      case "iso8"
+        opt_mesh.elem_type = {"iso8", "penta6", "iso4", "tria3"};
+    endswitch
+    mesh = fem_pre_mesh_reorder(fem_pre_mesh_import([filename, ".msh"], "gmsh",opt_mesh));
+  endfunction
+
+  function [mesh, cms_opt, load_case_dof] = generate_model(mesh, options, param)
+    cms_opt.algorithm = "shift-invert";
+    cms_opt.refine_max_iter = int32(3);
+    cms_opt.element.name = "elem_id_rotor";
+    cms_opt.nodes.modal.name = "node_id_rotor";
+    cms_opt.nodes.interfaces(1).name = "node_id_bearing1";
+    cms_opt.nodes.interfaces(2).name = "node_id_bearing2";
+    cms_opt.number_of_threads = mbdyn_solver_num_threads_default();
+    cms_opt.verbose = options.verbose;
+    cms_opt.modes.number = int32(options.number_of_modes);
+    cms_opt.nodes.modal.number = int32(rows(mesh.nodes) + 1);
+    cms_opt.nodes.interfaces(1).number = int32(rows(mesh.nodes) + 2);
+    cms_opt.nodes.interfaces(2).number = int32(rows(mesh.nodes) + 3);
+    cms_opt.invariants = true;
+    mesh.nodes(cms_opt.nodes.modal.number, :) = [0, 0, param.o, 0, 0, 0];
+    mesh.nodes([cms_opt.nodes.interfaces.number], :) = [0, 0, -0.5 * param.l, 0, 0, 0;
+                                                        0, 0,  0.5 * param.l, 0, 0, 0];
+    switch (options.elem_type)
+      case "iso20"
+        mesh.elements.rbe3(2) = fem_pre_mesh_rbe3_from_surf(mesh, 1, cms_opt.nodes.interfaces(1).number, "tria6h");
+        mesh.elements.rbe3(3) = fem_pre_mesh_rbe3_from_surf(mesh, 2, cms_opt.nodes.interfaces(2).number, "tria6h");
+        mesh.elements.rbe3(1) = fem_pre_mesh_rbe3_from_surf(mesh, 3, cms_opt.nodes.modal.number, "quad8");
+      case "iso8"
+        mesh.elements.rbe3(2) = fem_pre_mesh_rbe3_from_surf(mesh, 1, cms_opt.nodes.interfaces(1).number, "iso4");
+        mesh.elements.rbe3(3) = fem_pre_mesh_rbe3_from_surf(mesh, 2, cms_opt.nodes.interfaces(2).number, "iso4");
+        mesh.elements.rbe3(1) = fem_pre_mesh_rbe3_from_surf(mesh, 3, cms_opt.nodes.modal.number, "iso4");
+    endswitch
+    load_case_dof.locked_dof = false(rows(mesh.nodes), columns(mesh.nodes));
+    switch (options.elem_type)
+      case "iso20"
+        mesh.materials.iso20 = zeros(rows(mesh.elements.iso20), 1, "int32");
+        mesh.materials.penta15 = zeros(rows(mesh.elements.penta15), 1, "int32");
+        mesh.materials.iso20([mesh.groups.iso20(find([mesh.groups.iso20.id] == 1)).elements]) = 1;
+        mesh.materials.iso20([mesh.groups.iso20(find([mesh.groups.iso20.id] == 2)).elements]) = 2;
+        mesh.materials.penta15([mesh.groups.penta15(find([mesh.groups.penta15.id] == 1)).elements]) = 1;
+        mesh.materials.penta15([mesh.groups.penta15(find([mesh.groups.penta15.id] == 2)).elements]) = 2;
+      case "iso8"
+        mesh.materials.iso8 = zeros(rows(mesh.elements.iso8), 1, "int32");
+        mesh.materials.iso8([mesh.groups.iso8(find([mesh.groups.iso8.id] == 1)).elements]) = 1;
+        mesh.materials.iso8([mesh.groups.iso8(find([mesh.groups.iso8.id] == 2)).elements]) = 2;
+    endswitch
+    mesh.material_data(1).rho = param.rho;
+    mesh.material_data(1).E = param.E;
+    mesh.material_data(1).nu = param.nu;
+    mesh.material_data(2).rho = param.rho;
+    mesh.material_data(2).E = 100 * param.E;
+    mesh.material_data(2).nu = param.nu;
+  endfunction
+
+  function [mesh_cms, mat_ass, dof_map, sol_eig, cms_opt] = generate_cms(mesh, cms_opt, load_case_dof, filename, options, param)
+    fprintf(stderr, "building cms element ...\n");
+    [mesh_cms, mat_ass, dof_map, sol_eig, cms_opt] = fem_cms_create2(mesh, load_case_dof, cms_opt);
+    mesh_post_pro_file = sprintf("%s_post.msh", filename);
+    fem_post_mesh_export(mesh_post_pro_file, mesh_cms);
+    for j=1:size(sol_eig.def, 3)
+      eig_post_pro_file_mode{j} = sprintf("%s_eig_def_%03d.msh", filename, j);
+      fem_post_sol_step_export(eig_post_pro_file_mode{j}, sol_eig, j, j, sol_eig.f(j), options.scale_def / max(norm(sol_eig.def(:, 1:3, j), "rows")));
+    endfor
+    eig_post_pro_file = sprintf("%s_modes_post.geo", filename);
+    fd = -1;
+    unwind_protect
+      [fd, msg] = fopen(eig_post_pro_file, "w");
+      if (fd == -1)
+        error("failed to open file \"%s\"", eig_post_pro_file);
+      endif
+      fprintf(fd, "Merge \"%s\";\n", mesh_post_pro_file);
+      for j=1:numel(eig_post_pro_file_mode)
+        fprintf(fd, "Merge \"%s\";\n", eig_post_pro_file_mode{j});
+      endfor
+      fputs(fd, "View.Type = 1;\n");
+      fputs(fd, "View.VectorType = 5;\n");
+      fputs(fd, "View.Visible = 1;\n");
+      fputs(fd, "View.DisplacementFactor = 1;\n");
+      fputs(fd, "View.ShowTime = 6;\n");
+      fputs(fd, "View.ShowElement = 1;\n");
+      fputs(fd, "View.IntervalsType = 3;\n");
+      fputs(fd, "View.NbIso = 20;\n");
+    unwind_protect_cleanup
+      if (fd ~= -1)
+        fclose(fd);
+      endif
+    end_unwind_protect
+    mat_ass.Dred = param.alpha * mat_ass.Mred + param.beta * mat_ass.Kred;
+    fem_cms_export([filename, "_cms"], mesh_cms, dof_map, mat_ass, cms_opt);
+  endfunction
+
+  function opt_mbd_mesh = write_solid_elem_files(mesh, cms_opt, load_case_dof, filename, param)
+    opt_mbd_mesh.struct_nodes.type = repmat(MBDYN_NODE_TYPE_DYNAMIC_STRUCT_DISP, rows(mesh.nodes), 1);
+    opt_mbd_mesh.struct_nodes.type([cms_opt.nodes.modal.number]) = MBDYN_NODE_TYPE_DYNAMIC_STRUCT;
+    opt_mbd_mesh.struct_nodes.type([cms_opt.nodes.interfaces.number]) = MBDYN_NODE_TYPE_STATIC_STRUCT;
+    opt_mbd_mesh.struct_nodes.reference_frame = "ref_id_shaft";
+    solid_csl_file = [filename, "_solid.csl"];
+    solid_nodes_file = [filename, "_solid.nod"];
+    solid_elem_file = [filename, "_solid.elm"];
+    load_case = struct();
+    assert_simple(mesh.nodes(cms_opt.nodes.modal.number, 1:3), [0, 0, param.o]);
+    assert_simple(mesh.nodes(cms_opt.nodes.interfaces(1).number, 1:3), [0, 0, -0.5 * param.l]);
+    assert_simple(mesh.nodes(cms_opt.nodes.interfaces(2).number, 1:3), [0, 0, 0.5 * param.l]);
+    opt_mbd_mesh = mbdyn_pre_solid_write_const_laws(mesh, solid_csl_file, opt_mbd_mesh);
+    opt_mbd_mesh = mbdyn_pre_solid_write_nodes(mesh, solid_nodes_file, opt_mbd_mesh);
+    opt_mbd_mesh = mbdyn_pre_solid_write_elements(mesh, load_case_dof, load_case, solid_elem_file, opt_mbd_mesh);
+  endfunction
+
+  function write_solid_file(mesh, cms_opt, opt_mbd_mesh, base_filename, input_filename, output_filename, istage, options)
+    solid_csl_file = [base_filename, "_solid.csl"];
+    solid_nodes_file = [base_filename, "_solid.nod"];
+    solid_elem_file = [base_filename, "_solid.elm"];
+
+    fd = -1;
+    unwind_protect
+      fd = fopen(input_filename, "w");
+      if (fd == -1)
+        error("failed to open file \"%s\"", input_filename);
+      endif
+      fputs(fd, "include: \"${MBDYN_ROTOR_DYN_CMS_PARAM_FILE}\";\n");
+      fputs(fd, "set: integer ref_id_ground = 1001;\n");
+      fputs(fd, "set: integer ref_id_shaft = 1002;\n");
+      fputs(fd, "set: integer ref_id_rotor = 1003;\n");
+      fputs(fd, "set: integer ref_id_bearing1 = 1004;\n");
+      fputs(fd, "set: integer ref_id_bearing2 = 1005;\n");
+      fprintf(fd, "set: integer node_id_rotor = %d;\n", cms_opt.nodes.modal.number);
+      fprintf(fd, "set: integer node_id_bearing1 = %d;\n", cms_opt.nodes.interfaces(1).number);
+      fprintf(fd, "set: integer node_id_bearing2 = %d;\n", cms_opt.nodes.interfaces(2).number);
+      fputs(fd, "set: integer body_id_unbalance = 2001;\n");
+      fputs(fd, "set: integer joint_id_bearing1 = 3006;\n");
+      fputs(fd, "set: integer joint_id_bearing2 = 3007;\n");
+      fputs(fd, "set: integer joint_id_drive = 3008;\n");
+      fputs(fd, "set: integer elem_id_inertia = 4001;\n");
+      fputs(fd, "set: integer drive_id_rotor_speed = 5001;\n");
+      fputs(fd, "set: integer drive_id_time_step = 5002;\n");
+      fputs(fd, "set: real initial_time = 0.;\n");
+      fputs(fd, "set: real final_time = 2. * pi * n / (abs(omega1 + omega0) / 2.);\n");
+      fputs(fd, "begin: data;\n");
+      fputs(fd, "        problem: initial value;\n");
+      fputs(fd, "end: data;\n");
+      fputs(fd, "begin: initial value;\n");
+      fputs(fd, "        initial time: initial_time;\n");
+      fputs(fd, "        final time: final_time;\n");
+      fputs(fd, "        time step: 2. * pi / (360. * abs(omega0));\n");
+      fputs(fd, "        strategy: change, postponed, drive_id_time_step;\n");
+      fputs(fd, "        method: ms, 0.6;\n");
+      fputs(fd, "        tolerance: 1e-5, test, sepnorm, 1e-5, test, norm;\n");
+      fputs(fd, "        derivatives tolerance: 1e-3, 1e-3;\n");
+      fputs(fd, "        max iterations: 100;\n");
+      fputs(fd, "        derivatives max iterations: 10;\n");
+      fputs(fd, "        derivatives coefficient: auto;\n");
+      fputs(fd, "        output: iterations, cpu time, solver condition number, stat, yes;\n");
+      fputs(fd, "        linear solver: umfpack, grad, scale, iterative, always, max iterations, 0;\n");
+      fputs(fd, "        enforce constraint equations: constraint violations;\n");
+      fprintf(fd, "      threads: assembly, %d;\n", mbdyn_solver_num_threads_default());
+      fprintf(fd, "      threads: solver, %d;\n", mbdyn_solver_num_threads_default());
+      fputs(fd, "        nonlinear solver: nox, modified, 25,\n");
+      fputs(fd, "             keep jacobian matrix,\n");
+      fputs(fd, "             inner iterations before assembly, 12,\n");
+      fputs(fd, "             use preconditioner as solver, no,\n");
+      fputs(fd, "             jacobian operator, newton krylov,\n");
+      fputs(fd, "             solver, line search based,\n");
+      fputs(fd, "             forcing term, type 2,\n");
+      fputs(fd, "             forcing term min tolerance, 1e-10,\n");
+      fputs(fd, "             forcing term max tolerance, 1e-8,\n");
+      fputs(fd, "             direction, newton,\n");
+      fputs(fd, "             weighted rms absolute tolerance, 0.,\n");
+      fputs(fd, "             weighted rms relative tolerance, 0.,\n");
+      fputs(fd, "             linear solver, gmres,\n");
+      fputs(fd, "             linear solver max iterations, 24,\n");
+      fputs(fd, "             krylov subspace size, 24;\n");
+      fputs(fd, "end: initial value;\n");
+      fputs(fd, "begin: control data;\n");
+      switch (istage)
+        case 1
+          fputs(fd, "     make restart file: times, 1, 0.5 * (final_time - initial_time) + initial_time, format, binary;\n");
+        case 2
+          fprintf(fd, "    load restart file: \"%s.0.rst\";\n", output_filename);
+          fputs(fd, "     skip initial joint assembly;\n");
+      endswitch
+      fputs(fd, "       use automatic differentiation;\n");
+      fputs(fd, "       default output: none, structural nodes;\n");
+      fputs(fd, "       default orientation: euler123;\n");
+      fputs(fd, "       output precision: 16;\n");
+      fputs(fd, "       max iterations: 0;\n");
+      fprintf(fd, "        structural nodes: %d;\n", opt_mbd_mesh.struct_nodes.number);
+      fprintf(fd, "        solids: %d;\n", opt_mbd_mesh.solids.number);
+      fprintf(fd, "        joints: 3 + %d;\n", opt_mbd_mesh.joints.number);
+      fputs(fd,   "        rigid bodies: 1;\n");
+      if (options.f_rbk)
+        fputs(fd, "        rigid body kinematics: drive, angular velocity,\n");
+        fputs(fd, "        component,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           array, 2,\n");
+        fputs(fd, "             mult, time, const, (omega1 - omega0) / (final_time - initial_time),\n");
+        fputs(fd, "             const, omega0,\n");
+        fputs(fd, "        angular acceleration,\n");
+        fputs(fd, "        component,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           null,\n");
+        fputs(fd, "           const, (omega1 - omega0) / (final_time - initial_time);\n");
+      endif
+      fputs(fd, "          gravity;\n");
+      fputs(fd, "end: control data;\n");
+      fputs(fd, "reference: ref_id_ground,\n");
+      fputs(fd, "        reference, global, null,\n");
+      fputs(fd, "        reference, global, eye,\n");
+      fputs(fd, "        reference, global, null,\n");
+      fputs(fd, "        reference, global, null;\n");
+      fputs(fd, "reference: ref_id_shaft,\n");
+      fputs(fd, "        reference, ref_id_ground, null,\n");
+      fputs(fd, "        reference, ref_id_ground, eye,\n");
+      fputs(fd, "        reference, ref_id_ground, null,\n");
+      if (options.f_rbk)
+        fputs(fd, "        reference, ref_id_ground, null;\n");
+      else
+        fputs(fd, "        reference, ref_id_ground, 0., 0., omega0;\n");
+      endif
+      fputs(fd, "reference: ref_id_rotor,\n");
+      fputs(fd, "        reference, ref_id_shaft, 0., 0., o,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_bearing1,\n");
+      fputs(fd, "        reference, ref_id_shaft,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   -0.5 * l,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "reference: ref_id_bearing2,\n");
+      fputs(fd, "        reference, ref_id_shaft,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.,\n");
+      fputs(fd, "                   0.5 * l,\n");
+      fputs(fd, "        reference, ref_id_shaft, eye,\n");
+      fputs(fd, "        reference, ref_id_shaft, null,\n");
+      fputs(fd, "        reference, ref_id_shaft, null;\n");
+      fputs(fd, "begin: nodes;\n");
+      fprintf(fd, "include: \"%s\";\n", solid_nodes_file);
+      fputs(fd, "end: nodes;\n");
+      fprintf(fd, "include: \"%s\";\n", solid_csl_file);
+      fputs(fd, "begin: elements;\n");
+      fputs(fd, "       drive caller: drive_id_rotor_speed, string, \"(omega1 - omega0) / (final_time - initial_time) * Time + omega0\", output, yes;\n");
+      fputs(fd, "       drive caller: drive_id_time_step, string, \"2. * pi / (max(1., 360. * abs(model::drive(drive_id_rotor_speed, Time))))\";\n");
+      fprintf(fd, "      include: \"%s\";\n", solid_elem_file);
+      fputs(fd, "       body: body_id_unbalance,\n");
+      fputs(fd, "             node_id_rotor,\n");
+      fputs(fd, "                dm,\n");
+      fputs(fd, "                  reference, ref_id_rotor, dr, 0., 0.,\n");
+      fputs(fd, "                  diag, 0., 0., 0.;\n");
+      fputs(fd, "        joint: joint_id_bearing1, total pin joint,\n");
+      fputs(fd, "                node_id_bearing1,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing1, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing1, eye,\n");
+      fputs(fd, "               position constraint,\n");
+      fputs(fd, "                        active, active, active,\n");
+      fputs(fd, "                        null,\n");
+      fputs(fd, "               orientation constraint,\n");
+      fputs(fd, "                        inactive, inactive, inactive,\n");
+      fputs(fd, "                        null;\n");
+      fputs(fd, "        joint: joint_id_bearing2, total pin joint,\n");
+      fputs(fd, "               node_id_bearing2,\n");
+      fputs(fd, "                        position, reference, ref_id_bearing2, null,\n");
+      fputs(fd, "                        position orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "                        rotation orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               position, reference, ref_id_bearing2, null,\n");
+      fputs(fd, "               position orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               rotation orientation, reference, ref_id_bearing2, eye,\n");
+      fputs(fd, "               position constraint,\n");
+      fputs(fd, "                        active, active, inactive,\n");
+      fputs(fd, "                        null,\n");
+      fputs(fd, "               orientation constraint,\n");
+      fputs(fd, "                        inactive, inactive, inactive,\n");
+      fputs(fd, "                        null;\n");
+      fputs(fd, "     joint: joint_id_drive, angular velocity,\n");
+      fputs(fd, "             # node label\n");
+      fputs(fd, "             node_id_rotor, \n");
+      fputs(fd, "             # direction\n");
+      fputs(fd, "             0.,0.,1.,\n");
+      fputs(fd, "             # angular velocity\n");
+      if (~options.f_rbk)
+        fputs(fd, "           reference, drive_id_rotor_speed;\n");
+      else
+        fputs(fd, "           null;\n");
+      endif
+      fputs(fd, "        inertia: elem_id_inertia,\n");
+      fputs(fd, "                 position, reference, ref_id_rotor, null,\n");
+      fputs(fd, "                 orientation, reference, ref_id_rotor, eye,\n");
+      fputs(fd, "                 body, all, solid, all, loadable, all,\n");
+      fputs(fd, "                 output, both;\n");
+      fputs(fd, "        gravity: uniform, 0., 0., -1., g;\n");
+      fputs(fd, "end: elements;\n");
+    unwind_protect_cleanup
+      if (fd ~= -1)
+        fclose(fd);
+      endif
+    end_unwind_protect
+  endfunction
+
+  function [omega, r] = rotordynamics_test_case(param, options, SI_unit)
+    enable_filenames = [options.f_enable_modal, options.f_enable_beam, options.f_enable_solid];
+
+    mbdyn_filename_suffix = {"cms", "beam", "solid"};
+
+    r = omega = cell(2, numel(mbdyn_filename_suffix));
+
+    filename = tempname();
+
+    if (ispc())
+      filename(filename == "\\") = "/";
+    endif
+
+    unwind_protect
+      for istage=1:2
+        for i=1:numel(mbdyn_filename_suffix)
+          mbdyn_filenames{i} = [filename, mbdyn_filename_suffix{i}, ".mbdyn"];
+        endfor
+        fd = -1;
+        if (options.f_enable_modal)
+          write_modal_file(mbdyn_filenames{1}, sprintf("%s_%d", filename, 1), istage);
+        endif
+        fd = -1;
+        if (options.f_enable_beam)
+          write_beam_file(mbdyn_filenames{2}, sprintf("%s_%d", filename, 2), istage, options);
+        endif
+        fd = -1;
+        if (istage == 1 && options.f_enable_modal || options.f_enable_solid)
+          mesh = generate_mesh(filename, options, param);
+          [mesh, cms_opt, load_case_dof] = generate_model(mesh, options, param);
+        endif
+        if (istage == 1 && options.f_enable_modal)
+          [mesh_cms, mat_ass, dof_map, sol_eig, cms_opt] = generate_cms(mesh, cms_opt, load_case_dof, filename, options, param);
+        endif
+        if (options.f_enable_solid)
+          if (istage == 1)
+            opt_mbd_mesh = write_solid_elem_files(mesh, cms_opt, load_case_dof, filename, param);
+          endif
+          write_solid_file(mesh, cms_opt, opt_mbd_mesh, filename, mbdyn_filenames{3}, sprintf("%s_%d", filename, 3), istage, options);
+        endif
+        for i=1:numel(mbdyn_filenames)
+          if (~enable_filenames(i))
+            continue;
+          endif
+          options_mbd(i).output_file = sprintf("%s_%d", filename, i);
+          if (~options.verbose)
+            options_mbd(i).logfile = sprintf("%s_%d.stdout", filename, i);
+          endif
+          options_mbd(i).f_run_mbdyn2easyanim = false;
+          param_file = sprintf("%s_%d.set", filename, i);
+          putenv("MBDYN_ROTOR_DYN_CMS_ELEM_FILE", [filename, "_cms.elm"]);
+          putenv("MBDYN_ROTOR_DYN_CMS_PARAM_FILE", param_file);
+          mbdyn_pre_write_param_file(param_file, param);
+          mbdyn_solver_run(mbdyn_filenames{i}, options_mbd(i));
+          res(i).log_dat = mbdyn_post_load_log(options_mbd(i).output_file);
+          [res(i).t, res(i).trajectory, res(i).deformation, res(i).velocity, res(i).acceleration, res(i).node_id] = mbdyn_post_load_output_struct(options_mbd(i).output_file);
+          res(i).log_dat.vars = mbdyn_post_id_to_index(res(i), res(i).log_dat.vars);
+          [res(i).drive_id, res(i).drive_data] = mbdyn_post_load_output_drv(options_mbd(i).output_file);
+        endfor
+        if (options.f_plot)
+          figure("visible", "off");
+          hold on;
+        endif
+        for i=1:numel(mbdyn_filenames)
+          if (~enable_filenames(i))
+            continue;
+          endif
+          omega{istage, i} = res(i).drive_data{find(res(i).drive_id == res(i).log_dat.vars.drive_id_rotor_speed)};
+          r{istage, i} = norm(res(i).trajectory{res(i).log_dat.vars.node_idx_rotor}(:, 1:2), "rows");
+          if (options.f_plot)
+            plot(omega{istage, i} * 30 / pi * (1 / SI_unit.second), 1e3 * r{istage, i} * SI_unit.meter, sprintf("-;%s;%d", printable_title(mbdyn_filename_suffix{i}), i));
+          endif
+        endfor
+        if (options.f_plot)
+          xlabel("n [rpm]");
+          ylabel("r [mm]");
+          grid on;
+          grid minor on;
+          title("resonance curve center of disk versus speed - magnitude");
+          figure_list();
+        endif
+        tol = 0.5e-2;
+        for i=1:numel(mbdyn_filenames)
+          if (~enable_filenames(i))
+            continue;
+          endif
+          for j=1:numel(mbdyn_filenames)
+            if (~enable_filenames(j))
+              continue;
+            endif
+            assert_simple(interp1(omega{istage, i}, r{istage, i}, omega{istage, j}, "pchip", "extrap"), r{istage, j}, tol * max(abs(r{istage, j})));
+          endfor
+        endfor
+      endfor
+      tolstage = 1e-4;
+      for j=1:numel(mbdyn_filenames)
+        if (~enable_filenames(j))
+          continue;
+        endif
+        assert_simple(interp1(omega{1, j}, r{1, j}, omega{2, j}, "pchip", "extrap"), r{2, j}, tolstage * max(abs(r{2, j})));
+      endfor
+    unwind_protect_cleanup
+      if (numel(filename))
+        fn = dir([filename, "*"]);
+        for i=1:numel(fn)
+          if (0 ~= unlink(fullfile(fn(i).folder, fn(i).name)))
+            warning("failed to remove file \"%s\"", fn(i).name);
+          endif
+        endfor
+      endif
+    end_unwind_protect
+  endfunction
+
+  ## Define the unit system
+  SI_unit.meter = 1e-3;
+  SI_unit.second = 1e-3;
+  SI_unit.kilogram = 1e-3;
+  SI_unit.newton = SI_unit.kilogram * SI_unit.meter / SI_unit.second^2;
+  SI_unit.pascal = SI_unit.newton / SI_unit.meter^2;
+  param.alpha = 0 / (1 / SI_unit.second);
+  param.beta = 1e-7 / (SI_unit.second);
+  param.h1 = 5e-3 / SI_unit.meter; ## fine mesh size
+  param.h2 = 5e-3 / SI_unit.meter; ## fine mesh size
+  param.l = 350e-3 / SI_unit.meter; ## bearing distance
+  param.d = 10e-3 / SI_unit.meter; ## shaft diameter
+  param.D = 150e-3 / SI_unit.meter; ## disk diameter
+  param.w = 15e-3 / SI_unit.meter; ## disk width
+  param.o = 75e-3 / SI_unit.meter; ## disk offset
+  param.ecg = 1e-3 * param.D;
+  param.dm = 1e-6 / SI_unit.kilogram;
+  param.E = 210000e6 / SI_unit.pascal;
+  param.nu = 0.3;
+  param.rho = 7850 / (SI_unit.kilogram / SI_unit.meter^3);
+  param.g = 9.81 / (SI_unit.meter / SI_unit.second^2);
+  m1 = param.D^2 * pi / 4 * param.w * param.rho;
+  param.dr = param.ecg * (m1 + param.dm) / param.dm;
+  param.omega0 = 1000 * pi / 30 / (1 / SI_unit.second);
+  param.omega1 = 10000 * pi / 30 / (1 / SI_unit.second);
+  param.n = 10;
+  options.number_of_modes = int32(10);
+  options.scale_def = 10e-3;
+  options.geo_tol = sqrt(eps);
+  options.code.use_package = false;
+  options.f_run_mbdyn = [true, true];
+  options.verbose = false;
+  options.elem_type = "iso20";
+  options.f_rbk = false;
+  options.f_enable_beam = true;
+  options.f_enable_modal = true;
+  options.f_enable_solid = false; ## disabled because of long execution time
+  [omega1, r1] = rotordynamics_test_case(param, options, SI_unit);
+  options.f_rbk = true;
+  options.f_enable_modal = false;
+  [omega2, r2] = rotordynamics_test_case(param, options, SI_unit);
+  for istage=1:2
+    assert_simple(r2{istage,2}, r1{istage,2}, 1e-3 * max(abs(r1{istage,2})));
+  endfor
+  options.elem_type = "iso20";
+  options.f_enable_beam = false; ## disabled because of coarse mesh and linear elements
+  options.f_enable_solid = true;
+  options.f_enable_modal = true;
+  param.h1 = 50e-3 / SI_unit.meter; ## coarse mesh size
+  param.h2 = 200e-3 / SI_unit.meter; ## coarse mesh size
+  [omega3, r3] = rotordynamics_test_case(param, options, SI_unit);
+catch
+  gtest_error = lasterror();
+  gtest_fail(gtest_error, __FILE__);
+  rethrow(gtest_error);
+end_try_catch
-- 
GitLab


From 3228b2f8112fc194716b7234df7e0bc52904d3b4 Mon Sep 17 00:00:00 2001
From: Reinhard Resch <mbdyn-user@a1.net>
Date: Tue, 13 May 2025 19:02:25 +0200
Subject: [PATCH 2/2] Fix missing artifacts for Gmsh

---
 testsuite/mbdyn-tests-public-test-job.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/testsuite/mbdyn-tests-public-test-job.yml b/testsuite/mbdyn-tests-public-test-job.yml
index db9714094..4fbce76b1 100644
--- a/testsuite/mbdyn-tests-public-test-job.yml
+++ b/testsuite/mbdyn-tests-public-test-job.yml
@@ -82,6 +82,8 @@ mbdyn-tests-public-test-job:   # This job runs in the test stage.
       artifacts: true
     - job: mbdyn-build-job
       artifacts: true
+    - job: gmsh-build-job
+      artifacts: true      
     - job: octave-pkg-build-job ## needed for gtest-octave-cli
       artifacts: true
   script:
-- 
GitLab