HoneycombMeshGenerator.cpp

00001 
00002 /*
00003 
00004 Copyright (C) University of Oxford, 2005-2010
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Chaste is free software: you can redistribute it and/or modify it
00013 under the terms of the GNU Lesser General Public License as published
00014 by the Free Software Foundation, either version 2.1 of the License, or
00015 (at your option) any later version.
00016 
00017 Chaste is distributed in the hope that it will be useful, but WITHOUT
00018 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00019 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00020 License for more details. The offer of Chaste under the terms of the
00021 License is subject to the License being interpreted in accordance with
00022 English Law and subject to any action against the University of Oxford
00023 being under the jurisdiction of the English Courts.
00024 
00025 You should have received a copy of the GNU Lesser General Public License
00026 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00027 
00028 */
00029 #include "HoneycombMeshGenerator.hpp"
00030 #include "RandomNumberGenerator.hpp"
00031 #include "UblasCustomFunctions.hpp"
00032 
00033 
00034 HoneycombMeshGenerator::HoneycombMeshGenerator(unsigned numNodesAlongWidth, unsigned numNodesAlongLength, unsigned ghosts, bool cylindrical, double scaleFactor)
00035   : mpMesh(NULL),
00036     mCryptWidth(numNodesAlongWidth*scaleFactor),
00037     mNumCellWidth(numNodesAlongWidth), //*1 because cells are considered to be size one
00038     mNumCellLength(numNodesAlongLength),
00039     mCylindrical(cylindrical)
00040 {
00041     mGhostNodeIndices.empty();
00042 
00043     std::stringstream pid; // Gives a unique filename
00044     pid << getpid();
00045     mMeshFilename = "2D_temporary_periodic_crypt_mesh_" + pid.str();
00046     Make2dPeriodicCryptMesh(mCryptWidth, ghosts);
00047     OutputFileHandler output_file_handler("");
00048     std::string output_dir = output_file_handler.GetOutputDirectoryFullPath();
00049 
00050     TrianglesMeshReader<2,2> mesh_reader(output_dir + mMeshFilename);
00051 
00052     if (!mCylindrical)
00053     {
00054         mpMesh = new MutableMesh<2,2>;
00055         mpMesh->ConstructFromMeshReader(mesh_reader);
00056     }
00057     else
00058     {
00059         mpMesh = new Cylindrical2dMesh(mCryptWidth);
00060         mpMesh->ConstructFromMeshReader(mesh_reader);
00061         NodeMap map(mpMesh->GetNumNodes());
00062         mpMesh->ReMesh(map); // This makes the mesh cylindrical (uses Triangle library mode inside this ReMesh call).
00063     }
00064 
00065     // Delete the temporary files
00066     std::string command = "rm " + output_dir + mMeshFilename + ".*";
00067     int return_value = system(command.c_str());
00068     if (return_value != 0)
00069     {
00070         // Can't figure out how to make this throw but seems as if it should be here?
00071         #define COVERAGE_IGNORE
00072         EXCEPTION("HoneycombMeshGenerator cannot delete temporary files\n");
00073         #undef COVERAGE_IGNORE
00074     }
00075 
00076     TissueConfig::Instance()->SetCryptLength(mCryptDepth);
00077     TissueConfig::Instance()->SetCryptWidth(mCryptWidth);
00078 }
00079 
00080 
00081 HoneycombMeshGenerator::~HoneycombMeshGenerator()
00082 {
00083     delete mpMesh;
00084 }
00085 
00086 
00087 MutableMesh<2,2>* HoneycombMeshGenerator::GetMesh()
00088 {
00089     if (mCylindrical)
00090     {
00091         EXCEPTION("A cylindrical mesh was created but a normal mesh is being requested.");
00092     }
00093     return mpMesh;
00094 }
00095 
00096 
00097 Cylindrical2dMesh* HoneycombMeshGenerator::GetCylindricalMesh()
00098 {
00099     if (!mCylindrical)
00100     {
00101         EXCEPTION("A normal mesh was created but a cylindrical mesh is being requested.");
00102     }
00103     return (Cylindrical2dMesh*) mpMesh;
00104 }
00105 
00106 std::vector<unsigned> HoneycombMeshGenerator::GetCellLocationIndices()
00107 {
00108     std::vector<unsigned> location_indices;
00109 
00110     for (unsigned i=0; i<mpMesh->GetNumNodes(); i++)
00111     {
00112         if (mGhostNodeIndices.find(i)==mGhostNodeIndices.end())
00113         {
00114             location_indices.push_back(i);
00115         }
00116     }
00117     return location_indices;
00118 }
00119 
00120 MutableMesh<2,2>* HoneycombMeshGenerator::GetCircularMesh(double radius)
00121 {
00122     assert(!mCylindrical); // Following call only safe if is not a cylindrical mesh
00123 
00124     // Centre the mesh at (0,0)
00125     c_vector<double,2> centre = zero_vector<double>(2);
00126     for (unsigned i=0; i<mpMesh->GetNumNodes(); i++)
00127     {
00128         centre += mpMesh->GetNode(i)->rGetLocation();
00129     }
00130     centre /= (double)mpMesh->GetNumNodes();
00131 
00132     mpMesh->Translate(-centre[0], -centre[1]);
00133 
00134     // Iterate over nodes, deleting any that lie more
00135     // than the specified radius from (0,0)
00136     for (unsigned i=0; i<mpMesh->GetNumAllNodes(); i++)
00137     {
00138         if ( norm_2(mpMesh->GetNode(i)->rGetLocation()) >= radius)
00139         {
00140             mpMesh->DeleteNodePriorToReMesh(i);
00141         }
00142         else
00143         {
00144             // Jiggle the data
00145             c_vector<double,2>& r_location = mpMesh->GetNode(i)->rGetModifiableLocation();
00146             c_vector<double,2> shift;
00147             RandomNumberGenerator* p_gen = RandomNumberGenerator::Instance();
00148             double max_jiggle = radius*5e-6;
00149             shift[0] = max_jiggle*(p_gen->ranf()-0.5);
00150             shift[1] = max_jiggle*(p_gen->ranf()-0.5);
00151             r_location += shift;
00152         }
00153     }
00154 
00155     // Remesh
00156     NodeMap map(mpMesh->GetNumNodes());
00157     mpMesh->ReMesh(map);
00158 
00159     return mpMesh;
00160 }
00161 
00162 void HoneycombMeshGenerator::Make2dPeriodicCryptMesh(double width, unsigned ghosts)
00163 {
00164     OutputFileHandler output_file_handler("");
00165 
00166     if (PetscTools::AmMaster())
00167     {
00168         out_stream p_node_file = output_file_handler.OpenOutputFile(mMeshFilename+".node");
00169         (*p_node_file) << std::scientific;
00170 
00171         out_stream p_elem_file = output_file_handler.OpenOutputFile(mMeshFilename+".ele");
00172         (*p_elem_file) << std::scientific;
00173 
00174         unsigned numNodesAlongWidth = mNumCellWidth;
00175         unsigned numNodesAlongLength = mNumCellLength;
00176         double horizontal_spacing = width / (double)numNodesAlongWidth;
00177         double vertical_spacing = (sqrt(3)/2)*horizontal_spacing;
00178 
00179         // This line needed to define ghost nodes later...
00180         mCryptDepth = (double)(numNodesAlongLength) * vertical_spacing;
00181 
00182         // Add in the ghost nodes...
00183         if (!mCylindrical)
00184         {
00185             numNodesAlongWidth = numNodesAlongWidth + 2*ghosts;
00186         }
00187         numNodesAlongLength = numNodesAlongLength + 2*ghosts;
00188 
00189         unsigned num_nodes            = numNodesAlongWidth*numNodesAlongLength;
00190         unsigned num_elem_along_width = numNodesAlongWidth-1;
00191         unsigned num_elem_along_length = numNodesAlongLength-1;
00192         unsigned num_elem             = 2*num_elem_along_width*num_elem_along_length;
00193         unsigned num_edges            = 3*num_elem_along_width*num_elem_along_length + num_elem_along_width + num_elem_along_length;
00194 
00195         double x0 = -horizontal_spacing*ghosts;
00196         if (mCylindrical)
00197         {
00198             x0 = 0;
00199         }
00200         double y0 = -vertical_spacing*ghosts;
00201         mBottom = -vertical_spacing*ghosts;
00202         mTop = mBottom + vertical_spacing*(numNodesAlongLength-1);
00203 
00204         (*p_node_file) << num_nodes << "\t2\t0\t1" << std::endl;
00205         unsigned node = 0;
00206 
00207         for (unsigned i=0; i<numNodesAlongLength; i++)
00208         {
00209             for (unsigned j=0; j<numNodesAlongWidth; j++)
00210             {
00211                 if ( i<ghosts || i>=(ghosts+mNumCellLength))
00212                 {
00213                     mGhostNodeIndices.insert(node);
00214                 }
00215                 else if ( !mCylindrical && (j < ghosts || j >= (ghosts+mNumCellWidth)))
00216                 {
00217                     mGhostNodeIndices.insert(node);
00218                 }
00219                 unsigned boundary = 0;
00220                 if ((i==0) || (i==numNodesAlongLength-1))
00221                 {
00222                     boundary = 1;
00223                 }
00224                 if (!mCylindrical)
00225                 {
00226                     if ((j==0) || (j==numNodesAlongWidth-1))
00227                     {
00228                         boundary = 1;
00229                     }
00230                 }
00231 
00232                 double x = x0 + horizontal_spacing*((double)j + 0.25*(1.0+ SmallPow(-1,i+1)));
00233                 double y = y0 + vertical_spacing*(double)i;
00234 
00235                 // Avoid floating point errors which upset CryptSimulation2d
00236                 if ( (y<0.0) && (y>-1e-12) )
00237                 {
00238                     // Difficult to cover - just corrects floating point errors that have occurred from time to time!
00239                     #define COVERAGE_IGNORE
00240                     y = 0.0;
00241                     #undef COVERAGE_IGNORE
00242                 }
00243 
00244                 (*p_node_file) << node++ << "\t" << x << "\t" << y << "\t" << boundary << std::endl;
00245             }
00246         }
00247         p_node_file->close();
00248 
00249         out_stream p_edge_file = output_file_handler.OpenOutputFile(mMeshFilename+".edge");
00250         (*p_node_file) << std::scientific;
00251 
00252         (*p_elem_file) << num_elem << "\t3\t0" << std::endl;
00253         (*p_edge_file) << num_edges << "\t1" << std::endl;
00254 
00255         unsigned elem = 0;
00256         unsigned edge = 0;
00257         for (unsigned i=0; i<num_elem_along_length; i++)
00258         {
00259             for (unsigned j=0; j < num_elem_along_width; j++)
00260             {
00261                 unsigned node0 =     i*numNodesAlongWidth + j;
00262                 unsigned node1 =     i*numNodesAlongWidth + j+1;
00263                 unsigned node2 = (i+1)*numNodesAlongWidth + j;
00264 
00265                 if (i%2 != 0)
00266                 {
00267                     node2 = node2 + 1;
00268                 }
00269 
00270                 (*p_elem_file) << elem++ << "\t" << node0 << "\t" << node1 << "\t" << node2 << std::endl;
00271 
00272                 unsigned horizontal_edge_is_boundary_edge = 0;
00273                 unsigned vertical_edge_is_boundary_edge = 0;
00274                 if (i==0)
00275                 {
00276                     horizontal_edge_is_boundary_edge = 1;
00277                 }
00278                 if (j==0 && i%2==0 && !mCylindrical)
00279                 {
00280                     vertical_edge_is_boundary_edge = 1;
00281                 }
00282 
00283                 (*p_edge_file) << edge++ << "\t" << node0 << "\t" << node1 <<  "\t" << horizontal_edge_is_boundary_edge << std::endl;
00284                 (*p_edge_file) << edge++ << "\t" << node1 << "\t" << node2 <<  "\t" << 0 << std::endl;
00285                 (*p_edge_file) << edge++ << "\t" << node2 << "\t" << node0 <<  "\t" << vertical_edge_is_boundary_edge << std::endl;
00286 
00287                 node0 = i*numNodesAlongWidth + j + 1;
00288 
00289                 if (i%2 != 0)
00290                 {
00291                     node0 = node0 - 1;
00292                 }
00293                 node1 = (i+1)*numNodesAlongWidth + j+1;
00294                 node2 = (i+1)*numNodesAlongWidth + j;
00295 
00296                 (*p_elem_file) << elem++ << "\t" << node0 << "\t" << node1 << "\t" << node2 << std::endl;
00297             }
00298         }
00299 
00300         for (unsigned i=0; i<num_elem_along_length; i++)
00301         {
00302             unsigned node0, node1;
00303 
00304             if (i%2==0)
00305             {
00306                  node0 = (i+1)*numNodesAlongWidth - 1;
00307                  node1 = (i+2)*numNodesAlongWidth - 1;
00308             }
00309             else
00310             {
00311                 node0 = (i+1)*numNodesAlongWidth;
00312                 node1 = (i)*numNodesAlongWidth;
00313             }
00314             (*p_edge_file) << edge++ << "\t" << node0 << "\t" << node1 << "\t" << 1 << std::endl;
00315         }
00316 
00317         for (unsigned j=0; j<num_elem_along_width; j++)
00318         {
00319             unsigned node0 = numNodesAlongWidth*(numNodesAlongLength-1) + j;
00320             unsigned node1 = numNodesAlongWidth*(numNodesAlongLength-1) + j+1;
00321             (*p_edge_file) << edge++ << "\t" << node1 << "\t" << node0 << "\t" << 1 << std::endl;
00322         }
00323 
00324         p_elem_file->close();
00325         p_edge_file->close();
00326     }
00327 
00328     // Wait for the new mesh to be available
00329     PetscTools::Barrier();
00330 }

Generated by  doxygen 1.6.2