dyn_vertex_utils.inl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2006-2017 by
  4. * The Salk Institute for Biological Studies and
  5. * Pittsburgh Supercomputing Center, Carnegie Mellon University
  6. *
  7. * Use of this source code is governed by an MIT-style
  8. * license that can be found in the LICENSE file or at
  9. * https://opensource.org/licenses/MIT.
  10. *
  11. ******************************************************************************/
  12. #ifndef SRC4_DYN_VERTEX_UTILS_INC_
  13. #define SRC4_DYN_VERTEX_UTILS_INC_
  14. #include <iostream>
  15. #include "logging.h"
  16. #include "partition.h"
  17. #include "dyn_vertex_structs.h"
  18. #include "geometry.h"
  19. #include "grid_utils.inl"
  20. #include "geometry_utils.inl"
  21. #include "diffusion_utils.inl"
  22. #include "collision_utils.inl"
  23. #include "wall_utils.inl"
  24. using namespace std;
  25. namespace MCell {
  26. namespace DynVertexUtils {
  27. // based on place_mol_relative_to_mesh
  28. static void move_volume_molecule_to_closest_wall_point(Partition& p, const VolumeMoleculeMoveInfo& molecule_move_info) {
  29. Molecule& vm = p.get_m(molecule_move_info.molecule_id);
  30. assert(vm.is_vol());
  31. // 1) check all walls to get a reference hopefully
  32. // then try to limit only to wall's neighbors - we would really like to avoid any regions (maybe...?)
  33. wall_index_t best_wall_index;
  34. Vec2 best_wall_pos2d;
  35. WallUtils::find_closest_wall(p, vm.v.pos, molecule_move_info.wall_index, false, best_wall_index, best_wall_pos2d);
  36. if (best_wall_index == WALL_INDEX_INVALID) {
  37. mcell_error("Could not find a wall close to volume molecule with id %d.\n", vm.id);
  38. }
  39. Wall& wall = p.get_wall(best_wall_index);
  40. Vec3 new_pos3d = GeometryUtils::uv2xyz(best_wall_pos2d, wall, p.get_wall_vertex(wall, 0));
  41. #ifdef DEBUG_DYNAMIC_GEOMETRY
  42. vm.dump(p, "", "Moving vm towards new wall: ", p.stats.get_current_iteration(), 0);
  43. wall.dump(p, "", true);
  44. #endif
  45. // displacement
  46. pos_t bump = (molecule_move_info.place_above) ? POS_EPS : -POS_EPS;
  47. Vec3 displacement(2 * bump * wall.normal.x, 2 * bump * wall.normal.y, 2 * bump * wall.normal.z);
  48. // move the molecule a bit so that it ends up at the correct side of the wall
  49. vm.v.pos = new_pos3d;
  50. vm.v.subpart_index = p.get_subpart_index(vm.v.pos);
  51. Vec3 new_pos_after_diffuse;
  52. DiffusionUtils::tiny_diffuse_3D(p, vm, displacement, wall.index, new_pos_after_diffuse);
  53. // TODO:
  54. // Make sure we didn't end up on a neighbor's wall, which is kind of easy to
  55. // do with, for example, a shrinking box/cuboid.
  56. // - see place_mol_relative_to_mesh
  57. // move the molecule and also update the information on subpartition reactants
  58. vm.v.pos = new_pos_after_diffuse;
  59. vm.v.subpart_index = p.get_subpart_index(vm.v.pos);
  60. p.update_molecule_reactants_map(vm);
  61. #ifdef DEBUG_DYNAMIC_GEOMETRY
  62. vm.dump(p, "", "Vm after being moved: ", p.stats.get_current_iteration() /*iteration*/, 0);
  63. #endif
  64. }
  65. // insert_surface_molecule & place_surface_molecule in mcell3
  66. // TODO: quite similar code as in WallUtils::place_surface_molecule,
  67. // can be it be somehow merged?
  68. static void move_surface_molecule_to_closest_wall_point(
  69. Partition& p,
  70. const SurfaceMoleculeMoveInfo& molecule_move_info) {
  71. Molecule& sm = p.get_m(molecule_move_info.molecule_id);
  72. assert(sm.is_surf());
  73. #ifdef DEBUG_DYNAMIC_GEOMETRY_MCELL4_ONLY
  74. sm.dump(p, "", "Moving sm towards new wall: ", p.stats.get_current_iteration(), 0);
  75. #endif
  76. // 1) check all walls to get a reference hopefully
  77. // then try to limit only to wall's neighbors - we would really like to avoid any regions (maybe...?)
  78. wall_index_t best_wall_index;
  79. Vec2 best_wall_pos2d;
  80. pos_t best_d2 = WallUtils::find_closest_wall(
  81. p, molecule_move_info.pos3d, molecule_move_info.wall_index, true, best_wall_index, best_wall_pos2d);
  82. if (best_wall_index == WALL_INDEX_INVALID) {
  83. mcell_error("Could not find a wall close to volume molecule with id %d.\n", sm.id);
  84. }
  85. // place molecule onto the found wall, all the remaining information about the molecule stays the same
  86. wall_index_t found_wall_index;
  87. tile_index_t found_tile_index;
  88. Vec2 found_pos2d(FLT_INVALID);
  89. GridUtils::find_closest_tile_on_wall(
  90. p, best_wall_index, best_wall_pos2d, best_d2, p.config.vacancy_search_dist2,
  91. found_wall_index, found_tile_index, found_pos2d
  92. );
  93. assert(found_wall_index != WALL_INDEX_INVALID);
  94. assert(found_tile_index != TILE_INDEX_INVALID);
  95. assert(found_pos2d != Vec2(FLT_INVALID));
  96. sm.s.wall_index = found_wall_index;
  97. sm.s.grid_tile_index = found_tile_index;
  98. sm.s.pos = found_pos2d;
  99. Wall& w = p.get_wall(found_wall_index);
  100. w.grid.set_molecule_tile(found_tile_index, sm.id);
  101. // TODO: reschedule unimolar
  102. #ifdef DEBUG_DYNAMIC_GEOMETRY
  103. sm.dump(p, "", "Sm after being moved: ", p.stats.get_current_iteration() /*iteration*/, 0);
  104. #endif
  105. }
  106. namespace Local {
  107. static void dump_blender_display_code(
  108. const Partition& p,
  109. const Wall& orig_wall, const vertex_index_t* orig_indices,
  110. const Wall& new_wall, const vertex_index_t* new_indices) {
  111. // NOTE: move to some 'dump utils'?
  112. // script mcell/utils/blender_debug_scripts/dyn_vertex_check.py can be used to visualize the
  113. // collision detection
  114. cout << "Constructed object from initial and final triangle:\n";
  115. // dump the object in a form that can be imported to blender
  116. map<vertex_index_t, uint> map_assigned_index;
  117. cout << "mesh_verts = [\n";
  118. for (uint i = 0; i < VERTICES_IN_TRIANGLE; i++) {
  119. cout << " " << p.get_geometry_vertex(orig_indices[i]) << ", #" << i << "\n";
  120. map_assigned_index[orig_indices[i]] = i;
  121. }
  122. for (uint i = 0; i < VERTICES_IN_TRIANGLE; i++) {
  123. cout << " " << p.get_geometry_vertex(new_indices[i]) << ", #" << i + VERTICES_IN_TRIANGLE << "\n";
  124. map_assigned_index[new_indices[i]] = i + VERTICES_IN_TRIANGLE;
  125. }
  126. cout << "]\n";
  127. // dumping only the orig and new wall
  128. cout << "mesh_faces = [\n";
  129. cout << " (";
  130. for (uint k = 0; k < VERTICES_IN_TRIANGLE; k++) {
  131. cout << map_assigned_index[ orig_wall.vertex_indices[k] ] << ", ";
  132. }
  133. cout << "),\n";
  134. cout << " (";
  135. for (uint k = 0; k < VERTICES_IN_TRIANGLE; k++) {
  136. cout << map_assigned_index[new_wall.vertex_indices[k] ] << ", ";
  137. }
  138. cout << "),\n";
  139. // NOTE: how to display the interconnections as well?
  140. /*cout << "mesh_faces = [\n";
  141. for (uint i = 0; i < TRIANGLES_IN_MOVED_TRIANGLE_MESH; i++) {
  142. cout << " (";
  143. for (uint k = 0; k < VERTICES_IN_TRIANGLE; k++) {
  144. cout << map_assigned_index[ moved_triangle_walls[i]->vertex_indices[k] ] << ", ";
  145. }
  146. cout << "),\n";
  147. }
  148. */
  149. cout << "]\n";
  150. cout << "add_mesh(mesh_verts, mesh_faces, \"my_mesh\")\n";
  151. }
  152. } // namespace Local
  153. static void collect_volume_molecules_moved_due_to_moving_wall(
  154. Partition& p,
  155. const wall_index_t moved_wall_index, const WallMoveInfo& move_info,
  156. MoleculeIdsSet& already_moved_molecules,
  157. VolumeMoleculeMoveInfoVector& molecule_moves
  158. ) {
  159. const Wall& orig_wall = p.get_wall(moved_wall_index);
  160. #ifdef DEBUG_DYNAMIC_GEOMETRY_MCELL4_ONLY
  161. cout << "*** Moving vertices of a wall:\n";
  162. orig_wall.dump(p, "", false);
  163. for (const VertexMoveInfo& info: move_info) {
  164. cout << "vertex index: " << info.vertex_index << "\n";
  165. cout << "original position: " << p.get_geometry_vertex(info.vertex_index) << "\n";
  166. cout << "translation: " << info.displacement << "\n";
  167. }
  168. cout << "***\n";
  169. #endif
  170. assert(move_info.vertex_moves.size() > 0 && move_info.vertex_moves.size() <= 3 && "Move infos belong to the wall that is being moved");
  171. // construct a virtual space where we have all the walls with new and old
  172. // positions
  173. const vertex_index_t* orig_indices = orig_wall.vertex_indices;
  174. // create 3 new temporary vertices
  175. Vec3 n[VERTICES_IN_TRIANGLE];
  176. for (uint i = 0; i < VERTICES_IN_TRIANGLE; i++) {
  177. // copy position of the original vertex
  178. Vec3 new_vertex = p.get_geometry_vertex(orig_indices[i]);
  179. // should we move it?
  180. bool moved = false;
  181. for (const VertexMoveInfo* vertex_move: move_info.vertex_moves) {
  182. if (vertex_move->vertex_index == orig_indices[i]) {
  183. assert(!moved);
  184. new_vertex = new_vertex + vertex_move->displacement;
  185. #ifndef NDEBUG
  186. break;
  187. #endif
  188. }
  189. }
  190. n[i] = new_vertex;
  191. }
  192. const Vec3* o[VERTICES_IN_TRIANGLE] = {
  193. &p.get_geometry_vertex(orig_indices[0]),
  194. &p.get_geometry_vertex(orig_indices[1]),
  195. &p.get_geometry_vertex(orig_indices[2])
  196. };
  197. // opposite triangle (wall after being moved)
  198. // first true argument - we need the wall constants to be precomputed,
  199. // second false argument - we do not care about edge information
  200. WallWithVertices new_wall = WallWithVertices(p, n[0], n[1], n[2], true);
  201. #ifdef DEBUG_DYNAMIC_GEOMETRY_COLLISION_DETECTIONS
  202. Local::dump_blender_display_code(p, orig_wall, orig_indices, new_wall, new_indices);
  203. #endif
  204. // if moving by one edge creates just a triangle, store this information
  205. bool egde_moved[EDGES_IN_TRIANGLE];
  206. Wall* wall_if_edge_defines_triangle[EDGES_IN_TRIANGLE];
  207. for (uint i1 = 0; i1 < EDGES_IN_TRIANGLE; i1++) {
  208. uint i2 = (i1 + 1) % EDGES_IN_TRIANGLE;
  209. bool v1_same = *o[i1] == n[i1];
  210. bool v2_same = *o[i2] == n[i2];
  211. if (v1_same && v2_same) {
  212. egde_moved[i1] = false;
  213. wall_if_edge_defines_triangle[i1] = nullptr;
  214. }
  215. else if (v1_same) {
  216. egde_moved[i1] = true;
  217. wall_if_edge_defines_triangle[i1] = new WallWithVertices(p, *o[i1], *o[i2], n[i2], true);
  218. }
  219. else if (v2_same) {
  220. egde_moved[i1] = true;
  221. wall_if_edge_defines_triangle[i1] = new WallWithVertices(p, *o[i1], *o[i2], n[i1], true);
  222. }
  223. else {
  224. egde_moved[i1] = true;
  225. wall_if_edge_defines_triangle[i1] = nullptr;
  226. }
  227. }
  228. // TODO DYN GEOM: optimize only for molecules in relevant subpartitions
  229. std::vector<Molecule>& molecules = p.get_molecules();
  230. for (Molecule& m: molecules) {
  231. if (m.is_defunct()) {
  232. continue;
  233. }
  234. if (m.is_surf()) {
  235. continue;
  236. }
  237. if (already_moved_molecules.count(m.id) == 1) {
  238. //assert(false && "this should not happen anymore?");
  239. continue;
  240. }
  241. #ifdef DEBUG_DYNAMIC_GEOMETRY_COLLISION_DETECTIONS
  242. cout << "mol" << m.id << " = " << m.v.pos << "\n";
  243. cout << "add_point(mol" << m.id << ", \"molecule" << m.id << "\")\n";
  244. #endif
  245. // now check a single projection against all walls created by these two triangles
  246. // if the number of crosses is odd, then we are inside
  247. int num_hits = 0;
  248. // cast ray along the whole partition
  249. Vec3 move(0, 0, p.config.partition_edge_length);
  250. // check collision with the original wall
  251. bool collides = CollisionUtils::collide_wall_test(p, orig_wall, m.v.pos, move);
  252. if (collides) { num_hits++; }
  253. // and with the new wall as well, the wall does not exist in the partition
  254. collides = CollisionUtils::collide_wall_test(p, new_wall, m.v.pos, move);
  255. if (collides) { num_hits++; }
  256. // now, let's deal with the areas that are 'drawn' by the moving edges
  257. // NOTE: many of the values can be pre-computed, but let's keep it simple for now
  258. for (uint i1 = 0; i1 < EDGES_IN_TRIANGLE; i1++) {
  259. uint i2 = (i1 + 1) % EDGES_IN_TRIANGLE;
  260. collides = CollisionUtils::collide_moving_line_and_static_line_test(
  261. p,
  262. m.v.pos, move,
  263. *o[i1], n[i1], *o[i2], n[i2],
  264. egde_moved[i1], wall_if_edge_defines_triangle[i1]
  265. );
  266. if (collides) {
  267. num_hits++;
  268. }
  269. }
  270. if (num_hits % 2 == 1) {
  271. // we are moving with a single wall here.
  272. // different from MCell3 behavior where it tries to find the closest point on the mesh with a given name
  273. // this might be super expensive if the mesh is large
  274. // first we need to figure out on which side of the new wall we should place the molecule
  275. // with regards to its normal
  276. bool place_above = GeometryUtils::is_point_above_plane_defined_by_wall(p, orig_wall, m.v.pos);
  277. molecule_moves.push_back(VolumeMoleculeMoveInfo(m.id, orig_wall.index, place_above));
  278. // and remember that we must not be moving it anymore
  279. // should work even without it...
  280. already_moved_molecules.insert_unique(m.id);
  281. }
  282. }
  283. // free added walls (wall with index 0 existed before)
  284. for (uint i = 1; i < EDGES_IN_TRIANGLE; i++) {
  285. if (wall_if_edge_defines_triangle[i] != nullptr) {
  286. delete wall_if_edge_defines_triangle[i];
  287. }
  288. }
  289. }
  290. void collect_surface_molecules_moved_due_to_moving_wall(
  291. const Partition& p,
  292. const wall_index_t moved_wall_index,
  293. SurfaceMoleculeMoveInfoVector& molecule_moves,
  294. MoleculeIdsVector& paired_molecules) {
  295. // We have no direct set that would say which molecules belong to a given wall,
  296. // attempts were done, but even maintaining a simple set in grid costs ~5% of runtime.
  297. // With the data that we have available, the simples option is to
  298. // go through the array in grid and check all existing molecules.
  299. // There might be other solutions that use subpartitions but let's keep it simple for now.
  300. const Grid* g = p.get_wall_grid_if_exists(moved_wall_index);
  301. if (g == nullptr) {
  302. return;
  303. }
  304. small_vector<molecule_id_t> molecule_ids;
  305. g->get_contained_molecules(molecule_ids);
  306. const Wall& w = p.get_wall(moved_wall_index);
  307. const Vec3& vert0 = p.get_geometry_vertex(w.vertex_indices[0]);
  308. for (molecule_id_t id: molecule_ids) {
  309. const Molecule& sm = p.get_m(id);
  310. assert(sm.is_surf());
  311. assert(!sm.is_defunct());
  312. Vec3 pos3d = GeometryUtils::uv2xyz(sm.s.pos, w, vert0);
  313. molecule_moves.push_back(
  314. SurfaceMoleculeMoveInfo(id, moved_wall_index, pos3d)
  315. );
  316. if (p.get_paired_molecule(id) != MOLECULE_ID_INVALID) {
  317. paired_molecules.push_back(id);
  318. }
  319. }
  320. }
  321. } // namespace DynVertexUtil
  322. } // namespace MCell
  323. #endif // SRC4_DYN_VERTEX_UTILS_INC_