Chaste Commit::6e4f5fe395bca70eb7641cf6e0e87f450383ca5a
Electrodes.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 "Electrodes.hpp"
37#include "DistributedTetrahedralMesh.hpp"
38#include "HeartConfig.hpp"
39
40#include <cmath>
41
42template<unsigned DIM>
44 : mpMesh(&rMesh),
45 mLeftElectrodeArea(0.0),
46 mRightElectrodeArea(0.0)
47{
48 unsigned axis_index;
49 double magnitude, duration;
50
52
53 assert(axis_index < DIM);
54 assert(duration > 0);
55 mEndTime = mStartTime + duration;
56 mAreActive = false; // consider electrodes initially switched off!
57
59 double global_min = bounding_box.rGetLowerCorner()[axis_index];
60 double global_max = bounding_box.rGetUpperCorner()[axis_index];
61
63
64
65 double input_flux;
66 double output_flux;
67
68 try
69 {
70 ComputeElectrodesAreasAndCheckEquality(axis_index, global_min, global_max);
71 input_flux = magnitude;
72 output_flux = -input_flux;
73
74 }
75 catch (Exception&)
76 {
77 // magnitude of second electrode scaled so that left_area*magnitude_left = -right_area*magnitude_right
78 input_flux = magnitude;
79 output_flux = -input_flux*mLeftElectrodeArea/mRightElectrodeArea;
80
81 // Paranoia. In case one of the areas is 0
82 assert( ! std::isnan(output_flux));
83 assert( output_flux != 0.0);
84 }
85
86 ConstBoundaryCondition<DIM>* p_bc_flux_in = new ConstBoundaryCondition<DIM>(input_flux);
87 ConstBoundaryCondition<DIM>* p_bc_flux_out = new ConstBoundaryCondition<DIM>(output_flux);
88
89 // loop over boundary elements and add a non-zero phi_e boundary condition (ie extracellular
90 // stimulus) if (assuming axis_index=0, etc) x=lowerValue (where x is the x-value of the centroid)
91 for (auto iter = mpMesh->GetBoundaryElementIteratorBegin();
93 iter++)
94 {
95 if (fabs((*iter)->CalculateCentroid()[axis_index] - global_min) < 1e-6)
96 {
97 mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_in, 1);
98 }
99
101 {
102 if (fabs((*iter)->CalculateCentroid()[axis_index] - global_max) < 1e-6)
103 {
104 mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_out, 1);
105 }
106 }
107 }
108
109 // set up mGroundedNodes using opposite surface ie second electrode is
110 // grounded
112 {
114 // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
115 this->mpBoundaryConditionsContainer->ResetDirichletCommunication();
116
118 iter != mpMesh->GetNodeIteratorEnd();
119 ++iter)
120 {
121 if (fabs((*iter).rGetLocation()[axis_index]-global_max) < 1e-6)
122 {
123 mpBoundaryConditionsContainer->AddDirichletBoundaryCondition(&(*iter), p_zero_bc, 1);
124 }
125 }
126
127 //Unused boundary conditions will not be deleted by the b.c. container
128 delete p_bc_flux_out;
129 }
130}
131
132template<unsigned DIM>
133boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,2> > Electrodes<DIM>::GetBoundaryConditionsContainer()
134{
135 //assert(mAreActive);
136 return mpBoundaryConditionsContainer;
137}
138
139template<unsigned DIM>
141{
142 // This smidge has to be the same as the one below
144 double smidge = 1e-10;
145 if (mAreActive && time>mEndTime-smidge)
146 {
147 mAreActive = false;
148 return true;
149 }
150
151 return false;
152}
153
154template<unsigned DIM>
156{
157 // This smidge has to be the same as the one above.
159 double smidge = 1e-10;
160 if (!mAreActive && time>=mStartTime && time<=mEndTime - smidge)
161 {
162 mAreActive = true;
163 return true;
164 }
165
166 return false;
167}
168
169template<unsigned DIM>
170void Electrodes<DIM>::ComputeElectrodesAreasAndCheckEquality(unsigned dimensionIndex, double lowerValue, double upperValue)
171{
172 //
173 // loop over boundary elements and determine areas of the electrode faces
174 //
175 double local_left_area = 0.0;
176 double local_right_area = 0.0;
177
178 c_vector<double,DIM> weighted_direction;
179 double jacobian_determinant;
180
181 for (auto iter = mpMesh->GetBoundaryElementIteratorBegin();
182 iter != mpMesh->GetBoundaryElementIteratorEnd();
183 iter++)
184 {
185 if (mpMesh->CalculateDesignatedOwnershipOfBoundaryElement((*iter)->GetIndex()))
186 {
187 if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - lowerValue) < 1e-6)
188 {
189 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
190 local_left_area += jacobian_determinant;
191 }
192
193 if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - upperValue) < 1e-6)
194 {
195 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
196 local_right_area += jacobian_determinant;
197 }
198 }
199 }
200
201 if (DIM == 3)
202 {
203 // if the dimension of the face is 1, the mapping is from the face to the canonical element [0,1], so the
204 // jacobian_determinants used above will be exactly the areas of the faces
205 // if the dimension of the face is 1, the mapping is from the face to the canonical element, the triangle
206 // with nodes (0,0), (0,1), (1,0), which has area 0.5, so scale the jacobian_determinants by 0.5 to
207 // get the face areas, ie scale the total area by 0.5:
208 // (Not technically needed as it is only the ratio of these that is used but since we are calling the variables
209 // 'area's we ought to be correct).
210 local_left_area /= 2.0;
211 local_right_area /= 2.0;
212 }
213
214 int mpi_ret = MPI_Allreduce(&local_left_area, &mLeftElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
215 UNUSED_OPT(mpi_ret);
216 assert(mpi_ret == MPI_SUCCESS);
217
218 mpi_ret = MPI_Allreduce(&local_right_area, &mRightElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
219 assert(mpi_ret == MPI_SUCCESS);
220
221 if (mLeftElectrodeArea != mRightElectrodeArea)
222 {
223 EXCEPTION("Electrodes have different area");
224 }
225}
226
227// Explicit instantiation
228template class Electrodes<1>;
229template class Electrodes<2>;
230template class Electrodes<3>;
231
232// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define UNUSED_OPT(var)
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
NodeIterator GetNodeIteratorEnd()
ChasteCuboid< SPACE_DIM > CalculateBoundingBox(const std::vector< Node< SPACE_DIM > * > &rNodes) const
NodeIterator GetNodeIteratorBegin(bool skipDeletedNodes=true)
BoundaryElementIterator GetBoundaryElementIteratorBegin() const
BoundaryElementIterator GetBoundaryElementIteratorEnd() const
const ChastePoint< SPACE_DIM > & rGetUpperCorner() const
const ChastePoint< SPACE_DIM > & rGetLowerCorner() const
bool SwitchOn(double time)
void ComputeElectrodesAreasAndCheckEquality(unsigned index, double lowerValue, double upperValue)
bool SwitchOff(double time)
double mStartTime
double mLeftElectrodeArea
bool mAreActive
boost::shared_ptr< BoundaryConditionsContainer< DIM, DIM, 2 > > mpBoundaryConditionsContainer
double mRightElectrodeArea
boost::shared_ptr< BoundaryConditionsContainer< DIM, DIM, 2 > > GetBoundaryConditionsContainer()
double mEndTime
AbstractTetrahedralMesh< DIM, DIM > * mpMesh
bool mGroundSecondElectrode
void GetElectrodeParameters(bool &rGroundSecondElectrode, unsigned &rIndex, double &rMagnitude, double &rStartTime, double &rDuration)
static HeartConfig * Instance()