partition.cpp 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2019-2021 by
  4. * The Salk Institute for Biological Studies
  5. *
  6. * Use of this source code is governed by an MIT-style
  7. * license that can be found in the LICENSE file or at
  8. * https://opensource.org/licenses/MIT.
  9. *
  10. ******************************************************************************/
  11. #include <iostream>
  12. #include <algorithm>
  13. #include "logging.h"
  14. #include "partition.h"
  15. #include "datamodel_defines.h"
  16. #include "wall_utils.h"
  17. #include "dyn_vertex_utils.inl"
  18. #include "collision_utils.inl"
  19. #include "wall_utils.inl"
  20. using namespace std;
  21. namespace MCell {
  22. static void zero_out_lowest_bits_pos(pos_t* val) {
  23. uint tmp;
  24. memcpy(&tmp, val, sizeof(uint));
  25. tmp &= 0xFFFFF000;
  26. memcpy(val, &tmp, sizeof(uint));
  27. }
  28. Partition::Partition(
  29. const partition_id_t id_,
  30. const Vec3& origin_corner_,
  31. const SimulationConfig& config_,
  32. BNG::BNGEngine& bng_engine_,
  33. SimulationStats& stats_
  34. )
  35. : origin_corner(origin_corner_),
  36. next_molecule_id(0),
  37. volume_molecule_reactants_per_reactant_class(config_.num_subparts),
  38. id(id_),
  39. config(config_),
  40. bng_engine(bng_engine_),
  41. stats(stats_) {
  42. #if POS_T_BYTES == 4
  43. zero_out_lowest_bits_pos(&origin_corner.x);
  44. zero_out_lowest_bits_pos(&origin_corner.y);
  45. zero_out_lowest_bits_pos(&origin_corner.z);
  46. #endif
  47. opposite_corner = origin_corner + config.partition_edge_length;
  48. // check that the subpart grid goes through (0, 0, 0),
  49. // (this point does not have to be contained in this partition)
  50. // required for correct function of raycast_with_endpoints,
  51. // round is required because values might be negative
  52. Vec3 how_many_subparts_from_000 = origin_corner/Vec3(config.subpart_edge_length);
  53. release_assert(cmp_eq(round3(how_many_subparts_from_000), how_many_subparts_from_000, POS_SQRT_EPS) &&
  54. "Partition is not aligned to the subpartition grid."
  55. );
  56. // pre-allocate volume_molecules arrays and also volume_molecule_indices_per_time_step
  57. walls_per_subpart.resize(config.num_subparts);
  58. // create an empty counted volume
  59. CountedVolume counted_volume_outside_all;
  60. counted_volume_index_t index = find_or_add_counted_volume(counted_volume_outside_all);
  61. assert(index == COUNTED_VOLUME_INDEX_OUTSIDE_ALL && "The empty counted volume must have index 0");
  62. rng_init(&aux_rng, config.initial_seed);
  63. }
  64. Partition::~Partition() {
  65. // these data can be shared among multiple walls therefore they are
  66. // owned by Partition
  67. for (WallSharedData* ptr: wall_shared_data) {
  68. delete ptr;
  69. }
  70. }
  71. // when a wall is added with add_uninitialized_wall,
  72. // its type and vertices are not know yet, we must include the walls
  73. // into subvolumes and also for other purposes
  74. void Partition::finalize_walls() {
  75. for (Wall& w: walls) {
  76. wall_index_t wall_index = w.index;
  77. for (vertex_index_t vi: w.vertex_indices) {
  78. add_wall_using_vertex_mapping(vi, wall_index);
  79. }
  80. w.present_in_subparts.clear();
  81. // also insert this triangle into walls per subpartition
  82. SubpartIndicesVector colliding_subparts;
  83. GeometryUtils::wall_subparts_collision_test(*this, w, colliding_subparts);
  84. for (subpart_index_t subpart_index: colliding_subparts) {
  85. assert(subpart_index < walls_per_subpart.size());
  86. // mapping subpart->wall
  87. walls_per_subpart[subpart_index].insert(wall_index);
  88. // mapping wall->subpart
  89. w.present_in_subparts.insert(subpart_index); // TODO: use insert_unique
  90. }
  91. // make a cache-optimized copy of certain fields from Wall
  92. assert(wall_collision_rejection_data.size() == wall_index);
  93. wall_collision_rejection_data.push_back(w);
  94. }
  95. }
  96. // remove items when 'insert' is false
  97. void Partition::update_walls_per_subpart(const WallsWithTheirMovesMap& walls_with_their_moves, const bool insert) {
  98. for (auto it: walls_with_their_moves) {
  99. wall_index_t wall_index = it.first;
  100. SubpartIndicesVector colliding_subparts;
  101. Wall& w = get_wall(wall_index);
  102. GeometryUtils::wall_subparts_collision_test(*this, w, colliding_subparts);
  103. assert(insert || SubpartIndicesSet(colliding_subparts.begin(), colliding_subparts.end()) == w.present_in_subparts);
  104. for (subpart_index_t subpart_index: colliding_subparts) {
  105. assert(subpart_index < walls_per_subpart.size());
  106. if (insert) {
  107. walls_per_subpart[subpart_index].insert_unique(wall_index);
  108. w.present_in_subparts.insert(subpart_index);
  109. }
  110. else {
  111. walls_per_subpart[subpart_index].erase_existing(wall_index);
  112. w.present_in_subparts.erase(subpart_index);
  113. }
  114. }
  115. }
  116. }
  117. template<typename T>
  118. static void randomize_vector(rng_state& rng, std::vector<T>& vec) {
  119. uint n = vec.size();
  120. for (uint i = n-1; i > 0; --i) {
  121. double rand = rng_dbl(&rng);
  122. release_assert(rand >= 0 && rand <= 1);
  123. // scale rand to 0..n-1
  124. uint rand_index = (double)(n-1) * rand;
  125. assert(rand_index < n);
  126. T tmp = vec[i];
  127. vec[i] = vec[rand_index];
  128. vec[rand_index] = tmp;
  129. }
  130. }
  131. void Partition::apply_vertex_moves(
  132. const bool randomize_order,
  133. std::vector<VertexMoveInfo>& ordered_vertex_moves,
  134. std::set<GeometryObjectWallUnorderedPair>& colliding_walls,
  135. std::vector<VertexMoveInfo*>& vertex_moves_due_to_paired_molecules) {
  136. colliding_walls.clear();
  137. std::vector<VertexMoveInfo*> vertex_moves;
  138. for (VertexMoveInfo& vertex_move_info: ordered_vertex_moves) {
  139. vertex_moves.push_back(&vertex_move_info);
  140. }
  141. if (randomize_order) {
  142. randomize_vector(aux_rng, vertex_moves);
  143. }
  144. // due to wall-wall collision detection, we must move vertices of each object separately
  145. // is there a single object that we are moving?
  146. geometry_object_id_t object_id = GEOMETRY_OBJECT_ID_INVALID;
  147. bool single_object = true;
  148. for (const VertexMoveInfo* vertex_move_info: vertex_moves) {
  149. if (object_id == GEOMETRY_OBJECT_ID_INVALID || object_id == vertex_move_info->geometry_object_id) {
  150. object_id = vertex_move_info->geometry_object_id;
  151. }
  152. else {
  153. single_object = false;
  154. break;
  155. }
  156. }
  157. if (single_object) {
  158. apply_vertex_moves_per_object(
  159. true, vertex_moves,
  160. colliding_walls, vertex_moves_due_to_paired_molecules);
  161. }
  162. else {
  163. // we must make a separate vector for each
  164. map<geometry_object_id_t, vector<VertexMoveInfo*>> vertex_moves_per_object;
  165. for (VertexMoveInfo* vertex_move_info: vertex_moves) {
  166. vertex_moves_per_object[vertex_move_info->geometry_object_id].push_back(vertex_move_info);
  167. }
  168. // randomize ordering of objects if requested
  169. vector<geometry_object_id_t> application_order;
  170. for (auto& pair_id_moves: vertex_moves_per_object) {
  171. application_order.push_back(pair_id_moves.first);
  172. }
  173. if (randomize_order) {
  174. randomize_vector(aux_rng, application_order);
  175. }
  176. // and process objects one by one
  177. for (geometry_object_id_t object_id: application_order) {
  178. apply_vertex_moves_per_object(
  179. true, vertex_moves_per_object[object_id],
  180. colliding_walls, vertex_moves_due_to_paired_molecules);
  181. }
  182. }
  183. }
  184. void Partition::apply_vertex_moves_per_object(
  185. const bool move_paired_walls,
  186. std::vector<VertexMoveInfo*>& vertex_moves,
  187. std::set<GeometryObjectWallUnorderedPair>& colliding_walls,
  188. std::vector<VertexMoveInfo*>& vertex_moves_due_to_paired_molecules) {
  189. // 0) clamp maximum movement and collect walls that collide
  190. clamp_vertex_moves_to_wall_wall_collisions(vertex_moves, colliding_walls);
  191. // 1) create a set of all affected walls with information on how much each wall moves,
  192. uint_set<vertex_index_t> moved_vertices_set;
  193. WallsWithTheirMovesMap walls_with_their_moves;
  194. for (VertexMoveInfo* vertex_move_info: vertex_moves) {
  195. // expecting that there we are not moving a single vertex twice
  196. if (moved_vertices_set.count(vertex_move_info->vertex_index) != 0) {
  197. mcell_error(
  198. "When moving dynamic vertices, each vertex may be listed just once, error for vertex with index %d.",
  199. vertex_move_info->vertex_index
  200. );
  201. }
  202. moved_vertices_set.insert(vertex_move_info->vertex_index);
  203. const std::vector<wall_index_t>& wall_indices = get_walls_using_vertex(vertex_move_info->vertex_index);
  204. // first check whether we can move all walls belonging to the vertex
  205. for (wall_index_t wall_index: wall_indices) {
  206. if (!get_wall(wall_index).is_movable) {
  207. // we must not move this vertex
  208. vertex_move_info->vertex_walls_are_movable = false;
  209. break;
  210. }
  211. }
  212. // if the move is applicable, create mapping in walls_with_their_moves
  213. if (vertex_move_info->vertex_walls_are_movable) {
  214. for (wall_index_t wall_index: wall_indices) {
  215. // remember mapping wall_index -> moves
  216. auto it = walls_with_their_moves.find(wall_index);
  217. if (it == walls_with_their_moves.end()) {
  218. it = walls_with_their_moves.insert(make_pair(wall_index, WallMoveInfo())).first;
  219. }
  220. it->second.vertex_moves.push_back(vertex_move_info);
  221. }
  222. }
  223. }
  224. // set whether walls change area
  225. for (auto& it: walls_with_their_moves) {
  226. // TODO: implement check to optimize performance
  227. it.second.wall_changes_area = true;
  228. }
  229. // 2) for each wall, detect what molecules will be moved and move them right away
  230. // In some cases, moving one wall might place a molecule into a path of another moved wall,
  231. // however, they should be moved at the same time, so we use just the first move and skip any other further moves.
  232. // Not completely sure about this, but it seems that the same behavior should be achieved when
  233. // we would first collect all moves and do them later, however we are creating temporary walls,
  234. // so remembering them would be more complicated.
  235. VolumeMoleculeMoveInfoVector volume_molecule_moves;
  236. MoleculeIdsSet already_moved_volume_molecules;
  237. SurfaceMoleculeMoveInfoVector surface_molecule_moves;
  238. #ifdef DEBUG_DYNAMIC_GEOMETRY_MCELL4_ONLY
  239. cout << "*** Walls being moved:\n";
  240. for (const auto& it: walls_with_their_moves) {
  241. const Wall& w = get_wall(it.first);
  242. w.dump(*this, " ", true);
  243. }
  244. #endif
  245. MoleculeIdsVector paired_molecules;
  246. for (const auto& it: walls_with_their_moves) {
  247. DynVertexUtils::collect_volume_molecules_moved_due_to_moving_wall(
  248. *this, it.first, it.second, already_moved_volume_molecules, volume_molecule_moves);
  249. // required also to find paired molecules
  250. DynVertexUtils::collect_surface_molecules_moved_due_to_moving_wall(
  251. *this, it.first, surface_molecule_moves, paired_molecules);
  252. }
  253. // 3) get information on where these walls are and remove them
  254. update_walls_per_subpart(walls_with_their_moves, false);
  255. // 4) then we move the vertices and update relevant walls
  256. Geometry::update_moved_walls(*this, vertex_moves, walls_with_their_moves);
  257. // 5) update subpartition info for the walls
  258. update_walls_per_subpart(walls_with_their_moves, true);
  259. // 6) move volume molecules
  260. for (const VolumeMoleculeMoveInfo& move_info: volume_molecule_moves) {
  261. DynVertexUtils::move_volume_molecule_to_closest_wall_point(*this, move_info);
  262. }
  263. // 7) move surface molecules
  264. // 7.1) clear grids of affected walls
  265. for (const auto& it: walls_with_their_moves) {
  266. if (it.second.wall_changes_area) {
  267. Wall& w = get_wall(it.first);
  268. if (w.grid.is_initialized()) {
  269. w.grid.reset_all_tiles();
  270. }
  271. }
  272. }
  273. // 7.2) do the actual movement
  274. // mcell3 compatibility - sort surface_molecule_moves by id in order to
  275. // use the same grid locations as in mcell3, not really needed for correctness
  276. sort( surface_molecule_moves.begin(), surface_molecule_moves.end(),
  277. [ ]( const SurfaceMoleculeMoveInfo& lhs, const SurfaceMoleculeMoveInfo& rhs )
  278. {
  279. return lhs.molecule_id < rhs.molecule_id;
  280. }
  281. );
  282. for (const SurfaceMoleculeMoveInfo& move_info: surface_molecule_moves) {
  283. // finally fix positions of the surface molecules (only those that are on walls that change area)
  284. DynVertexUtils::move_surface_molecule_to_closest_wall_point(*this, move_info);
  285. }
  286. // 8) move walls that contain paired molecules
  287. if (move_paired_walls && !paired_molecules.empty()) {
  288. // calls recursively Partition::apply_vertex_moves_per_object(false, ...)
  289. // there should be no colliding walls (not completely sure?)
  290. move_walls_with_paired_molecules(
  291. paired_molecules, walls_with_their_moves,
  292. vertex_moves_due_to_paired_molecules);
  293. }
  294. }
  295. static Vec3 average_vec3(const std::vector<const Vec3*>& moves) {
  296. Vec3 res = 0;
  297. for (const Vec3* vec: moves) {
  298. res = res + *vec;
  299. }
  300. res = res / Vec3(moves.size());
  301. return res;
  302. }
  303. // called from apply_vertex_moves_per_object
  304. void Partition::move_walls_with_paired_molecules(
  305. const MoleculeIdsVector& paired_molecules,
  306. const WallsWithTheirMovesMap& walls_with_their_moves,
  307. std::vector<VertexMoveInfo*>& vertex_moves_due_to_paired_molecules) {
  308. // notation:
  309. // object A:
  310. // - the object whose walls were moved in the call 'apply_vertex_moves_per_object'
  311. // that initiated call of this method
  312. // molecules on A (paired_molecules argument of this method):
  313. // - molecules present on object A
  314. //
  315. // object B:
  316. // - the object whose walls we are going to move now
  317. // there cannot be currently multiple objects moved at the same time due to
  318. // potentially missing some moves if molecules on A would be paired with molecules on objects B and C
  319. // molecules on B:
  320. // - molecules present on object B that are paired with molecules on A
  321. // we are going to move walls of object B that contain molecules on B
  322. // collect which vertices of B will be moved along with molecules on A that initiate their move
  323. map<vertex_index_t, vector<const Vec3*>> vertices_from_B_with_moves;
  324. geometry_object_id_t oid_B = GEOMETRY_OBJECT_ID_INVALID;
  325. bool warning_printed = false;
  326. // assuming that there are usually just a few molecules per wall so we process
  327. // paired molecule from A one by one (just a performance assumption)
  328. for (molecule_id_t mid_A: paired_molecules) {
  329. const Molecule& m_A = get_m(mid_A);
  330. assert(m_A.is_surf());
  331. wall_index_t wi_A = m_A.s.wall_index;
  332. molecule_id_t mid_B = get_paired_molecule(mid_A);
  333. const Molecule& m_B = get_m(mid_B);
  334. assert(m_B.is_surf());
  335. wall_index_t wi_B = m_B.s.wall_index;
  336. const Wall& w_B = get_wall(wi_B);
  337. // check that we are moving with a single object
  338. if (oid_B == GEOMETRY_OBJECT_ID_INVALID) {
  339. oid_B = w_B.object_id;
  340. }
  341. else if (oid_B != w_B.object_id && !warning_printed) {
  342. warns() << "Paired molecules from a single geometry object move with more than one "
  343. "other object. This might result is some missed wall moves.\n";
  344. warning_printed = true;
  345. }
  346. // find all moves for wall from A
  347. auto it_wall_moves_A = walls_with_their_moves.find(wi_A);
  348. if (it_wall_moves_A == walls_with_their_moves.end()) {
  349. // no moves for this wall? should not normally happen
  350. // but otherwise this means that the wall is not moved
  351. assert(false);
  352. continue;
  353. }
  354. // for each vertex of w_B, collect all moves
  355. for (uint i = 0; i < VERTICES_IN_TRIANGLE; i++) {
  356. vertex_index_t vi_B = w_B.vertex_indices[i];
  357. vector<const Vec3*>& displacements_B = vertices_from_B_with_moves[vi_B];
  358. for (const VertexMoveInfo* move_B: it_wall_moves_A->second.vertex_moves) {
  359. displacements_B.push_back(&move_B->displacement);
  360. }
  361. }
  362. }
  363. // prepare argument vector<VertexMoveInfo*>& vertex_moves
  364. // by computing average displacement for each vertex of wall that contains a paired molecule
  365. vector<VertexMoveInfo*> vertex_move_infos_B;
  366. for (const auto& pair_vid_moves_B: vertices_from_B_with_moves) {
  367. Vec3 avg_displacement = average_vec3(pair_vid_moves_B.second);
  368. if (cmp_eq(avg_displacement, 0)) {
  369. // no need to move vertex if it is not being moved at all
  370. continue;
  371. }
  372. VertexMoveInfo* move_B = new VertexMoveInfo(
  373. PARTITION_ID_INITIAL, oid_B, pair_vid_moves_B.first, avg_displacement
  374. );
  375. vertex_move_infos_B.push_back(move_B);
  376. }
  377. // call apply_vertex_moves_per_object to move walls in B
  378. // the call must not cause move of paired walls (otherwise we would get infinite recursion)
  379. // there must not be any new wall collisions (warning if they are)
  380. std::set<GeometryObjectWallUnorderedPair> colliding_walls;
  381. vector<VertexMoveInfo*> ignored_moves;
  382. apply_vertex_moves_per_object(
  383. false, vertex_move_infos_B,
  384. colliding_walls, ignored_moves);
  385. assert(ignored_moves.empty());
  386. if (!colliding_walls.empty()) {
  387. warns() << "Unexpected wall collisions detected in move_walls_with_paired_molecules.\n";
  388. }
  389. // remember these vertex_moves so that the Python API can
  390. vertex_moves_due_to_paired_molecules.insert(
  391. vertex_moves_due_to_paired_molecules.end(),
  392. vertex_move_infos_B.begin(),
  393. vertex_move_infos_B.end()
  394. );
  395. }
  396. static Vec3 get_new_vertex_position(
  397. const Vec3& pos, const vertex_index_t vi, const map<vertex_index_t, const Vec3*>& displacements) {
  398. auto it = displacements.find(vi);
  399. if (it == displacements.end()) {
  400. // no displacement for this vertex
  401. return pos;
  402. }
  403. else {
  404. return pos + *(it->second);
  405. }
  406. }
  407. void Partition::clamp_vertex_moves_to_wall_wall_collisions(
  408. std::vector<VertexMoveInfo*>& vertex_moves,
  409. std::set<GeometryObjectWallUnorderedPair>& colliding_walls) {
  410. // this is a first test that checks whether our wall wont simply collide
  411. // if we send a ray from each of the moved vertices
  412. for (VertexMoveInfo* vertex_move_info: vertex_moves) {
  413. assert(vertex_move_info->partition_id == id);
  414. const std::vector<wall_index_t>& wall_indices = get_walls_using_vertex(vertex_move_info->vertex_index);
  415. Vec3& displacement = vertex_move_info->displacement;
  416. // check that object id is consistent
  417. assert(!wall_indices.empty());
  418. assert(get_wall(wall_indices[0]).object_id == vertex_move_info->geometry_object_id);
  419. const Vec3& pos = get_geometry_vertex(vertex_move_info->vertex_index);
  420. map<geometry_object_index_t, uint> num_crossed_walls_per_object_ignored;
  421. bool must_redo_test;
  422. wall_index_t closest_hit_wall_index;
  423. Vec3 dir(
  424. (cmp_eq(displacement.x, (pos_t)0) ? 0 : ((displacement.x > 0) ? 1 : -1)),
  425. (cmp_eq(displacement.y, (pos_t)0) ? 0 : ((displacement.y > 0) ? 1 : -1)),
  426. (cmp_eq(displacement.z, (pos_t)0) ? 0 : ((displacement.z > 0) ? 1 : -1))
  427. );
  428. // extra displacement to make sure that we will end up in front of a wall,
  429. // not on it
  430. Vec3 wall_gap_displacement = dir * Vec3(MIN_WALL_GAP);
  431. // TODO: preferably use some function that does not collect what we hit,
  432. // but we do not have such
  433. stime_t collision_time = CollisionUtils::get_num_crossed_walls_per_object(
  434. *this, pos, pos + displacement + wall_gap_displacement, false,
  435. num_crossed_walls_per_object_ignored, must_redo_test,
  436. vertex_move_info->geometry_object_id,
  437. &closest_hit_wall_index
  438. );
  439. bool ignore_hit = false;
  440. if (must_redo_test) {
  441. mcell_log(
  442. "Internal warning: wall collision redo in clamp_vertex_moves_to_wall_wall_collisions is not handled yet, "
  443. "the vertex move that caused it won't be applied."
  444. );
  445. ignore_hit = true;
  446. collision_time = 0;
  447. displacement = 0;
  448. }
  449. assert(collision_time >= 0.0 && collision_time <= 1.0);
  450. if (collision_time != 1.0) {
  451. if (collision_time < STIME_SQRT_EPS) {
  452. collision_time = 0.0;
  453. }
  454. else {
  455. collision_time -= STIME_SQRT_EPS;
  456. }
  457. // clamp the displacement (it is a reference)
  458. displacement = displacement * Vec3(collision_time);
  459. // decrement by MIN_WALL_GAP because we hit a wall and would like to stay a bit in front of it
  460. displacement = displacement - wall_gap_displacement;
  461. if (!ignore_hit) {
  462. assert(closest_hit_wall_index != WALL_INDEX_INVALID);
  463. // and remember collisions for each of the walls that are moved by this single vertex
  464. const Wall& hit_wall = get_wall(closest_hit_wall_index);
  465. for (wall_index_t wi: wall_indices) {
  466. colliding_walls.insert(
  467. GeometryObjectWallUnorderedPair(
  468. vertex_move_info->geometry_object_id,
  469. wi,
  470. hit_wall.object_id,
  471. hit_wall.index
  472. )
  473. );
  474. }
  475. }
  476. }
  477. }
  478. // construct a map that gives us displacement per vertex
  479. map<vertex_index_t, const Vec3*> displacement_per_moved_vertex;
  480. for (VertexMoveInfo* vertex_move_info: vertex_moves) {
  481. displacement_per_moved_vertex[vertex_move_info->vertex_index] = &vertex_move_info->displacement;
  482. }
  483. // next, we must check all edges used by this vertex - they must not cross a different object
  484. for (VertexMoveInfo* vertex_move_info: vertex_moves) {
  485. GeometryObject& obj = get_geometry_object_by_id(vertex_move_info->geometry_object_id);
  486. // get all vertices that share edge with the moved vertex
  487. const set<vertex_index_t>& connected_vertices = obj.get_connected_vertices(*this, vertex_move_info->vertex_index);
  488. Vec3 start = get_new_vertex_position(
  489. get_geometry_vertex(vertex_move_info->vertex_index),
  490. vertex_move_info->vertex_index,
  491. displacement_per_moved_vertex);
  492. for (vertex_index_t connected_vertex_index: connected_vertices) {
  493. // check if a different object is not crossed
  494. // must ignore current object because of "wall redo" checks
  495. Vec3 end = get_new_vertex_position(
  496. get_geometry_vertex(connected_vertex_index),
  497. connected_vertex_index,
  498. displacement_per_moved_vertex);
  499. map<geometry_object_index_t, uint> num_crossed_walls_per_object;
  500. bool must_redo_test;
  501. wall_index_t closest_hit_wall_index;
  502. stime_t collision_time = CollisionUtils::get_num_crossed_walls_per_object(
  503. *this, start, end, false,
  504. num_crossed_walls_per_object, must_redo_test,
  505. vertex_move_info->geometry_object_id,
  506. &closest_hit_wall_index
  507. );
  508. if (!num_crossed_walls_per_object.empty() || must_redo_test) {
  509. // we hit a wall, this means that the moved triangle is partially inside another object,
  510. // cancel the displacement
  511. vertex_move_info->displacement = 0;
  512. if (!must_redo_test) {
  513. // remember that there was a wall collision, redos are infrequent and can be ignored
  514. // which wall of the moved object collided?
  515. wall_index_t wi = obj.get_wall_for_vertex_pair(
  516. *this, vertex_move_info->vertex_index, connected_vertex_index);
  517. assert(wi != WALL_INDEX_INVALID);
  518. // choose the first colliding wall from the hit object
  519. const Wall& hit_wall = get_wall(closest_hit_wall_index);
  520. colliding_walls.insert(
  521. GeometryObjectWallUnorderedPair(
  522. vertex_move_info->geometry_object_id,
  523. wi,
  524. hit_wall.object_id,
  525. hit_wall.index
  526. )
  527. );
  528. }
  529. break;
  530. }
  531. }
  532. }
  533. }
  534. void Partition::dump(const bool with_geometry) {
  535. if (with_geometry) {
  536. GeometryObject::dump_array(*this, geometry_objects);
  537. }
  538. Region::dump_array(regions);
  539. #ifndef NDEBUG
  540. if (with_geometry) {
  541. for (size_t i = 0; i < walls_per_subpart.size(); i++) {
  542. if (!walls_per_subpart[i].empty()) {
  543. Vec3 llf, urb;
  544. get_subpart_llf_point(i, llf);
  545. urb = llf + Vec3(config.subpart_edge_length);
  546. cout << "subpart: " << i << ", llf: " << llf << ", urb: " << urb << "\n ";
  547. for (wall_index_t wi: walls_per_subpart[i]) {
  548. cout << wi << ", ";
  549. }
  550. cout << "\n";
  551. }
  552. }
  553. }
  554. #endif
  555. }
  556. // methods get_num_crossed_region_walls and get_num_crossed_walls_per_object
  557. // may fail with WALL_REDO, this means that a point is on a wall and
  558. // therefore it cannot be safely determined where it belongs
  559. // waypoints are used as an optimization so we can place them anywhere we like
  560. void Partition::move_waypoint_because_positioned_on_wall(
  561. const IVec3& waypoint_index, const bool reinitialize) {
  562. Waypoint& waypoint = get_waypoint(waypoint_index);
  563. // rng_dbl when used in Vec3 ctor causes compiler to print warnings
  564. // that a constant is subtracted from uint
  565. double r1 = rng_dbl(&aux_rng);
  566. double r2 = rng_dbl(&aux_rng);
  567. double r3 = rng_dbl(&aux_rng);
  568. Vec3 random_displacement = Vec3(POS_SQRT_EPS * r1, POS_SQRT_EPS * r2, POS_SQRT_EPS * r3);
  569. waypoint.pos = waypoint.pos + random_displacement;
  570. if (reinitialize) {
  571. initialize_waypoint(waypoint_index, false, IVec3(0), true);
  572. }
  573. }
  574. // keep_pos is false by default, when true, the waypoint position is reused and
  575. // this function only updates the counted_volume_index for the new position
  576. void Partition::initialize_waypoint(
  577. const IVec3& waypoint_index,
  578. const bool use_previous_waypoint,
  579. const IVec3& previous_waypoint_index,
  580. const bool keep_pos
  581. ) {
  582. // array was allocated
  583. Waypoint& waypoint = get_waypoint(waypoint_index);
  584. if (!keep_pos) {
  585. waypoint.pos =
  586. origin_corner +
  587. Vec3(config.subpart_edge_length) * Vec3(waypoint_index) + // llf of a subpart
  588. Vec3(config.subpart_edge_length / 2)
  589. ;
  590. }
  591. // make sure that the waypoint is not on any wall, this is required for correct function
  592. // of regions as well
  593. pos_t dist2;
  594. do {
  595. wall_index_t wall_index_ignored;
  596. Vec2 wall_pos2d_ignored;
  597. dist2 = WallUtils::find_closest_wall_any_object(
  598. *this, waypoint.pos, POS_SQRT_EPS, false, wall_index_ignored, wall_pos2d_ignored);
  599. if (dist2 < POS_EPS) {
  600. move_waypoint_because_positioned_on_wall(waypoint_index, false);
  601. }
  602. } while (dist2 < POS_EPS);
  603. // another required check is that there must not be an edge between the current and
  604. // previous waypoint, this is required for initialization of regions
  605. if (use_previous_waypoint) {
  606. bool redo;
  607. int num_attempts = 0;
  608. Vec3& previous_waypoint_pos = get_waypoint(previous_waypoint_index).pos;
  609. do {
  610. map<geometry_object_index_t, uint> num_crossed_walls_per_object;
  611. CollisionUtils::get_num_crossed_walls_per_object(
  612. *this, waypoint.pos, previous_waypoint_pos, false, // all walls
  613. num_crossed_walls_per_object, redo
  614. );
  615. if (redo) {
  616. move_waypoint_because_positioned_on_wall(waypoint_index, false);
  617. if (num_attempts >= 5) {
  618. // give up because we are getting redos due to the previous waypoint,
  619. // count from scratch
  620. break;
  621. }
  622. num_attempts++;
  623. }
  624. } while (redo);
  625. }
  626. // it makes sense to compute counted_volume_index if there are any
  627. // counted objects
  628. if (config.has_intersecting_counted_objects) {
  629. // see if we can reuse the previous waypoint to accelerate computation
  630. if (use_previous_waypoint) {
  631. Waypoint& previous_waypoint = get_waypoint(previous_waypoint_index);
  632. map<geometry_object_index_t, uint> num_crossed_walls_per_object;
  633. bool must_redo_test = false;
  634. CollisionUtils::get_num_crossed_walls_per_object(
  635. *this, waypoint.pos, previous_waypoint.pos, true, // only counted objects
  636. num_crossed_walls_per_object, must_redo_test
  637. );
  638. if (must_redo_test) {
  639. // updates values referenced by waypoint
  640. // the redo can be also due to the previous waypoint, so we
  641. // will check whether the waypoint is contained without the previous waypoint
  642. // do not call reinitialize to avoid recursion
  643. move_waypoint_because_positioned_on_wall(waypoint_index, false);
  644. }
  645. // ok, test passed safely
  646. else if (num_crossed_walls_per_object.empty()) {
  647. // ok, we can simply copy counted volume from the previous waypoint
  648. waypoint.counted_volume_index = previous_waypoint.counted_volume_index;
  649. return;
  650. }
  651. }
  652. // figure out in which counted volumes is this waypoint present
  653. waypoint.counted_volume_index = compute_counted_volume_from_scratch(waypoint.pos);
  654. }
  655. else {
  656. waypoint.counted_volume_index = COUNTED_VOLUME_INDEX_OUTSIDE_ALL;
  657. }
  658. }
  659. void Partition::initialize_all_waypoints() {
  660. // we need waypoints to be initialized all the time because they
  661. // are used not just when dealing with counted volumes, but also
  662. // by regions when detecting whether a point is inside
  663. // each center of a subpartition has a waypoint
  664. uint num_waypoints_per_dimension = config.num_subparts_per_partition_edge;
  665. mcell_log("Initializing %d waypoints... ", powu(num_waypoints_per_dimension, 3));
  666. bool use_previous_waypoint = false;
  667. IVec3 previous_waypoint_index;
  668. waypoints.resize(num_waypoints_per_dimension);
  669. for (uint x = 0; x < num_waypoints_per_dimension; x++) {
  670. waypoints[x].resize(num_waypoints_per_dimension);
  671. for (uint y = 0; y < num_waypoints_per_dimension; y++) {
  672. waypoints[x][y].resize(num_waypoints_per_dimension);
  673. for (uint z = 0; z < num_waypoints_per_dimension; z++) {
  674. initialize_waypoint(IVec3(x, y, z), use_previous_waypoint, previous_waypoint_index);
  675. use_previous_waypoint = true;
  676. previous_waypoint_index = IVec3(x, y, z);
  677. }
  678. // starting a new line - start from scratch
  679. use_previous_waypoint = false;
  680. }
  681. }
  682. }
  683. // method is in .cpp file because it uses inlined implementation
  684. counted_volume_index_t Partition::compute_counted_volume_from_scratch(const Vec3& pos) {
  685. return CollisionUtils::compute_counted_volume_for_pos(*this, pos);
  686. }
  687. counted_volume_index_t Partition::compute_counted_volume_using_waypoints(const Vec3& pos) {
  688. return CollisionUtils::compute_counted_volume_using_waypoints(*this, pos);
  689. }
  690. counted_volume_index_t Partition::find_or_add_counted_volume(const CountedVolume& cv) {
  691. assert(counted_volumes_vector.size() == counted_volumes_set.size());
  692. auto it = counted_volumes_set.find(cv);
  693. if (it != counted_volumes_set.end()) {
  694. return it->index;
  695. }
  696. else {
  697. // we must add this new item
  698. CountedVolume cv_copy = cv;
  699. cv_copy.index = counted_volumes_set.size();
  700. counted_volumes_set.insert(cv_copy);
  701. counted_volumes_vector.push_back(cv_copy);
  702. return cv_copy.index;
  703. }
  704. }
  705. // returns compartment ID, all compartments are represented by a counted volume
  706. // so we can use the counted volume information to determine it
  707. BNG::compartment_id_t Partition::get_compartment_id_for_counted_volume(const counted_volume_index_t counted_volume_index) {
  708. // caching, the mapping object->compartment is static and does not change even with dynamic geometry
  709. auto it = counted_volume_index_to_compartment_id_cache.find(counted_volume_index);
  710. if (it != counted_volume_index_to_compartment_id_cache.end()) {
  711. return it->second;
  712. }
  713. const CountedVolume& cv = get_counted_volume(counted_volume_index);
  714. // first collect all compartments we are in
  715. set<BNG::compartment_id_t> inside_of_compartments;
  716. for (geometry_object_index_t obj_index: cv.contained_in_objects) {
  717. const GeometryObject& obj = get_geometry_object(obj_index);
  718. if (obj.represents_compartment()) {
  719. inside_of_compartments.insert(obj.vol_compartment_id);
  720. }
  721. }
  722. if (inside_of_compartments.empty()) {
  723. // we are outside of any defined compartment
  724. counted_volume_index_to_compartment_id_cache[counted_volume_index] = BNG::COMPARTMENT_ID_NONE;
  725. return BNG::COMPARTMENT_ID_NONE;
  726. }
  727. if (inside_of_compartments.size() == 1) {
  728. // there is just one compartment
  729. BNG::compartment_id_t res = *inside_of_compartments.begin();
  730. counted_volume_index_to_compartment_id_cache[counted_volume_index] = res;
  731. return res;
  732. }
  733. // find the lowest level child because volume compartments are always hierarchical
  734. // and if we are inside of objects that correspond to EC and CP, it means that we are in CP
  735. BNG::compartment_id_t current = *inside_of_compartments.begin();
  736. bool child_found;
  737. do {
  738. const BNG::Compartment& comp = bng_engine.get_data().get_compartment(current);
  739. assert(comp.is_3d);
  740. // which child are we possibly in?
  741. child_found = false;
  742. for (BNG::compartment_id_t child: comp.children_compartments) {
  743. const BNG::Compartment& child_comp = bng_engine.get_data().get_compartment(child);
  744. if (!child_comp.is_3d) {
  745. // go through children of the 2d compartment (should be just one usually)
  746. for (BNG::compartment_id_t child3d: child_comp.children_compartments) {
  747. if (inside_of_compartments.count(child3d) == 1) {
  748. child_found = true;
  749. current = child3d;
  750. break;
  751. }
  752. }
  753. }
  754. else {
  755. if (inside_of_compartments.count(child) == 1) {
  756. child_found = true;
  757. current = child;
  758. break;
  759. }
  760. }
  761. }
  762. } while (child_found);
  763. counted_volume_index_to_compartment_id_cache[counted_volume_index] = current;
  764. return current;
  765. }
  766. void Partition::remove_from_known_vol_species(const species_id_t species_id) {
  767. if (known_vol_species.count(species_id) == 0) {
  768. return;
  769. }
  770. known_vol_species.erase(species_id);
  771. }
  772. void Partition::remove_reactant_class_usage(const BNG::reactant_class_id_t reactant_class_id) {
  773. volume_molecule_reactants_per_reactant_class.remove_reactant_sets_for_reactant_class(reactant_class_id);
  774. }
  775. void Partition::shuffle_schedulable_molecule_ids() {
  776. size_t n = schedulable_molecule_ids.size();
  777. if (n == 0) {
  778. return;
  779. }
  780. release_assert(n < (size_t)UINT32_MAX);
  781. for (molecule_index_t i = n-1; i > 0; --i) {
  782. double rand = rng_dbl(&aux_rng);
  783. release_assert(rand >= 0 && rand <= 1);
  784. // scale rand to 0..n-1
  785. uint rand_index = (double)(n-1) * rand;
  786. assert(rand_index < n);
  787. molecule_id_t tmp = schedulable_molecule_ids[i];
  788. schedulable_molecule_ids[i] = schedulable_molecule_ids[rand_index];
  789. schedulable_molecule_ids[rand_index] = tmp;
  790. }
  791. }
  792. static std::string check_is_valid_surface_molecule(const Partition& p, const molecule_id_t id, const char* name) {
  793. if (id == MOLECULE_ID_INVALID || id >= p.get_molecule_id_to_index_mapping().size()) {
  794. return string("Molecule with id '") + name + "' is invalid.";
  795. }
  796. const Molecule& m = p.get_m(id);
  797. if (!m.is_surf()) {
  798. return string("Molecule with id '") + name + "' (" + to_string(id) + ") is not a surface molecule.";
  799. }
  800. return "";
  801. }
  802. std::string Partition::pair_molecules(const molecule_id_t id1, const molecule_id_t id2) {
  803. string res;
  804. res = check_is_valid_surface_molecule(*this, id1, "id1");
  805. if (res != "") { return res; }
  806. res = check_is_valid_surface_molecule(*this, id2, "id2");
  807. if (res != "") { return res; }
  808. const Molecule& m1 = get_m(id1);
  809. const Molecule& m2 = get_m(id2);
  810. const Wall& w1 = get_wall(m1.s.wall_index);
  811. const Wall& w2 = get_wall(m2.s.wall_index);
  812. if (w1.object_id == w2.object_id) {
  813. return "Molecules must be present on surfaces of different objects.";
  814. }
  815. if (get_paired_molecule(id1) != MOLECULE_ID_INVALID) {
  816. return "Molecule with id 'id1' (" + to_string(id1) + ") is already paired.";
  817. }
  818. if (get_paired_molecule(id2) != MOLECULE_ID_INVALID) {
  819. return "Molecule with id 'id2' (" + to_string(id2) + ") is already paired.";
  820. }
  821. // pair
  822. paired_molecules[id1] = id2;
  823. paired_molecules[id2] = id1;
  824. return "";
  825. }
  826. std::string Partition::unpair_molecules(const molecule_id_t id1, const molecule_id_t id2) {
  827. string res;
  828. res = check_is_valid_surface_molecule(*this, id1, "id1");
  829. if (res != "") { return res; }
  830. res = check_is_valid_surface_molecule(*this, id2, "id2");
  831. if (res != "") { return res; }
  832. molecule_id_t pair1 = get_paired_molecule(id1);
  833. if (pair1 == MOLECULE_ID_INVALID) {
  834. return "Molecule with id 'id1' (" + to_string(id1) + ") is not paired.";
  835. }
  836. molecule_id_t pair2 = get_paired_molecule(id2);
  837. if (pair2 == MOLECULE_ID_INVALID) {
  838. return "Molecule with id 'id2' (" + to_string(id2) + ") is not paired.";
  839. }
  840. if (pair1 != id2) {
  841. return "Molecules are not paired to each other.";
  842. }
  843. assert(pair2 == id1);
  844. paired_molecules.erase(id1);
  845. paired_molecules.erase(id2);
  846. return "";
  847. }
  848. molecule_id_t Partition::get_paired_molecule(const molecule_id_t id) const {
  849. auto it = paired_molecules.find(id);
  850. if (it == paired_molecules.end()) {
  851. return MOLECULE_ID_INVALID;
  852. }
  853. else {
  854. return it->second;
  855. }
  856. }
  857. void Partition::to_data_model(Json::Value& mcell, std::set<rgba_t>& used_colors) const {
  858. // there are two places in data model where geometry objects are
  859. // defined - in KEY_GEOMETRICAL_OBJECTS and KEY_MODEL_OBJECTS
  860. Json::Value& geometrical_objects = mcell[KEY_GEOMETRICAL_OBJECTS];
  861. Json::Value& object_list = geometrical_objects[KEY_OBJECT_LIST];
  862. if (object_list.isNull()) {
  863. object_list = Json::Value(Json::arrayValue);
  864. }
  865. for (const GeometryObject& g: geometry_objects) {
  866. Json::Value object;
  867. g.to_data_model_as_geometrical_object(*this, config, object, used_colors);
  868. object_list.append(object);
  869. }
  870. Json::Value& model_objects = mcell[KEY_MODEL_OBJECTS];
  871. DMUtils::add_version(model_objects, VER_DM_2018_01_11_1330);
  872. Json::Value& model_object_list = model_objects[KEY_MODEL_OBJECT_LIST];
  873. model_object_list = Json::Value(Json::arrayValue);
  874. for (const GeometryObject& g: geometry_objects) {
  875. Json::Value model_object;
  876. g.to_data_model_as_model_object(*this, model_object);
  877. model_object_list.append(model_object);
  878. }
  879. // mod_surf_regions
  880. Json::Value& modify_surface_regions = mcell[KEY_MODIFY_SURFACE_REGIONS];
  881. DMUtils::add_version(modify_surface_regions, VER_DM_2014_10_24_1638);
  882. Json::Value& modify_surface_regions_list = modify_surface_regions[KEY_MODIFY_SURFACE_REGIONS_LIST];
  883. modify_surface_regions_list = Json::Value(Json::arrayValue);
  884. for (const Region& reg: regions) {
  885. if (reg.is_reactive() || reg.has_initial_molecules()) {
  886. Json::Value modify_surface_region;
  887. reg.to_data_model(*this, modify_surface_region);
  888. modify_surface_regions_list.append(modify_surface_region);
  889. }
  890. }
  891. }
  892. void Partition::print_periodic_stats() const {
  893. std::cout <<
  894. "Partition: molecules.size() = " << molecules.size() << "\n" <<
  895. "Partition: molecule_id_to_index_mapping.size() = " << molecule_id_to_index_mapping.size() << "\n" <<
  896. "Partition: next_molecule_id = " << next_molecule_id << "\n" <<
  897. "Partition: known_vol_species.size() = " << known_vol_species.size() << "\n";
  898. }
  899. } // namespace mcell