Chaste Commit::29c3c4ab2bee00ddd311a035a1e522c03cbe928a
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 // If this is a CVODE cell there's a chance we can recover an ODE solving problem by doing
501 // a reset, so put the call in a try...catch.
502 try
503 {
504 mCellsDistributed[index.Local]->ComputeExceptVoltage(time, nextTime);
505 }
506 catch (Exception &e)
508 // Try an 'emergency' reset if this is a CVODE cell.
509 // See #2594 for why we think this may be necessary.
510 if (dynamic_cast<AbstractCvodeCell*>(mCellsDistributed[index.Local]))
511 {
512 // Reset the CVODE cell, this leads to a call to CVodeReInit.
513 static_cast<AbstractCvodeCell*>(mCellsDistributed[index.Local])->ResetSolver();
514 mCellsDistributed[index.Local]->ComputeExceptVoltage(time, nextTime);
515 WARNING("Global node " << index.Global << " had an ODE solving problem in t = [" << time <<
516 ", " << nextTime << "] ms. This was fixed by a reset of CVODE, but may suggest PDE time"
517 " step should be reduced, or CVODE tolerances relaxed.");
518 }
519 else
520 {
521 throw e;
522 }
523 }
524 }
525 else
526 {
527 // solve, including updating the voltage (for the operator-splitting implementation of the monodomain solver)
528 mCellsDistributed[index.Local]->SolveAndUpdateState(time, nextTime);
529 voltage[index] = mCellsDistributed[index.Local]->GetVoltage();
530 }
531 }
532 catch (Exception &e)
533 {
534 std::cout << std::setprecision(16);
535 std::cout << "Global node " << index.Global << " had problems with ODE solve between "
536 "t = " << time << " and " << nextTime << "ms.\n";
537
538 std::cout << "Voltage at this node before solve was " << voltage_before_update << "mV\n"
539 "(this SHOULD NOT necessarily be the same as the one in the state variables,\n"
540 "which can be ignored and stay at the initial condition - the voltage is dictated by PDE instead of state variable.)\n";
541
542 std::cout << "Stimulus current (NB converted to micro-Amps per cm^3) applied here is equal to:\n\t"
543 << mCellsDistributed[index.Local]->GetIntracellularStimulus(time) << " at t = " << time << "ms,\n\t"
544 << mCellsDistributed[index.Local]->GetIntracellularStimulus(nextTime) << " at t = " << nextTime << "ms.\n";
545
546 std::cout << "Cell model: " << dynamic_cast<AbstractUntemplatedParameterisedSystem*>(mCellsDistributed[index.Local])->GetSystemName() << "\n";
547
548 std::cout << "All state variables are now:\n";
549 std::vector<double> state_vars = mCellsDistributed[index.Local]->GetStdVecStateVariables();
550 std::vector<std::string> state_var_names = mCellsDistributed[index.Local]->rGetStateVariableNames();
551 for (unsigned i=0; i<state_vars.size(); i++)
552 {
553 std::cout << "\t" << state_var_names[i] << "\t:\t" << state_vars[i] << "\n";
554 }
555 std::cout << std::flush;
556
557 throw e;
558 }
559 // update the Iionic and stimulus caches
560 UpdateCaches(index.Global, index.Local, nextTime);
561 }
562
563 if (updateVoltage)
564 {
565 dist_solution.Restore();
566 }
567 }
568 catch (Exception &e)
569 {
571 throw e;
572 }
573
575 // Solve purkinje cell models
577 if (mHasPurkinje)
578 {
579 DistributedVector::Stripe purkinje_voltage(dist_solution, 1);
580 try
581 {
582 for (DistributedVector::Iterator index = dist_solution.Begin();
583 index != dist_solution.End();
584 ++index)
585 {
586 // overwrite the voltage with the input value
587 mPurkinjeCellsDistributed[index.Local]->SetVoltage( purkinje_voltage[index] );
588
589 // solve
590 // Note: Voltage is not being updated. The voltage is updated in the PDE solve.
591 mPurkinjeCellsDistributed[index.Local]->ComputeExceptVoltage(time, nextTime);
592
593 // update the Iionic and stimulus caches
594 UpdatePurkinjeCaches(index.Global, index.Local, nextTime);
595 }
596 }
597 // LCOV_EXCL_START
598 catch (Exception& e)
599 {
603
606 throw e;
607 }
608 // LCOV_EXCL_STOP
609 }
610
612 HeartEventHandler::EndEvent(HeartEventHandler::SOLVE_ODES);
613
614 // Communicate new state variable values to halo nodes
615 if (mExchangeHalos)
616 {
617 assert(!mHasPurkinje);
618
619 for ( unsigned rank_offset = 1; rank_offset < PetscTools::GetNumProcs(); rank_offset++ )
620 {
621 unsigned send_to = (PetscTools::GetMyRank() + rank_offset) % (PetscTools::GetNumProcs());
622 unsigned receive_from = (PetscTools::GetMyRank() + PetscTools::GetNumProcs()- rank_offset ) % (PetscTools::GetNumProcs());
623
624 unsigned number_of_cells_to_send = mNodesToSendPerProcess[send_to].size();
625 unsigned number_of_cells_to_receive = mNodesToReceivePerProcess[receive_from].size();
626
627 // Pack send buffer
628 unsigned send_size = 0;
629 for (unsigned i=0; i<number_of_cells_to_send; i++)
630 {
631 unsigned global_cell_index = mNodesToSendPerProcess[send_to][i];
632 send_size += mCellsDistributed[global_cell_index - mpDistributedVectorFactory->GetLow()]->GetNumberOfStateVariables();
633 }
634
635 boost::scoped_array<double> send_data(new double[send_size]);
636
637 unsigned send_index = 0;
638 for (unsigned cell = 0; cell < number_of_cells_to_send; cell++)
639 {
640 unsigned global_cell_index = mNodesToSendPerProcess[send_to][cell];
641 AbstractCardiacCellInterface* p_cell = mCellsDistributed[global_cell_index - mpDistributedVectorFactory->GetLow()];
642 std::vector<double> cell_data = p_cell->GetStdVecStateVariables();
643 const unsigned num_state_vars = p_cell->GetNumberOfStateVariables();
644 for (unsigned state_variable = 0; state_variable < num_state_vars; state_variable++)
645 {
646 send_data[send_index++] = cell_data[state_variable];
647 }
648 }
649 // Receive buffer
650 unsigned receive_size = 0;
651 for (unsigned i=0; i<number_of_cells_to_receive; i++)
652 {
653 unsigned halo_cell_index = mHaloGlobalToLocalIndexMap[mNodesToReceivePerProcess[receive_from][i]];
654 receive_size += mHaloCellsDistributed[halo_cell_index]->GetNumberOfStateVariables();
655 }
656
657 boost::scoped_array<double> receive_data(new double[receive_size]);
658
659 // Send and receive
660 int ret;
661 MPI_Status status;
662 ret = MPI_Sendrecv(send_data.get(), send_size,
663 MPI_DOUBLE,
664 send_to, 0,
665 receive_data.get(), receive_size,
666 MPI_DOUBLE,
667 receive_from, 0,
668 PETSC_COMM_WORLD, &status);
669 UNUSED_OPT(ret);
670 assert ( ret == MPI_SUCCESS);
671
672 // Unpack
673 unsigned receive_index = 0;
674 for ( unsigned cell = 0; cell < number_of_cells_to_receive; cell++ )
675 {
676 AbstractCardiacCellInterface* p_cell = mHaloCellsDistributed[mHaloGlobalToLocalIndexMap[mNodesToReceivePerProcess[receive_from][cell]]];
677 const unsigned number_of_state_variables = p_cell->GetNumberOfStateVariables();
678
679 std::vector<double> cell_data(number_of_state_variables);
680 for (unsigned state_variable = 0; state_variable < number_of_state_variables; state_variable++)
681 {
682 cell_data[state_variable] = receive_data[receive_index++];
683 }
684 p_cell->SetStateVariables(cell_data);
685 }
686 }
687 }
688
689 HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
690 if (mDoCacheReplication)
691 {
692 ReplicateCaches();
693 }
694 HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
695}
696
697template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
702
703template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
708
709template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
711{
712 EXCEPT_IF_NOT(mHasPurkinje);
713 return mPurkinjeIionicCacheReplicated;
714}
715
716template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
718{
719 EXCEPT_IF_NOT(mHasPurkinje);
720 return mPurkinjeIntracellularStimulusCacheReplicated;
721}
722
723template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
724void AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::UpdateCaches(unsigned globalIndex, unsigned localIndex, double nextTime)
725{
726 mIionicCacheReplicated[globalIndex] = mCellsDistributed[localIndex]->GetIIonic();
727 mIntracellularStimulusCacheReplicated[globalIndex] = mCellsDistributed[localIndex]->GetIntracellularStimulus(nextTime);
728}
729
730template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
731void AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::UpdatePurkinjeCaches(unsigned globalIndex, unsigned localIndex, double nextTime)
732{
733 assert(mHasPurkinje);
734 mPurkinjeIionicCacheReplicated[globalIndex] = mPurkinjeCellsDistributed[localIndex]->GetIIonic();
735 mPurkinjeIntracellularStimulusCacheReplicated[globalIndex] = mPurkinjeCellsDistributed[localIndex]->GetIntracellularStimulus(nextTime);
736}
737
738template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
740{
741 // ReplicateCaches only needed for SVI (and non-matrix based assembly which is no longer in code)
742 // which is not implemented with Purkinje. See commented code below if introducing this.
743 assert(!mHasPurkinje);
744
745 mIionicCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
746 mIntracellularStimulusCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
747
748 //if (mHasPurkinje)
749 //{
750 // mPurkinjeIionicCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
751 // mPurkinjeIntracellularStimulusCacheReplicated.Replicate(mpDistributedVectorFactory->GetLow(), mpDistributedVectorFactory->GetHigh());
752 //}
753}
754
755template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
756const std::vector<AbstractCardiacCellInterface*>& AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::rGetCellsDistributed() const
757{
758 return mCellsDistributed;
759}
760
761template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
762const std::vector<AbstractCardiacCellInterface*>& AbstractCardiacTissue<ELEMENT_DIM,SPACE_DIM>::rGetPurkinjeCellsDistributed() const
763{
764 EXCEPT_IF_NOT(mHasPurkinje);
765 return mPurkinjeCellsDistributed;
766}
767
768template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
773
774template <unsigned ELEMENT_DIM,unsigned SPACE_DIM>
776{
777 assert(pModifier!=NULL);
778 assert(mpConductivityModifier==NULL); // shouldn't be called twice for example, or with two different modifiers (remove this assert
779 // if for whatever reason want to be able to overwrite modifiers)
780 mpConductivityModifier = pModifier;
781}
782
783// Explicit instantiation
784template class AbstractCardiacTissue<1,1>;
785template class AbstractCardiacTissue<1,2>;
786template class AbstractCardiacTissue<1,3>;
787template class AbstractCardiacTissue<2,2>;
788template 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)