Adding SpringMobilityModel to adjust positions of nodes in large
topologies

Inspired by http://en.wikipedia.org/wiki/Force-based_algorithms_%28graph_drawing%29
diff --git a/utils/spring-mobility-helper.cc b/utils/spring-mobility-helper.cc
new file mode 100644
index 0000000..5e4b4f6
--- /dev/null
+++ b/utils/spring-mobility-helper.cc
@@ -0,0 +1,51 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2006, 2007 INRIA
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include "spring-mobility-helper.h"
+#include "spring-mobility-model.h"
+
+namespace ns3 {
+
+void
+SpringMobilityHelper::InstallSprings (Ptr<Node> node1, Ptr<Node> node2)
+{
+  Ptr<SpringMobilityModel> model1, model2;
+  model1 = node1->GetObject<SpringMobilityModel> ();
+  model2 = node2->GetObject<SpringMobilityModel> ();
+  
+  NS_ASSERT (model1 != 0 && model2 != 0);
+
+  model1->AddSpring (model2);
+  model2->AddSpring (model1);
+}
+
+void
+SpringMobilityHelper::InstallSprings (TopologyReader::ConstLinksIterator first, 
+				      TopologyReader::ConstLinksIterator end)
+{
+  for (; first != end; first++)
+    {
+      InstallSprings (first->GetFromNode (), 
+		      first->GetToNode ());
+    }
+}
+
+} // namespace ns3
+
diff --git a/utils/spring-mobility-helper.h b/utils/spring-mobility-helper.h
new file mode 100644
index 0000000..7285d25
--- /dev/null
+++ b/utils/spring-mobility-helper.h
@@ -0,0 +1,45 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2006, 2007 INRIA
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+#ifndef SPRING_MOBILITY_HELPER_H
+#define SPRING_MOBILITY_HELPER_H
+
+#include "ns3/topology-reader.h"
+
+namespace ns3 {
+
+/**
+ * \ingroup mobility
+ *
+ * \brief 
+ */
+class SpringMobilityHelper
+{
+public:
+  static void
+  InstallSprings (Ptr<Node> node1, Ptr<Node> node2);
+
+  static void
+  InstallSprings (TopologyReader::ConstLinksIterator first, 
+                  TopologyReader::ConstLinksIterator end);
+};
+
+} // namespace ns3
+
+#endif // SPRING_MOBILITY_HELPER_H
diff --git a/utils/spring-mobility-model.cc b/utils/spring-mobility-model.cc
new file mode 100644
index 0000000..5fb968b
--- /dev/null
+++ b/utils/spring-mobility-model.cc
@@ -0,0 +1,188 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2006, 2007 INRIA
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+#include "spring-mobility-model.h"
+#include "ns3/simulator.h"
+#include "ns3/double.h"
+#include "ns3/log.h"
+#include "ns3/node-list.h"
+#include "ns3/node.h"
+
+#include <boost/foreach.hpp>
+
+NS_LOG_COMPONENT_DEFINE ("SpringMobilityModel");
+
+namespace ns3 {
+
+NS_OBJECT_ENSURE_REGISTERED (SpringMobilityModel);
+
+double SpringMobilityModel::m_totalKineticEnergy = 0.0;
+
+const double COLOUMB_K = 200; 
+
+TypeId SpringMobilityModel::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("ns3::SpringMobilityModel")
+    .SetParent<MobilityModel> ()
+    .AddConstructor<SpringMobilityModel> ()
+    .AddAttribute ("Epsilon", "Bound for kinetic energy when system is considered stable",
+                   DoubleValue (1000.0),
+                   MakeDoubleAccessor (&SpringMobilityModel::m_epsilon),
+                   MakeDoubleChecker<double> ())
+    .AddAttribute ("NodeMass", "Node mass",
+                   DoubleValue (10),
+                   MakeDoubleAccessor (&SpringMobilityModel::m_nodeMass),
+                   MakeDoubleChecker<double> ())
+    .AddAttribute ("NodeCharge", "Node charge",
+                   DoubleValue (2),
+                   MakeDoubleAccessor (&SpringMobilityModel::m_nodeCharge),
+                   MakeDoubleChecker<double> ())
+    .AddAttribute ("SpringNormalLength", "Normal length of spring length",
+                   DoubleValue (10),
+                   MakeDoubleAccessor (&SpringMobilityModel::m_springNormalLength),
+                   MakeDoubleChecker<double> ())
+    .AddAttribute ("SpringConstant", "Spring constant",
+                   DoubleValue (0.2),
+                   MakeDoubleAccessor (&SpringMobilityModel::m_springConstant),
+                   MakeDoubleChecker<double> ())
+    .AddAttribute ("DampingFactor", "Dumping factor",
+                   DoubleValue (0.6),
+                   MakeDoubleAccessor (&SpringMobilityModel::m_dampingFactor),
+                   MakeDoubleChecker<double> ())
+    ;
+  
+  return tid;
+}
+
+SpringMobilityModel::SpringMobilityModel ()
+  : m_position (0,0,0)
+  , m_velocity (0,0,0)
+  , m_stable (false)
+{
+}
+
+SpringMobilityModel::~SpringMobilityModel ()
+{
+}
+
+void
+SpringMobilityModel::AddSpring (Ptr<MobilityModel> node)
+{
+  m_springs.push_back (node);
+}
+
+void
+SpringMobilityModel::DoStart ()
+{
+  // m_updateEvent = Simulator::Schedule (Seconds(0.05), &SpringMobilityModel::Update, this);
+
+  // Simulator::Schedule (Seconds(2.0), &SpringMobilityModel::Test, this);
+}
+
+void
+SpringMobilityModel::Update () const
+{
+  // NS_LOG_FUNCTION (this << m_stable << m_position << m_velocity);
+  if (m_stable) return;
+  Time now = Simulator::Now ();
+
+  if (now <= m_lastTime)
+    {
+      m_lastTime = now;
+      return;
+    }
+
+  double time_step_s = (now - m_lastTime).ToDouble (Time::S);
+  m_lastTime = now;
+
+  Vector force (0.0, 0.0, 0.0);
+
+  for (NodeList::Iterator node = NodeList::Begin ();
+       node != NodeList::End ();
+       node++)
+    {
+      if ((*node)->GetId () == GetObject<Node> ()->GetId ()) continue;
+      Ptr<SpringMobilityModel> model = (*node)->GetObject<SpringMobilityModel> ();
+      if (model == 0) continue;
+      if (model == this) continue;
+
+      double distance = GetDistanceFrom (model);
+      if (distance < 0.1) continue;
+
+      Vector direction = (GetPosition () - model->GetPosition ()) / distance; // direction vector of size 1, force trying to take nodes apart
+
+      force += direction * COLOUMB_K * m_nodeCharge * m_nodeCharge / distance / distance;
+    }
+
+  BOOST_FOREACH (Ptr<MobilityModel> model, m_springs)
+    {
+      double distance = GetDistanceFrom (model);
+      Vector direction = (model->GetPosition () - GetPosition ()) / distance; // direction vector of size 1, force trying to take nodes closer, if they are more than distance apart
+
+      force += direction * (- m_springNormalLength + distance) / m_springConstant;
+    }
+
+  // NS_LOG_DEBUG ("force: " << force);
+
+  // subtract previous value of kinetic energy for the node
+  double velocityValue = CalculateDistance (m_velocity, Vector(0,0,0)); 
+  m_totalKineticEnergy -= m_nodeMass * velocityValue * velocityValue;
+
+  // Correct velocity and position
+  m_velocity = (m_velocity + force * time_step_s) * m_dampingFactor;
+  m_position += m_velocity * time_step_s;
+
+  // Add new value for the kinetic energy
+  velocityValue = CalculateDistance (m_velocity, Vector(0,0,0)); 
+  m_totalKineticEnergy += m_nodeMass * velocityValue * velocityValue;
+
+  if (m_totalKineticEnergy < m_epsilon)
+    {
+      m_stable = true;
+      NS_LOG_INFO ("Stabilized with " << m_totalKineticEnergy);
+    }
+
+  NotifyCourseChange ();
+  // m_updateEvent = Simulator::Schedule (Seconds(0.05), &SpringMobilityModel::Update, this);
+}
+
+Vector
+SpringMobilityModel::DoGetPosition (void) const
+{
+  // NS_LOG_FUNCTION (this << m_position);
+  Update ();
+  
+  return m_position;
+}
+void 
+SpringMobilityModel::DoSetPosition (const Vector &position)
+{
+  // NS_LOG_FUNCTION (this << position);
+  m_position = position;
+
+  NotifyCourseChange ();
+  m_stable = false;
+}
+Vector
+SpringMobilityModel::DoGetVelocity (void) const
+{
+  return m_velocity;
+}
+
+} // namespace ns3
diff --git a/utils/spring-mobility-model.h b/utils/spring-mobility-model.h
new file mode 100644
index 0000000..553ab9d
--- /dev/null
+++ b/utils/spring-mobility-model.h
@@ -0,0 +1,94 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2006, 2007 INRIA
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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
+ *
+ * Author: 
+ */
+#ifndef SPRING_MOBILITY_MODEL_H
+#define SPRING_MOBILITY_MODEL_H
+
+#include "ns3/mobility-model.h"
+#include "ns3/nstime.h"
+#include "ns3/event-id.h"
+
+namespace ns3 {
+
+/**
+ * \ingroup mobility
+ *
+ * \brief 
+ */
+class SpringMobilityModel : public MobilityModel 
+{
+public:
+  static TypeId GetTypeId (void);
+
+  /**
+   * Create a position located at coordinates (0,0,0) with velocity (0,0,0)
+   */
+  SpringMobilityModel ();
+  virtual ~SpringMobilityModel ();
+
+  /**
+   * \brief Attach node by a spring
+   * \param node MobilityModel layer of the attaching node
+   */
+  void
+  AddSpring (Ptr<MobilityModel> node);
+
+private:
+  // from Object
+  virtual void
+  DoStart ();
+
+  // from MobilityModel
+  virtual Vector 
+  DoGetPosition (void) const;
+
+  virtual void 
+  DoSetPosition (const Vector &position);
+
+  virtual Vector 
+  DoGetVelocity (void) const;
+
+  // Updating positions
+  void 
+  Update (void) const;
+
+private:
+  double m_epsilon;
+
+  double m_nodeMass;
+  double m_nodeCharge;
+  double m_springNormalLength;
+  double m_springConstant;
+  double m_dampingFactor;
+
+  static double m_totalKineticEnergy;  
+  
+  mutable Vector m_position;
+  mutable Vector m_velocity;
+  mutable bool m_stable;
+  mutable Time m_lastTime;
+
+  EventId m_updateEvent;
+
+  std::list<Ptr<MobilityModel> > m_springs;
+};
+
+} // namespace ns3
+
+#endif // SPRING_MOBILITY_MODEL_H