Chaste Release::3.1
HoneycombMeshGenerator.cpp
00001 
00002 /*
00003 
00004 Copyright (c) 2005-2012, University of Oxford.
00005 All rights reserved.
00006 
00007 University of Oxford means the Chancellor, Masters and Scholars of the
00008 University of Oxford, having an administrative office at Wellington
00009 Square, Oxford OX1 2JD, UK.
00010 
00011 This file is part of Chaste.
00012 
00013 Redistribution and use in source and binary forms, with or without
00014 modification, are permitted provided that the following conditions are met:
00015  * Redistributions of source code must retain the above copyright notice,
00016    this list of conditions and the following disclaimer.
00017  * Redistributions in binary form must reproduce the above copyright notice,
00018    this list of conditions and the following disclaimer in the documentation
00019    and/or other materials provided with the distribution.
00020  * Neither the name of the University of Oxford nor the names of its
00021    contributors may be used to endorse or promote products derived from this
00022    software without specific prior written permission.
00023 
00024 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00025 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00026 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00027 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00028 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00029 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00030 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00031 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00033 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00034 
00035 */
00036 
00037 #include "HoneycombMeshGenerator.hpp"
00038 
00039 #include <boost/foreach.hpp>
00040 #include "TrianglesMeshReader.hpp"
00041 #include "OutputFileHandler.hpp"
00042 #include "RandomNumberGenerator.hpp"
00043 #include "MathsCustomFunctions.hpp"
00044 
00045 HoneycombMeshGenerator::HoneycombMeshGenerator(unsigned numNodesAlongWidth, unsigned numNodesAlongLength, unsigned ghosts, double scaleFactor)
00046   : mpMesh(NULL),
00047     mDomainWidth(numNodesAlongWidth*scaleFactor),
00048     mNumCellWidth(numNodesAlongWidth), //*1 because cells are considered to be size one
00049     mNumCellLength(numNodesAlongLength)
00050 {
00051     // The code below won't work in parallel
00052     assert(PetscTools::IsSequential());
00053 
00054     // An older version of the constructor might allow the wrong argument through to the scale factor
00055     assert(scaleFactor > 0.0);
00056 
00057     // Get a unique mesh filename
00058     std::stringstream pid;
00059     pid << getpid();
00060     mMeshFilename = "2D_temporary_honeycomb_mesh_" + pid.str();
00061 
00062     mGhostNodeIndices.empty();
00063 
00064     OutputFileHandler output_file_handler("");
00065     std::string output_dir = output_file_handler.GetOutputDirectoryFullPath();
00066 
00067     unsigned num_nodes_along_width = mNumCellWidth;
00068     unsigned num_nodes_along_length = mNumCellLength;
00069     double horizontal_spacing = mDomainWidth / (double)num_nodes_along_width;
00070     double vertical_spacing = (sqrt(3)/2)*horizontal_spacing;
00071 
00072     // This line is needed to define ghost nodes later
00073     mDomainDepth = (double)(num_nodes_along_length) * vertical_spacing;
00074 
00075     // Take account of ghost nodes
00076     num_nodes_along_width +=  2*ghosts;
00077     num_nodes_along_length += 2*ghosts;
00078 
00079     unsigned num_nodes            = num_nodes_along_width*num_nodes_along_length;
00080     unsigned num_elem_along_width = num_nodes_along_width-1;
00081     unsigned num_elem_along_length = num_nodes_along_length-1;
00082     unsigned num_elem             = 2*num_elem_along_width*num_elem_along_length;
00083     unsigned num_edges            = 3*num_elem_along_width*num_elem_along_length + num_elem_along_width + num_elem_along_length;
00084 
00085     double x0 = -horizontal_spacing*ghosts;
00086     double y0 = -vertical_spacing*ghosts;
00087 
00088     mBottom = -vertical_spacing*ghosts;
00089     mTop = mBottom + vertical_spacing*(num_nodes_along_length-1);
00090 
00091     // Write node file
00092     out_stream p_node_file = output_file_handler.OpenOutputFile(mMeshFilename+".node");
00093     (*p_node_file) << std::scientific;
00094     (*p_node_file) << std::setprecision(20);
00095     (*p_node_file) << num_nodes << "\t2\t0\t1" << std::endl;
00096 
00097     unsigned node = 0;
00098     for (unsigned i=0; i<num_nodes_along_length; i++)
00099     {
00100         for (unsigned j=0; j<num_nodes_along_width; j++)
00101         {
00102             if (i<ghosts || i>=(ghosts+mNumCellLength))
00103             {
00104                 mGhostNodeIndices.insert(node);
00105             }
00106             else if (j < ghosts || j >= (ghosts+mNumCellWidth))
00107             {
00108                 mGhostNodeIndices.insert(node);
00109             }
00110             unsigned boundary = 0;
00111             if (i==0 || i==num_nodes_along_length-1 || j==0 || j==num_nodes_along_width-1)
00112             {
00113                 boundary = 1;
00114             }
00115 
00116             double x = x0 + horizontal_spacing*((double)j + 0.25*(1.0+ SmallPow(-1,i+1)));
00117             double y = y0 + vertical_spacing*(double)i;
00118 
00119             // Avoid floating point errors which upset OffLatticeSimulation
00120             if ( (y<0.0) && (y>-1e-12) )
00121             {
00122                 // Difficult to cover - just corrects floating point errors that have occurred from time to time!
00123                 #define COVERAGE_IGNORE
00124                 y = 0.0;
00125                 #undef COVERAGE_IGNORE
00126             }
00127 
00128             (*p_node_file) << node++ << "\t" << x << "\t" << y << "\t" << boundary << std::endl;
00129         }
00130     }
00131     p_node_file->close();
00132 
00133     // Write element file and edge file
00134     out_stream p_elem_file = output_file_handler.OpenOutputFile(mMeshFilename+".ele");
00135     (*p_elem_file) << std::scientific;
00136 
00137     out_stream p_edge_file = output_file_handler.OpenOutputFile(mMeshFilename+".edge");
00138     (*p_node_file) << std::scientific;
00139 
00140     (*p_elem_file) << num_elem << "\t3\t0" << std::endl;
00141     (*p_edge_file) << num_edges << "\t1" << std::endl;
00142 
00143     unsigned elem = 0;
00144     unsigned edge = 0;
00145     for (unsigned i=0; i<num_elem_along_length; i++)
00146     {
00147         for (unsigned j=0; j < num_elem_along_width; j++)
00148         {
00149             unsigned node0 =     i*num_nodes_along_width + j;
00150             unsigned node1 =     i*num_nodes_along_width + j+1;
00151             unsigned node2 = (i+1)*num_nodes_along_width + j;
00152 
00153             if (i%2 != 0)
00154             {
00155                 node2 = node2 + 1;
00156             }
00157 
00158             (*p_elem_file) << elem++ << "\t" << node0 << "\t" << node1 << "\t" << node2 << std::endl;
00159 
00160             unsigned horizontal_edge_is_boundary_edge = 0;
00161             unsigned vertical_edge_is_boundary_edge = 0;
00162             if (i==0)
00163             {
00164                 horizontal_edge_is_boundary_edge = 1;
00165             }
00166             if (j==0 && i%2==0)
00167             {
00168                 vertical_edge_is_boundary_edge = 1;
00169             }
00170 
00171             (*p_edge_file) << edge++ << "\t" << node0 << "\t" << node1 << "\t" << horizontal_edge_is_boundary_edge << std::endl;
00172             (*p_edge_file) << edge++ << "\t" << node1 << "\t" << node2 << "\t" << 0 << std::endl;
00173             (*p_edge_file) << edge++ << "\t" << node2 << "\t" << node0 << "\t" << vertical_edge_is_boundary_edge << std::endl;
00174 
00175             node0 = i*num_nodes_along_width + j + 1;
00176 
00177             if (i%2 != 0)
00178             {
00179                 node0 = node0 - 1;
00180             }
00181             node1 = (i+1)*num_nodes_along_width + j+1;
00182             node2 = (i+1)*num_nodes_along_width + j;
00183 
00184             (*p_elem_file) << elem++ << "\t" << node0 << "\t" << node1 << "\t" << node2 << std::endl;
00185         }
00186     }
00187 
00188     for (unsigned i=0; i<num_elem_along_length; i++)
00189     {
00190         unsigned node0, node1;
00191 
00192         if (i%2==0)
00193         {
00194              node0 = (i+1)*num_nodes_along_width - 1;
00195              node1 = (i+2)*num_nodes_along_width - 1;
00196         }
00197         else
00198         {
00199             node0 = (i+1)*num_nodes_along_width;
00200             node1 = (i)*num_nodes_along_width;
00201         }
00202         (*p_edge_file) << edge++ << "\t" << node0 << "\t" << node1 << "\t" << 1 << std::endl;
00203     }
00204 
00205     for (unsigned j=0; j<num_elem_along_width; j++)
00206     {
00207         unsigned node0 = num_nodes_along_width*(num_nodes_along_length-1) + j;
00208         unsigned node1 = num_nodes_along_width*(num_nodes_along_length-1) + j+1;
00209         (*p_edge_file) << edge++ << "\t" << node1 << "\t" << node0 << "\t" << 1 << std::endl;
00210     }
00211 
00212     p_elem_file->close();
00213     p_edge_file->close();
00214 
00215 
00216     // Having written the mesh to file, now construct it using TrianglesMeshReader
00217     TrianglesMeshReader<2,2> mesh_reader(output_dir + mMeshFilename);
00218     mpMesh = new MutableMesh<2,2>;
00219     mpMesh->ConstructFromMeshReader(mesh_reader);
00220 
00221     // Delete the temporary files
00222     FileFinder output_dir_finder(output_dir);
00223     BOOST_FOREACH(FileFinder temp_file, output_dir_finder.FindMatches(mMeshFilename + ".*"))
00224     {
00225         temp_file.Remove(true);
00226     }
00227 
00228     // The original files have been deleted, it is better if the mesh object forgets about them
00229     mpMesh->SetMeshHasChangedSinceLoading();
00230 }
00231 
00232 HoneycombMeshGenerator::~HoneycombMeshGenerator()
00233 {
00234     delete mpMesh;
00235 }
00236 
00237 MutableMesh<2,2>* HoneycombMeshGenerator::GetMesh()
00238 {
00239     return mpMesh;
00240 }
00241 
00242 std::vector<unsigned> HoneycombMeshGenerator::GetCellLocationIndices()
00243 {
00244     std::vector<unsigned> location_indices;
00245 
00246     for (unsigned i=0; i<mpMesh->GetNumNodes(); i++)
00247     {
00248         if (mGhostNodeIndices.find(i)==mGhostNodeIndices.end())
00249         {
00250             location_indices.push_back(i);
00251         }
00252     }
00253     return location_indices;
00254 }
00255 
00256 MutableMesh<2,2>* HoneycombMeshGenerator::GetCircularMesh(double radius)
00257 {
00258     if (!mGhostNodeIndices.empty())
00259     {
00260         EXCEPTION("Cannot call GetCircularMesh on a HoneycombMesh with ghost nodes");
00261     }
00262 
00263     // Centre the mesh at (0,0)
00264     c_vector<double,2> centre = zero_vector<double>(2);
00265     for (unsigned i=0; i<mpMesh->GetNumNodes(); i++)
00266     {
00267         centre += mpMesh->GetNode(i)->rGetLocation();
00268     }
00269     centre /= (double)mpMesh->GetNumNodes();
00270 
00271     mpMesh->Translate(-centre[0], -centre[1]);
00272 
00273     // Iterate over nodes, deleting any that lie more than the specified radius from (0,0)
00274     for (unsigned i=0; i<mpMesh->GetNumAllNodes(); i++)
00275     {
00276         if (norm_2(mpMesh->GetNode(i)->rGetLocation()) >= radius)
00277         {
00278             mpMesh->DeleteNodePriorToReMesh(i);
00279         }
00280         else
00281         {
00282             // Jiggle the data
00283             c_vector<double,2>& r_location = mpMesh->GetNode(i)->rGetModifiableLocation();
00284             c_vector<double,2> shift;
00285             RandomNumberGenerator* p_gen = RandomNumberGenerator::Instance();
00286             double max_jiggle = radius*5e-6;
00287             shift[0] = max_jiggle*(p_gen->ranf()-0.5);
00288             shift[1] = max_jiggle*(p_gen->ranf()-0.5);
00289             r_location += shift;
00290         }
00291     }
00292 
00293     // Remesh
00294     NodeMap map(mpMesh->GetNumNodes());
00295     mpMesh->ReMesh(map);
00296 
00297     return mpMesh;
00298 }
00299 
00300 double HoneycombMeshGenerator::GetDomainDepth()
00301 {
00302     return mDomainDepth;
00303 }
00304 
00305 double HoneycombMeshGenerator::GetDomainWidth()
00306 {
00307     return mDomainWidth;
00308 }