Chaste Commit::6e4f5fe395bca70eb7641cf6e0e87f450383ca5a
AbstractCardiacTissue.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "AbstractCardiacTissue.hpp"
37
38#include <boost/scoped_array.hpp>
39
40#include "DistributedVector.hpp"
41#include "AxisymmetricConductivityTensors.hpp"
42#include "OrthotropicConductivityTensors.hpp"
43#include "Exception.hpp"
44#include "ChastePoint.hpp"
45#include "AbstractChasteRegion.hpp"
46#include "HeartEventHandler.hpp"
47#include "PetscTools.hpp"
48#include "PetscVecTools.hpp"
49#include "AbstractCvodeCell.hpp"
50#include "Warnings.hpp"
51
52template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
55 bool exchangeHalos)
56 : mpMesh(pCellFactory->GetMesh()),
57 mpDistributedVectorFactory(mpMesh->GetDistributedVectorFactory()),
58 mpConductivityModifier(NULL),
59 mHasPurkinje(false),
60 mDoCacheReplication(true),
61 mMeshUnarchived(false),
62 mExchangeHalos(exchangeHalos)
63{
64 //This constructor is called from the Initialise() method of the CardiacProblem class
65 assert(pCellFactory != NULL);
66 assert(pCellFactory->GetMesh() != NULL);
67
69 {
70 //Remove the request for a halo exchange
71 mExchangeHalos = false;
72 }
73
74 unsigned num_local_nodes = mpDistributedVectorFactory->GetLocalOwnership();
75 bool process_has_no_nodes = (num_local_nodes == 0u);
76 if (PetscTools::ReplicateBool(process_has_no_nodes))
77 {
78 /* If there are no nodes on a process then there is a potential for an error in preconditioning.
79 * This is dangerous because the process without nodes may segfault and not propagate the error
80 * to the other processes. Therefore, to avoid deadlock, we share this potential for error between
81 * processes and throw an exception.
82 */
83// LCOV_EXCL_START
84 // This problem normally occurs on 3 or more processes, so we can't cover it - coverage only runs with 1 and 2 processes.
85 EXCEPTION("No cells were assigned some process in AbstractCardiacTissue constructor. Advice: Make total number of processors no greater than number of nodes in the mesh");
86// LCOV_EXCL_STOP
87 }
88 unsigned ownership_range_low = mpDistributedVectorFactory->GetLow();
89 mCellsDistributed.resize(num_local_nodes);
90
91 // Figure out if we're dealing with Purkinje
92 auto* p_purkinje_cell_factory = dynamic_cast<AbstractPurkinjeCellFactory<ELEMENT_DIM,SPACE_DIM>*>(pCellFactory);
93 if (p_purkinje_cell_factory)
94 {
95 mHasPurkinje = true;
96 mPurkinjeCellsDistributed.resize(num_local_nodes);
97 }
98
100 // Set up cells
102 try
103 {
104 for (unsigned local_index = 0; local_index < num_local_nodes; local_index++)
105 {
106 unsigned global_index = ownership_range_low + local_index;
107 Node<SPACE_DIM>* p_node = mpMesh->GetNode(global_index);
108 mCellsDistributed[local_index] = pCellFactory->CreateCardiacCellForNode(p_node);
110
111 if (mHasPurkinje)
112 {
113 mPurkinjeCellsDistributed[local_index] = p_purkinje_cell_factory->CreatePurkinjeCellForNode(p_node, mCellsDistributed[local_index]);
114 mPurkinjeCellsDistributed[local_index]->SetUsedInTissueSimulation();
115 }
116 }
117
121 if (mHasPurkinje)
122 {
123 p_purkinje_cell_factory->FinalisePurkinjeCellCreation(&mPurkinjeCellsDistributed,
126 }
127 }
128 catch (const Exception& e)
129 {
130 // This catch statement is quite tricky to cover, but it is actually done
131 // in TestCardiacSimulation::TestMono1dSodiumBlockBySettingNamedParameter()
132
133 // Errors thrown creating cells will often be process-specific
135
136 // Delete cells
137 // Should really do this for other processes too, but this is all we need
138 // to get memory testing to pass, and leaking when we're about to die isn't
139 // that bad!
140 for (std::vector<AbstractCardiacCellInterface*>::iterator cell_iterator = mCellsDistributed.begin();
141 cell_iterator != mCellsDistributed.end();
142 ++cell_iterator)
143 {
144 delete (*cell_iterator);
145 }
146
147 throw e;
148 }
150
151 // Halo nodes (if required)
152 SetUpHaloCells(pCellFactory);
153
154 HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
157
158 if (mHasPurkinje)
159 {
162 }
163 HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
164
165 if (HeartConfig::Instance()->IsMeshProvided() && HeartConfig::Instance()->GetLoadMesh())
166 {
168 }
169 else
170 {
171 // As of r10671 fibre orientation can only be defined when loading a mesh from disc.
173 }
175}
176
177// Constructor used for archiving
178template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
180 : mpMesh(pMesh),
181 mpDistributedVectorFactory(mpMesh->GetDistributedVectorFactory()),
182 mHasPurkinje(false),
183 mDoCacheReplication(true),
184 mMeshUnarchived(true),
185 mExchangeHalos(false)
186{
189
192}
193
194template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
196{
197 // Delete cells
198 for (std::vector<AbstractCardiacCellInterface*>::iterator iter = mCellsDistributed.begin();
199 iter != mCellsDistributed.end();
200 ++iter)
201 {
202 delete (*iter);
203 }
204
205 // Delete cells for halo nodes
206 for (std::vector<AbstractCardiacCellInterface*>::iterator iter = mHaloCellsDistributed.begin();
207 iter != mHaloCellsDistributed.end();
208 ++iter)
209 {
210 delete (*iter);
211 }
212
213 delete mpIntracellularConductivityTensors;
214
215 // Delete Purkinje cells
216 for (std::vector<AbstractCardiacCellInterface*>::iterator iter = mPurkinjeCellsDistributed.begin();
217 iter != mPurkinjeCellsDistributed.end();
218 ++iter)
219 {
220 delete (*iter);
221 }
222
223 // If the mesh was unarchived we need to free it explicitly.
224 if (mMeshUnarchived)
225 {
226 delete mpMesh;
227 }
228}
229
230
231template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
233{
234 return mHasPurkinje;
235}
236
237template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
239{
240 HeartEventHandler::BeginEvent(HeartEventHandler::READ_MESH);
241 mpConfig = HeartConfig::Instance();
242
243 if (mpConfig->IsMeshProvided() && mpConfig->GetLoadMesh())
244 {
245 assert(mFibreFilePathNoExtension != "");
246
247 switch (mpConfig->GetConductivityMedia())
248 {
249 case cp::media_type::Orthotropic:
250 {
251 mpIntracellularConductivityTensors = new OrthotropicConductivityTensors<ELEMENT_DIM,SPACE_DIM>;
252 FileFinder ortho_file(mFibreFilePathNoExtension + ".ortho", RelativeTo::AbsoluteOrCwd);
253 assert(ortho_file.Exists());
254 mpIntracellularConductivityTensors->SetFibreOrientationFile(ortho_file);
255 break;
256 }
257
258 case cp::media_type::Axisymmetric:
259 {
260 mpIntracellularConductivityTensors = new AxisymmetricConductivityTensors<ELEMENT_DIM,SPACE_DIM>;
261 FileFinder axi_file(mFibreFilePathNoExtension + ".axi", RelativeTo::AbsoluteOrCwd);
262 assert(axi_file.Exists());
263 mpIntracellularConductivityTensors->SetFibreOrientationFile(axi_file);
264 break;
265 }
266
267 case cp::media_type::NoFibreOrientation:
269 mpIntracellularConductivityTensors = new OrthotropicConductivityTensors<ELEMENT_DIM,SPACE_DIM>;
270 break;
271
272 default:
274 }
275 }
276 else // Slab defined in config file or SetMesh() called; no fibre orientation assumed
277 {
279 mpIntracellularConductivityTensors = new OrthotropicConductivityTensors<ELEMENT_DIM,SPACE_DIM>;
280 }
281
282 c_vector<double, SPACE_DIM> intra_conductivities;
283 mpConfig->GetIntracellularConductivities(intra_conductivities);
284
285 // this definition must be here (and not inside the if statement) because SetNonConstantConductivities() will keep
286 // a pointer to it and we don't want it to go out of scope before Init() is called
287 unsigned num_local_elements = mpMesh->GetNumLocalElements();
288 std::vector<c_vector<double, SPACE_DIM> > hetero_intra_conductivities;
289
290 if (mpConfig->GetConductivityHeterogeneitiesProvided())
291 {
292 try
293 {
294 assert(hetero_intra_conductivities.size()==0);
295 hetero_intra_conductivities.resize(num_local_elements, intra_conductivities);
296 }
297 // LCOV_EXCL_START
298 catch(std::bad_alloc &r_bad_alloc)
299 {
300 std::cout << "Failed to allocate std::vector of size " << num_local_elements << std::endl;
302 throw r_bad_alloc;
303 }
304 // LCOV_EXCL_STOP
305
307
308 std::vector<boost::shared_ptr<AbstractChasteRegion<SPACE_DIM> > > conductivities_heterogeneity_areas;
309 std::vector< c_vector<double,3> > intra_h_conductivities;
310 std::vector< c_vector<double,3> > extra_h_conductivities;
311 HeartConfig::Instance()->GetConductivityHeterogeneities(conductivities_heterogeneity_areas,
312 intra_h_conductivities,
313 extra_h_conductivities);
314
315 unsigned local_element_index = 0;
316
317 for (typename AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>::ElementIterator it = mpMesh->GetElementIteratorBegin();
318 it != mpMesh->GetElementIteratorEnd();
319 ++it)
320 {
321// unsigned element_index = it->GetIndex();
322 // if element centroid is contained in the region
323 ChastePoint<SPACE_DIM> element_centroid(it->CalculateCentroid());
324 for (unsigned region_index=0; region_index< conductivities_heterogeneity_areas.size(); region_index++)
325 {
326 if (conductivities_heterogeneity_areas[region_index]->DoesContain(element_centroid))
327 {
328 //We don't use ublas vector assignment here, because we might be getting a subvector of a 3-vector
329 for (unsigned i=0; i<SPACE_DIM; i++)
330 {
331 hetero_intra_conductivities[local_element_index][i] = intra_h_conductivities[region_index][i];
332 }
333 }
334 }
335 local_element_index++;
336 }
337
338 mpIntracellularConductivityTensors->SetNonConstantConductivities(&hetero_intra_conductivities);
339 }
340 else
341 {
342 mpIntracellularConductivityTensors->SetConstantConductivities(intra_conductivities);
343 }
344
345 mpIntracellularConductivityTensors->Init(this->mpMesh);
346 HeartEventHandler::EndEvent(HeartEventHandler::READ_MESH);
347}
348
349
350template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
352{
353 mDoCacheReplication = doCacheReplication;
354}
355
356template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
359 return mDoCacheReplication;
360}
361
362template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
363const c_matrix<double, SPACE_DIM, SPACE_DIM>& AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::rGetIntracellularConductivityTensor(unsigned elementIndex)
364{
365 assert( mpIntracellularConductivityTensors);
366 if (mpConductivityModifier==NULL)
367 {
368 return (*mpIntracellularConductivityTensors)[elementIndex];
369 }
370 else
372 return mpConductivityModifier->rGetModifiedConductivityTensor(elementIndex, (*mpIntracellularConductivityTensors)[elementIndex], 0u);
373 }
374}
375
376template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
377const c_matrix<double, SPACE_DIM, SPACE_DIM>& AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::rGetExtracellularConductivityTensor(unsigned elementIndex)
379 EXCEPTION("Monodomain tissues do not have extracellular conductivity tensors.");
380}
382template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
385 assert(mpDistributedVectorFactory->GetLow() <= globalIndex &&
386 globalIndex < mpDistributedVectorFactory->GetHigh());
387 return mCellsDistributed[globalIndex - mpDistributedVectorFactory->GetLow()];
388}
389
390template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
393 assert(mpDistributedVectorFactory->GetLow() <= globalIndex &&
394 globalIndex < mpDistributedVectorFactory->GetHigh());
395 EXCEPT_IF_NOT(mHasPurkinje);
396 return mPurkinjeCellsDistributed[globalIndex - mpDistributedVectorFactory->GetLow()];
397}
398
399template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
401{
402 std::map<unsigned, unsigned>::const_iterator node_position;
403 // First search the halo
404 if ((node_position=mHaloGlobalToLocalIndexMap.find(globalIndex)) != mHaloGlobalToLocalIndexMap.end())
405 {
406 // Found a halo node
407 return mHaloCellsDistributed[node_position->second];
408 }
409 // Then search the owned node
410 if (mpDistributedVectorFactory->IsGlobalIndexLocal(globalIndex))
411 {
412 // Found an owned node
413 return mCellsDistributed[globalIndex - mpDistributedVectorFactory->GetLow()];
414 }
415 // Not here
416 EXCEPTION("Requested node/halo " << globalIndex << " does not belong to processor " << PetscTools::GetMyRank());
417}
418
419
420template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
422{
423 std::set<unsigned> halos_as_set;
424 for (unsigned proc=0; proc<PetscTools::GetNumProcs(); proc++)
425 {
426 halos_as_set.insert(mNodesToReceivePerProcess[proc].begin(), mNodesToReceivePerProcess[proc].end());
427 }
428 mHaloNodes = std::vector<unsigned>(halos_as_set.begin(), halos_as_set.end());
429 //PRINT_VECTOR(mHaloNodes);
430}
431
432
433template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
435{
436 if (mExchangeHalos)
437 {
438 mpMesh->CalculateNodeExchange(mNodesToSendPerProcess, mNodesToReceivePerProcess);
439 // Note that the following call will not work for a TetrahedralMesh which has
440 // no concept of halo nodes.
441 //mpMesh->GetHaloNodeIndices( mHaloNodes );
442 CalculateHaloNodesFromNodeExchange();
443 unsigned num_halo_nodes = mHaloNodes.size();
444 mHaloCellsDistributed.resize( num_halo_nodes );
445 for (unsigned local_index = 0; local_index < num_halo_nodes; local_index++)
446 {
447 unsigned global_index = mHaloNodes[local_index];
448 // These are all halo nodes, so we use the "GetNodeOrHaloNode" variety of GetNode
449 Node<SPACE_DIM>* p_node = mpMesh->GetNodeOrHaloNode(global_index);
450 mHaloCellsDistributed[local_index] = pCellFactory->CreateCardiacCellForNode(p_node);
451 mHaloCellsDistributed[local_index]->SetUsedInTissueSimulation();
452 mHaloGlobalToLocalIndexMap[global_index] = local_index;
453 }
454 // No need to call FinaliseCellCreation() as halo node cardiac cells will
455 // never be stimulated (their values are communicated from the process that
456 // owns them).
458}
459
461template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
462void AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::SolveCellSystems(Vec existingSolution, double time, double nextTime, bool updateVoltage)
464 if (mHasPurkinje)
465 {
466 // can't do Purkinje and operator splitting
467 assert(!updateVoltage);
468 // The code below assumes Purkinje is are monodomain, so the vector has two stripes.
469 // The assert will fail the first time bidomain purkinje is coded - need to decide what
470 // ordering the three stripes (V, V_purk, phi_e) are in
471 assert(PetscVecTools::GetSize(existingSolution)==2*mpMesh->GetNumNodes());
472 }
473
474 HeartEventHandler::BeginEvent(HeartEventHandler::SOLVE_ODES);
475
476 DistributedVector dist_solution = mpDistributedVectorFactory->CreateDistributedVector(existingSolution);
477
479 // Solve cell models (except purkinje cell models)
481 DistributedVector::Stripe voltage(dist_solution, 0);
482 try
483 {
484 double voltage_before_update;
485 for (DistributedVector::Iterator index = dist_solution.Begin();
486 index != dist_solution.End();
487 ++index)
488 {
489 voltage_before_update = voltage[index];
490 mCellsDistributed[index.Local]->SetVoltage( voltage_before_update );
491
492 // Added a try-catch here to provide more output to screen when an error occurs.
494 try
496 if (!updateVoltage)
497 {
498 // solve ODE system at this node.
499 // Note: Voltage is not being updated. The voltage is updated in the PDE solve.
500#ifndef CHASTE_CVODE
501 mCellsDistributed[index.Local]->ComputeExceptVoltage(time, nextTime);
502#else
503 // If CVODE is enabled, and this is a CVODE cell
504 // there's a chance we can recover this by doing a reset so put the above call in a try...catch.
505 try
506 {
507 mCellsDistributed[index.Local]->ComputeExceptVoltage(time, nextTime);
508 }
509 catch (Exception &e)
510 {
511 // Try an 'emergency' reset if this is a CVODE cell.
512 // See #2594 for why we think this may be necessary.
513 if (dynamic_cast<AbstractCvodeCell*>(mCellsDistributed[index.Local]))
515 // Reset the CVODE cell, this leads to a call to CVodeReInit.
516 static_cast<AbstractCvodeCell*>(mCellsDistributed[index.Local])->ResetSolver();
517 mCellsDistributed[index.Local]->ComputeExceptVoltage(time, nextTime);
518 WARNING("Global node " << index.Global << " had an ODE solving problem in t = [" << time <<
519 ", " << nextTime << "] ms. This was fixed by a reset of CVODE, but may suggest PDE time"
520 " step should be reduced, or CVODE tolerances relaxed.");
521 }
522 else
523 {
524 throw e;
525 }
526 }
527#endif // CHASTE_CVODE
528 }
529 else
530 {
531 // solve, including updating the voltage (for the operator-splitting implementation of the monodomain solver)
532 mCellsDistributed[index.Local]->SolveAndUpdateState(time, nextTime);
533 voltage[index] = mCellsDistributed[index.Local]->GetVoltage();
534 }
535 }
536 catch (Exception &e)
537 {
538 std::cout << std::setprecision(16);
539 std::cout << "Global node " << index.Global << " had problems with ODE solve between "
540 "t = " << time << " and " << nextTime << "ms.\n";
541
542 std::cout << "Voltage at this node before solve was " << voltage_before_update << "mV\n"
543 "(this SHOULD NOT necessarily be the same as the one in the state variables,\n"
544 "which can be ignored and stay at the initial condition - the voltage is dictated by PDE instead of state variable.)\n";
545
546 std::cout << "Stimulus current (NB converted to micro-Amps per cm^3) applied here is equal to:\n\t"
547 << mCellsDistributed[index.Local]->GetIntracellularStimulus(time) << " at t = " << time << "ms,\n\t"
548 << mCellsDistributed[index.Local]->GetIntracellularStimulus(nextTime) << " at t = " << nextTime << "ms.\n";
549
550 std::cout << "Cell model: " << dynamic_cast<AbstractUntemplatedParameterisedSystem*>(mCellsDistributed[index.Local])->GetSystemName() << "\n";
551
552 std::cout << "All state variables are now:\n";
553 std::vector<double> state_vars = mCellsDistributed[index.Local]->GetStdVecStateVariables();
554 std::vector<std::string> state_var_names = mCellsDistributed[index.Local]->rGetStateVariableNames();
555 for (unsigned i=0; i<state_vars.size(); i++)
556 {
557 std::cout << "\t" << state_var_names[i] << "\t:\t" << state_vars[i] << "\n";
558 }
559 std::cout << std::flush;
560
561 throw e;
562 }
563 // update the Iionic and stimulus caches
564 UpdateCaches(index.Global, index.Local, nextTime);
565 }
566
567 if (updateVoltage)
568 {
569 dist_solution.Restore();
570 }
571 }
572 catch (Exception &e)
573 {
575 throw e;
576 }
577
579 // Solve purkinje cell models
581 if (mHasPurkinje)
582 {
583 DistributedVector::Stripe purkinje_voltage(dist_solution, 1);
584 try
585 {
586 for (DistributedVector::Iterator index = dist_solution.Begin();
587 index != dist_solution.End();
588 ++index)
589 {
590 // overwrite the voltage with the input value
591 mPurkinjeCellsDistributed[index.Local]->SetVoltage( purkinje_voltage[index] );
592
593 // solve
594 // Note: Voltage is not being updated. The voltage is updated in the PDE solve.
595 mPurkinjeCellsDistributed[index.Local]->ComputeExceptVoltage(time, nextTime);
596
597 // update the Iionic and stimulus caches
598 UpdatePurkinjeCaches(index.Global, index.Local, nextTime);
599 }
600 }
601 // LCOV_EXCL_START
602 catch (Exception& e)
603 {
607
610 throw e;
611 }
612 // LCOV_EXCL_STOP
613 }
614
616 HeartEventHandler::EndEvent(HeartEventHandler::SOLVE_ODES);
617
618 // Communicate new state variable values to halo nodes
619 if (mExchangeHalos)
620 {
621 assert(!mHasPurkinje);
622
623 for ( unsigned rank_offset = 1; rank_offset < PetscTools::GetNumProcs(); rank_offset++ )
624 {
625 unsigned send_to = (PetscTools::GetMyRank() + rank_offset) % (PetscTools::GetNumProcs());
626 unsigned receive_from = (PetscTools::GetMyRank() + PetscTools::GetNumProcs()- rank_offset ) % (PetscTools::GetNumProcs());
627
628 unsigned number_of_cells_to_send = mNodesToSendPerProcess[send_to].size();
629 unsigned number_of_cells_to_receive = mNodesToReceivePerProcess[receive_from].size();
630
631 // Pack send buffer
632 unsigned send_size = 0;
633 for (unsigned i=0; i<number_of_cells_to_send; i++)
634 {
635 unsigned global_cell_index = mNodesToSendPerProcess[send_to][i];
636 send_size += mCellsDistributed[global_cell_index - mpDistributedVectorFactory->GetLow()]->GetNumberOfStateVariables();
637 }
638
639 boost::scoped_array<double> send_data(new double[send_size]);
640
641 unsigned send_index = 0;
642 for (unsigned cell = 0; cell < number_of_cells_to_send; cell++)
643 {
644 unsigned global_cell_index = mNodesToSendPerProcess[send_to][cell];
645 AbstractCardiacCellInterface* p_cell = mCellsDistributed[global_cell_index - mpDistributedVectorFactory->GetLow()];
646 std::vector<double> cell_data = p_cell->GetStdVecStateVariables();
647 const unsigned num_state_vars = p_cell->GetNumberOfStateVariables();
648 for (unsigned state_variable = 0; state_variable < num_state_vars; state_variable++)
649 {
650 send_data[send_index++] = cell_data[state_variable];
651 }
652 }
653 // Receive buffer
654 unsigned receive_size = 0;
655 for (unsigned i=0; i<number_of_cells_to_receive; i++)
656 {
657 unsigned halo_cell_index = mHaloGlobalToLocalIndexMap[mNodesToReceivePerProcess[receive_from][i]];
658 receive_size += mHaloCellsDistributed[halo_cell_index]->GetNumberOfStateVariables();
659 }
660
661 boost::scoped_array<double> receive_data(new double[receive_size]);
662
663 // Send and receive
664 int ret;
665 MPI_Status status;
666 ret = MPI_Sendrecv(send_data.get(), send_size,
667 MPI_DOUBLE,
668 send_to, 0,
669 receive_data.get(), receive_size,
670 MPI_DOUBLE,
671 receive_from, 0,
672 PETSC_COMM_WORLD, &status);
673 UNUSED_OPT(ret);
674 assert ( ret == MPI_SUCCESS);
675
676 // Unpack
677 unsigned receive_index = 0;
678 for ( unsigned cell = 0; cell < number_of_cells_to_receive; cell++ )
679 {
680 AbstractCardiacCellInterface* p_cell = mHaloCellsDistributed[mHaloGlobalToLocalIndexMap[mNodesToReceivePerProcess[receive_from][cell]]];
681 const unsigned number_of_state_variables = p_cell->GetNumberOfStateVariables();
682
683 std::vector<double> cell_data(number_of_state_variables);
684 for (unsigned state_variable = 0; state_variable < number_of_state_variables; state_variable++)
685 {
686 cell_data[state_variable] = receive_data[receive_index++];
687 }
688 p_cell->SetStateVariables(cell_data);
689 }
690 }
691 }
692
693 HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
694 if (mDoCacheReplication)
695 {
696 ReplicateCaches();
697 }
698 HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
699}
700
701template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
706
707template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
712
713template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
715{
716 EXCEPT_IF_NOT(mHasPurkinje);
717 return mPurkinjeIionicCacheReplicated;
718}
719
720template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
722{
723 EXCEPT_IF_NOT(mHasPurkinje);
724 return mPurkinjeIntracellularStimulusCacheReplicated;
725}
726
727template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
728void AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::UpdateCaches(unsigned globalIndex, unsigned localIndex, double nextTime)
729{
730 mIionicCacheReplicated[globalIndex] = mCellsDistributed[localIndex]->GetIIonic();
731 mIntracellularStimulusCacheReplicated[globalIndex] = mCellsDistributed[localIndex]->GetIntracellularStimulus(nextTime);
732}
733
734template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
735void AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::UpdatePurkinjeCaches(unsigned globalIndex, unsigned localIndex, double nextTime)
736{
737 assert(mHasPurkinje);
738 mPurkinjeIionicCacheReplicated[globalIndex] = mPurkinjeCellsDistributed[localIndex]->GetIIonic();
739 mPurkinjeIntracellularStimulusCacheReplicated[globalIndex] = mPurkinjeCellsDistributed[localIndex]->GetIntracellularStimulus(nextTime);
740}
741
742template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
744{
745 // ReplicateCaches only needed for SVI (and non-matrix based assembly which is no longer in code)
746 // which is not implemented with Purkinje. See commented code below if introducing this.
747 assert(!mHasPurkinje);
748
749 mIionicCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
750 mIntracellularStimulusCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
751
752 //if (mHasPurkinje)
753 //{
754 // mPurkinjeIionicCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
755 // mPurkinjeIntracellularStimulusCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
756 //}
757}
758
759template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
760const std::vector<AbstractCardiacCellInterface*>& AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::rGetCellsDistributed() const
761{
762 return mCellsDistributed;
763}
764
765template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
766const std::vector<AbstractCardiacCellInterface*>& AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::rGetPurkinjeCellsDistributed() const
767{
768 EXCEPT_IF_NOT(mHasPurkinje);
769 return mPurkinjeCellsDistributed;
770}
771
772template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
777
778template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
780{
781 assert(pModifier!=NULL);
782 assert(mpConductivityModifier==NULL); // shouldn't be called twice for example, or with two different modifiers (remove this assert
783 // if for whatever reason want to be able to overwrite modifiers)
784 mpConductivityModifier = pModifier;
785}
786
787// Explicit instantiation
788template class AbstractCardiacTissue<1,1>;
789template class AbstractCardiacTissue<1,2>;
790template class AbstractCardiacTissue<1,3>;
791template class AbstractCardiacTissue<2,2>;
792template class AbstractCardiacTissue<3,3>;
#define EXCEPTION(message)
#define EXCEPT_IF_NOT(test)
#define UNUSED_OPT(var)
#define NEVER_REACHED
virtual AbstractCardiacCellInterface * CreateCardiacCellForNode(Node< SPACE_DIM > *pNode)
AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * GetMesh()
virtual void FinaliseCellCreation(std::vector< AbstractCardiacCellInterface * > *pCellsDistributed, unsigned lo, unsigned hi)
virtual void SetStateVariables(const std::vector< double > &rVariables)=0
virtual unsigned GetNumberOfStateVariables() const =0
virtual std::vector< double > GetStdVecStateVariables()=0
const std::vector< AbstractCardiacCellInterface * > & rGetPurkinjeCellsDistributed() const
ReplicatableVector mPurkinjeIntracellularStimulusCacheReplicated
ReplicatableVector & rGetIionicCacheReplicated()
ReplicatableVector mIntracellularStimulusCacheReplicated
const AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * pGetMesh() const
AbstractCardiacCellInterface * GetCardiacCellOrHaloCell(unsigned globalIndex)
void UpdateCaches(unsigned globalIndex, unsigned localIndex, double nextTime)
void UpdatePurkinjeCaches(unsigned globalIndex, unsigned localIndex, double nextTime)
const std::vector< AbstractCardiacCellInterface * > & rGetCellsDistributed() const
ReplicatableVector & rGetPurkinjeIntracellularStimulusCacheReplicated()
virtual void SolveCellSystems(Vec existingSolution, double time, double nextTime, bool updateVoltage=false)
ReplicatableVector & rGetIntracellularStimulusCacheReplicated()
DistributedVectorFactory * mpDistributedVectorFactory
void SetCacheReplication(bool doCacheReplication)
ReplicatableVector & rGetPurkinjeIionicCacheReplicated()
std::vector< AbstractCardiacCellInterface * > mPurkinjeCellsDistributed
AbstractCardiacCellInterface * GetPurkinjeCell(unsigned globalIndex)
void SetConductivityModifier(AbstractConductivityModifier< ELEMENT_DIM, SPACE_DIM > *pModifier)
std::vector< AbstractCardiacCellInterface * > mCellsDistributed
ReplicatableVector mPurkinjeIionicCacheReplicated
void SetUpHaloCells(AbstractCardiacCellFactory< ELEMENT_DIM, SPACE_DIM > *pCellFactory)
AbstractCardiacCellInterface * GetCardiacCell(unsigned globalIndex)
virtual const c_matrix< double, SPACE_DIM, SPACE_DIM > & rGetExtracellularConductivityTensor(unsigned elementIndex)
const c_matrix< double, SPACE_DIM, SPACE_DIM > & rGetIntracellularConductivityTensor(unsigned elementIndex)
AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * mpMesh
AbstractCardiacTissue(AbstractCardiacCellFactory< ELEMENT_DIM, SPACE_DIM > *pCellFactory, bool exchangeHalos=false)
ReplicatableVector mIionicCacheReplicated
void ComputeExceptVoltage(double tStart, double tEnd)
const std::vector< std::string > & rGetStateVariableNames() const
static std::string GetMeshFilename()
static std::string GetArchiveDirectory()
bool Exists() const
std::string GetMeshName() const
void GetConductivityHeterogeneities(std::vector< boost::shared_ptr< AbstractChasteRegion< DIM > > > &conductivitiesHeterogeneityAreas, std::vector< c_vector< double, 3 > > &intraConductivities, std::vector< c_vector< double, 3 > > &extraConductivities) const
static HeartConfig * Instance()
Definition Node.hpp:59
static bool ReplicateBool(bool flag)
static bool IsSequential()
static unsigned GetMyRank()
static void ReplicateException(bool flag)
static unsigned GetNumProcs()
static unsigned GetSize(Vec vector)
void Resize(unsigned size)