Chaste Commit::1fd4e48e3990e67db148bc1bc4cf6991a0049d0c
Electrodes.cpp
1/*
2
3Copyright (c) 2005-2024, 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)
94 iter++)
95 {
96 if (fabs((*iter)->CalculateCentroid()[axis_index] - global_min) < 1e-6)
97 {
98 mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_in, 1);
99 }
100
102 {
103 if (fabs((*iter)->CalculateCentroid()[axis_index] - global_max) < 1e-6)
104 {
105 mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_out, 1);
106 }
107 }
108 }
109
110 // set up mGroundedNodes using opposite surface ie second electrode is
111 // grounded
113 {
115 // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
116 this->mpBoundaryConditionsContainer->ResetDirichletCommunication();
117
119 iter != mpMesh->GetNodeIteratorEnd();
120 ++iter)
121 {
122 if (fabs((*iter).rGetLocation()[axis_index]-global_max) < 1e-6)
123 {
124 mpBoundaryConditionsContainer->AddDirichletBoundaryCondition(&(*iter), p_zero_bc, 1);
125 }
126 }
127
128 //Unused boundary conditions will not be deleted by the b.c. container
129 delete p_bc_flux_out;
130 }
131}
132
133template<unsigned DIM>
134boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,2> > Electrodes<DIM>::GetBoundaryConditionsContainer()
135{
136 //assert(mAreActive);
137 return mpBoundaryConditionsContainer;
138}
139
140template<unsigned DIM>
142{
143 // This smidge has to be the same as the one below
145 double smidge = 1e-10;
146 if (mAreActive && time>mEndTime-smidge)
147 {
148 mAreActive = false;
149 return true;
150 }
151
152 return false;
153}
154
155template<unsigned DIM>
157{
158 // This smidge has to be the same as the one above.
160 double smidge = 1e-10;
161 if (!mAreActive && time>=mStartTime && time<=mEndTime - smidge)
162 {
163 mAreActive = true;
164 return true;
165 }
166
167 return false;
168}
169
170template<unsigned DIM>
171void Electrodes<DIM>::ComputeElectrodesAreasAndCheckEquality(unsigned dimensionIndex, double lowerValue, double upperValue)
172{
173 //
174 // loop over boundary elements and determine areas of the electrode faces
175 //
176 double local_left_area = 0.0;
177 double local_right_area = 0.0;
178
179 c_vector<double,DIM> weighted_direction;
180 double jacobian_determinant;
181
183 = mpMesh->GetBoundaryElementIteratorBegin();
184 iter != mpMesh->GetBoundaryElementIteratorEnd();
185 iter++)
186 {
187 if (mpMesh->CalculateDesignatedOwnershipOfBoundaryElement((*iter)->GetIndex()))
188 {
189 if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - lowerValue) < 1e-6)
190 {
191 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
192 local_left_area += jacobian_determinant;
193 }
194
195 if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - upperValue) < 1e-6)
196 {
197 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
198 local_right_area += jacobian_determinant;
199 }
200 }
201 }
202
203 if (DIM == 3)
204 {
205 // if the dimension of the face is 1, the mapping is from the face to the canonical element [0,1], so the
206 // jacobian_determinants used above will be exactly the areas of the faces
207 // if the dimension of the face is 1, the mapping is from the face to the canonical element, the triangle
208 // with nodes (0,0), (0,1), (1,0), which has area 0.5, so scale the jacobian_determinants by 0.5 to
209 // get the face areas, ie scale the total area by 0.5:
210 // (Not technically needed as it is only the ratio of these that is used but since we are calling the variables
211 // 'area's we ought to be correct).
212 local_left_area /= 2.0;
213 local_right_area /= 2.0;
214 }
215
216 int mpi_ret = MPI_Allreduce(&local_left_area, &mLeftElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
217 UNUSED_OPT(mpi_ret);
218 assert(mpi_ret == MPI_SUCCESS);
219
220 mpi_ret = MPI_Allreduce(&local_right_area, &mRightElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
221 assert(mpi_ret == MPI_SUCCESS);
222
223 if (mLeftElectrodeArea != mRightElectrodeArea)
224 {
225 EXCEPTION("Electrodes have different area");
226 }
227}
228
229// Explicit instantiation
230template class Electrodes<1>;
231template class Electrodes<2>;
232template class Electrodes<3>;
233
234// 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)
std::vector< BoundaryElement< ELEMENT_DIM-1, SPACE_DIM > * >::const_iterator BoundaryElementIterator
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()