VPTissue Reference Manual
NodeMover.cpp
Go to the documentation of this file.
1  /*
2  * Copyright 2011-2016 Universiteit Antwerpen
3  *
4  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
5  * the European Commission - subsequent versions of the EUPL (the "Licence");
6  * You may not use this work except in compliance with the Licence.
7  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the Licence is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the Licence for the specific language governing
13  * permissions and limitations under the Licence.
14  */
20 #include "NodeMover.h"
21 
22 #include "bio/Cell.h"
23 #include "bio/CellAttributes.h"
24 #include "bio/Mesh.h"
25 #include "bio/Node.h"
27 #include "math/MeshGeometry.h"
28 #include "math/RandomEngine.h"
30 #include "sim/CoreData.h"
31 #include "util/misc/Exception.h"
32 #include "util/misc/log_debug.h"
33 
34 #include <trng/uniform_dist.hpp>
35 #include <trng/uniform01_dist.hpp>
36 #include <trng/normal_dist.hpp>
37 #include <algorithm>
38 #include <array>
39 #include <set>
40 #include <unordered_set>
41 
42 using namespace std;
43 using namespace boost::property_tree;
44 using namespace SimPT_Sim;
45 using namespace SimPT_Sim::Util;
46 
47 namespace SimPT_Sim{
48 
49 NodeMover::NodeMover()
50  : m_mesh(nullptr), m_tissue_energy(0.0)
51 {
52 }
53 
55 {
56  Initialize(cd);
57 }
58 
59 void NodeMover::CalculateCellEnergies()
60 {
61  m_tissue_energy = 0.0;
62  for (auto c : m_mesh->GetCells()) {
63  const double e = m_hamiltonian(c);
64  m_cell_energies[c] = e;
65  m_tissue_energy += e;
66  }
67  const double e_bp = m_hamiltonian(m_mesh->GetBoundaryPolygon());
68  m_cell_energies[m_mesh->GetBoundaryPolygon()] = e_bp;
69  m_tissue_energy += e_bp;
70 }
71 
72 void NodeMover::CalculateCellEnergiesDelta()
73 {
74  m_tissue_energy = 0.0;
75  for (auto c : m_mesh->GetCells()) {
76  m_tissue_energy += m_hamiltonian(c);
77  }
78  m_tissue_energy += m_hamiltonian(m_mesh->GetBoundaryPolygon());
79 }
80 
82 {
83  assert((cd.Check() == true) && "CoreData not OK." );
84 
85  m_cd = cd;
86  m_cell_energies.clear();
87 
88  const auto model_group = m_cd.m_parameters->get<string>("model.group", "Default");
89  const auto factory = ComponentFactoryProxy::Create(model_group);
90  const bool allow_use_delta = m_cd.m_parameters->get<bool>("cell_mechanics.mc_allow_use_delta", true);
91 
92  if (allow_use_delta) {
93  m_delta_hamiltonian = factory->CreateDeltaHamiltonian(m_cd);
94  assert((m_delta_hamiltonian) && ("DeltaHamiltonian produces nullptr."));
95  }
96  // Even if allow_use_delta == true, we need the hamiltonian to calculate
97  // a baseline energy to determine a convergence rate.
98  m_hamiltonian = factory->CreateHamiltonian(m_cd);
99  assert((m_hamiltonian) && ("Hamiltonian produces nullptr."));
100 
101  m_mesh = m_cd.m_mesh.get();
102  m_tissue_energy = 0.0;
103 
104  // Initialize the various RNGs.
105  for (size_t i = 0; i < 1; ++i) {
106  m_mc_move_generator.push_back(factory->CreateMoveGenerator(m_cd));
107  m_normal_generator.push_back(m_cd.m_random_engine->GetGenerator(trng::normal_dist<double>(0.0, 1.0), i));
108  m_uniform_generator.push_back(m_cd.m_random_engine->GetGenerator(trng::uniform01_dist<double>(), i));
109  }
110 
111  if (UsesDeltaHamiltonian()) {
112  CalculateCellEnergiesDelta();
113  } else {
114  CalculateCellEnergies();
115  }
116 }
117 
118 bool NodeMover::IsNonIntersecting(Node* node, const std::array<double, 3>& move) const
119 {
120  const auto& owners = m_mesh->GetNodeOwningNeighbors(node);
121  bool non_intersects = true;
122  for (const auto& owner : owners) {
123  if ((*owner.GetCell()).MoveSelfIntersects(node, *(node)+move)) {
124  non_intersects = false;
125  break;
126  }
127  }
128  // TODO: possibly check all owning cells that have 3 nodes for flipping
129  return non_intersects;
130 }
131 
132 NodeMover::MetropolisInfo NodeMover::MoveNode(Node* n, double step_size, double temperature)
133 {
134  // Find the node owners (cells) and node owning walls
135  const auto& owners = m_mesh->GetNodeOwningNeighbors(n);
136  const auto& owningWalls = m_mesh->GetNodeOwningWalls(n);
137 
138  // Sum the old (current) energy of node owners (cells)
139  double old_energy = 0.0;
140  double stiff = 0.0;
141  for (const auto& owner : owners) {
142  Cell* const cell = owner.GetCell();
143  old_energy += m_cell_energies[cell];
144  stiff += cell->GetStiffness();
145  }
146 
147  // Temporarily move the node.
148  const array<double,3> move = m_mc_move_generator[0]() * step_size;
149  *n += move;
150  for (const auto& owner : owners) {
151  owner.GetCell()->SetGeoDirty();
152  }
153  for (auto& wall : owningWalls) {
154  wall->SetLengthDirty();
155  }
156 
157  // Recompute energies. We do not check validity of the move (i.e. is it
158  // non-intersecting) at this point. So, the numbers below might be garbage
159  // (in case of intersection the energy caculations produce garbage numbers).
160  // But even if, on the basis of the garbage numbers the energy or mc criteria
161  // accept the move, it will be rejected because of the intersection.
162  double new_energy = 0.0;
163  map<Cell*, double> new_energies_cache;
164  for (auto& owner : owners) {
165  Cell* cell = owner.GetCell();
166  const double e = m_hamiltonian(cell);
167  new_energies_cache[cell] = e;
168  new_energy += e;
169  }
170  const double dh = new_energy - old_energy;
171 
172  // Accept or Reject node movement, provided it is non-intersecting.
173  MetropolisInfo info;
174  const bool dh_accept = (dh < -stiff);
175  const bool mc_accept = m_uniform_generator[0]() < exp((-dh - stiff)/temperature);
176  if ((dh_accept || mc_accept) && IsNonIntersecting(n, move)) {
177  // ACCEPT: adjust energy vector.
178  for (auto& owner : owners) {
179  Cell* cell = owner.GetCell();
180  m_cell_energies[cell] = new_energies_cache[cell];
181  }
182  m_tissue_energy += dh;
183  info = dh_accept ? MetropolisInfo(1U, 0U, dh, 0.0) : MetropolisInfo(0U, 1U, 0.0, dh);
184  } else {
185  // REJECT: move the node back to original position.
186  *n -= move;
187  for (auto& owner : owners) {
188  owner.GetCell()->SetGeoDirty();
189  }
190  for (auto& wall : owningWalls) {
191  wall->SetLengthDirty();
192  }
193  }
194 
195  return info;
196 }
197 
198 NodeMover::MetropolisInfo NodeMover::MoveNodeDelta(Node* n, double step_size, double temperature)
199 {
200  // Find the node owners (cells) and node owning walls.
201  const auto& owners = m_mesh->GetNodeOwningNeighbors(n);
202  const auto& owningWalls = m_mesh->GetNodeOwningWalls(n);
203 
204  // Sum energy differences of node owners (cells) due to (potential) node move.
205  const array<double,3> move = m_mc_move_generator[0]() * step_size;
206  double dh = 0.0;
207  double stiff = 0.0;
208  for (const auto& owner : owners) {
209  dh += m_delta_hamiltonian(owner, n, move);
210  stiff += owner.GetCell()->GetStiffness();
211  }
212 
213  // Acceptance criteria for Metropolis.
214  const bool dh_accept = (dh < -stiff);
215  const bool mc_accept = m_uniform_generator[0]() < exp((-dh - stiff)/temperature);
216 
217  // Accept (and the execute it) or Reject (and then do nothing).
218  MetropolisInfo info;
219  if ((dh_accept || mc_accept) && IsNonIntersecting(n, move)) {
220  *n += move;
221  for (auto& owner : owners) {
222  owner.GetCell()->SetGeoDirty();
223  }
224  for (auto& wall : owningWalls) {
225  wall->SetLengthDirty();
226  }
227  m_tissue_energy += dh;
228  info = dh_accept ? MetropolisInfo(1U, 0U, dh, 0.0) : MetropolisInfo(0U, 1U, 0.0, dh);
229  }
230 
231  return info;
232 }
233 
234 NodeMover::MetropolisInfo NodeMover::SweepOverNodes(double step_size, double temperature)
235 {
236  // -------------------------------------------------------------------------
237  // Select the move method.
238  // -------------------------------------------------------------------------
239  const auto move_method = UsesDeltaHamiltonian() ? &NodeMover::MoveNodeDelta : &NodeMover::MoveNode;
240 
241  // -------------------------------------------------------------------------
242  // Shuffle the indices for the sweep.
243  // -------------------------------------------------------------------------
244  const auto& nodes = m_mesh->GetNodes();
245  vector<unsigned int> shuffled_indices(nodes.size());
246  DurstenfeldShuffle::Fill(shuffled_indices, *m_cd.m_random_engine);
247 
248  // -------------------------------------------------------------------------
249  // Loop of all indices, move, accumulate info.
250  // -------------------------------------------------------------------------
251  MetropolisInfo sweep_info;
252  for (const auto& i : shuffled_indices) {
253  const auto node = nodes[i];
254  if (!node->IsFixed()) {
255  sweep_info += (this->*move_method)(node, step_size, temperature);
256  }
257  }
258 
259  return sweep_info;
260 }
261 
262 } // namespace
Core data with mesh, parameters, random engine and time data.
Definition: CoreData.h:38
STL namespace.
A cell contains walls and nodes.
Definition: Cell.h:48
Node in cell wall.
Definition: Node.h:39
std::list< Wall * > & GetNodeOwningWalls(Node *n)
The walls that contain the given node somewhere on the wall.
Definition: Mesh.h:130
Core data used during model execution.
Namespace for miscellaneous utilities.
Definition: PTreeFile.cpp:44
Interface for MeshGeometry.
Interface for NodeMover.
static std::shared_ptr< ComponentFactoryProxy > Create(const string &group_name, bool throw_ok=true)
Create a factory proxy.
Interface of RandomEngine.
MetropolisInfo SweepOverNodes(double step_size, double temperature)
Sweep over all nodes, do metropolis move for each of them.
Definition: NodeMover.cpp:234
Interface for DurstenfeldShuffle.
const std::vector< Cell * > & GetCells() const
The cells of this mesh, EXCLUDING the boundary polygon.
Definition: Mesh.h:108
Namespace for the core simulator.
Interface for Cell.
Macro defs for debug and logging.
Proxy for dealing with the factories.
Cell * GetBoundaryPolygon() const
Get the boundary polygon of the mesh.
Definition: Mesh.h:105
bool UsesDeltaHamiltonian() const
Using the delta-hamiltonian.
Definition: NodeMover.h:114
NodeMover()
Straight initialization of empty object.
Definition: NodeMover.cpp:49
Interface for Node.
const std::vector< Node * > & GetNodes() const
The nodes of the mesh.
Definition: Mesh.h:136
std::list< NeighborNodes > & GetNodeOwningNeighbors(Node *n)
Get the neighbors associated with this node.
Definition: Mesh.h:124
void Initialize(const CoreData &cd)
Initializes based on values in property tree.
Definition: NodeMover.cpp:81
Interface for CellAttributes.
Header file for Exception class.
Information gathered to evaluat effects of Metropolis Algorithm.
Definition: NodeMover.h:52
bool Check() const
Verify all pointers non-null.
Definition: CoreData.h:53
Interface for Mesh.