Chaste  Release::3.4
AbstractContinuumMechanicsAssembler.hpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, 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 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #ifndef ABSTRACTCONTINUUMMECHANICSASSEMBLER_HPP_
37 #define ABSTRACTCONTINUUMMECHANICSASSEMBLER_HPP_
38 
39 #include "AbstractFeAssemblerInterface.hpp"
40 #include "AbstractTetrahedralMesh.hpp"
41 #include "QuadraticMesh.hpp"
42 #include "DistributedQuadraticMesh.hpp"
43 #include "LinearBasisFunction.hpp"
44 #include "QuadraticBasisFunction.hpp"
45 #include "ReplicatableVector.hpp"
46 #include "DistributedVector.hpp"
47 #include "PetscTools.hpp"
48 #include "PetscVecTools.hpp"
49 #include "PetscMatTools.hpp"
50 #include "GaussianQuadratureRule.hpp"
51 
52 
86 template<unsigned DIM, bool CAN_ASSEMBLE_VECTOR, bool CAN_ASSEMBLE_MATRIX>
87 class AbstractContinuumMechanicsAssembler : public AbstractFeAssemblerInterface<CAN_ASSEMBLE_VECTOR,CAN_ASSEMBLE_MATRIX>
88 {
92  static const bool BLOCK_SYMMETRIC_MATRIX = true; //generalise to non-block symmetric matrices later (when needed maybe)
93 
95  static const unsigned NUM_VERTICES_PER_ELEMENT = DIM+1;
96 
98  static const unsigned NUM_NODES_PER_ELEMENT = (DIM+1)*(DIM+2)/2; // assuming quadratic
99 
104 
107 
108 protected:
111 
114 
121  void DoAssemble();
122 
123 
143  virtual c_matrix<double,SPATIAL_BLOCK_SIZE_ELEMENTAL,SPATIAL_BLOCK_SIZE_ELEMENTAL> ComputeSpatialSpatialMatrixTerm(
144  c_vector<double, NUM_NODES_PER_ELEMENT>& rQuadPhi,
145  c_matrix<double, DIM, NUM_NODES_PER_ELEMENT>& rGradQuadPhi,
146  c_vector<double,DIM>& rX,
147  Element<DIM,DIM>* pElement)
148  {
150  }
151 
174  virtual c_matrix<double,SPATIAL_BLOCK_SIZE_ELEMENTAL,PRESSURE_BLOCK_SIZE_ELEMENTAL> ComputeSpatialPressureMatrixTerm(
175  c_vector<double, NUM_NODES_PER_ELEMENT>& rQuadPhi,
176  c_matrix<double, DIM, NUM_NODES_PER_ELEMENT>& rGradQuadPhi,
177  c_vector<double, NUM_VERTICES_PER_ELEMENT>& rLinearPhi,
178  c_matrix<double, DIM, NUM_VERTICES_PER_ELEMENT>& rGradLinearPhi,
179  c_vector<double,DIM>& rX,
180  Element<DIM,DIM>* pElement)
181  {
183  }
184 
185 
205  virtual c_matrix<double,PRESSURE_BLOCK_SIZE_ELEMENTAL,PRESSURE_BLOCK_SIZE_ELEMENTAL> ComputePressurePressureMatrixTerm(
206  c_vector<double, NUM_VERTICES_PER_ELEMENT>& rLinearPhi,
207  c_matrix<double, DIM, NUM_VERTICES_PER_ELEMENT>& rGradLinearPhi,
208  c_vector<double,DIM>& rX,
209  Element<DIM,DIM>* pElement)
210  {
212  }
213 
214 
237  virtual c_vector<double,SPATIAL_BLOCK_SIZE_ELEMENTAL> ComputeSpatialVectorTerm(
238  c_vector<double, NUM_NODES_PER_ELEMENT>& rQuadPhi,
239  c_matrix<double, DIM, NUM_NODES_PER_ELEMENT>& rGradQuadPhi,
240  c_vector<double,DIM>& rX,
241  Element<DIM,DIM>* pElement)
242  {
243  return zero_vector<double>(SPATIAL_BLOCK_SIZE_ELEMENTAL);
244  }
245 
246 
269  virtual c_vector<double,PRESSURE_BLOCK_SIZE_ELEMENTAL> ComputePressureVectorTerm(
270  c_vector<double, NUM_VERTICES_PER_ELEMENT>& rLinearPhi,
271  c_matrix<double, DIM, NUM_VERTICES_PER_ELEMENT>& rGradLinearPhi,
272  c_vector<double,DIM>& rX,
273  Element<DIM,DIM>* pElement)
274  {
275  return zero_vector<double>(PRESSURE_BLOCK_SIZE_ELEMENTAL);
276  }
277 
278 
279 
291  void AssembleOnElement(Element<DIM, DIM>& rElement,
292  c_matrix<double, STENCIL_SIZE, STENCIL_SIZE >& rAElem,
293  c_vector<double, STENCIL_SIZE>& rBElem);
294 
295 public:
300  : AbstractFeAssemblerInterface<CAN_ASSEMBLE_VECTOR,CAN_ASSEMBLE_MATRIX>(),
301  mpMesh(pMesh)
302  {
303  assert(pMesh);
304 
305  //Check that the mesh is Quadratic
306  QuadraticMesh<DIM>* p_quad_mesh = dynamic_cast<QuadraticMesh<DIM>* >(pMesh);
307  DistributedQuadraticMesh<DIM>* p_distributed_quad_mesh = dynamic_cast<DistributedQuadraticMesh<DIM>* >(pMesh);
308 
309  if(p_quad_mesh == NULL && p_distributed_quad_mesh == NULL)
310  {
311  EXCEPTION("Continuum mechanics assemblers require a quadratic mesh");
312  }
313 
314  // In general the Jacobian for a mechanics problem is non-polynomial.
315  // We therefore use the highest order integration rule available
317  }
318 
319 
320 // void SetCurrentSolution(Vec currentSolution);
321 
322 
327  {
328  delete mpQuadRule;
329  }
330 };
331 
332 
334 //template<unsigned DIM, bool CAN_ASSEMBLE_VECTOR, bool CAN_ASSEMBLE_MATRIX>
335 //void AbstractContinuumMechanicsAssembler<DIM,CAN_ASSEMBLE_VECTOR,CAN_ASSEMBLE_MATRIX>::SetCurrentSolution(Vec currentSolution)
336 //{
337 // assert(currentSolution != NULL);
338 //
339 // // Replicate the current solution and store so can be used in AssembleOnElement
340 // HeartEventHandler::BeginEvent(HeartEventHandler::COMMUNICATION);
341 // mCurrentSolutionOrGuessReplicated.ReplicatePetscVector(currentSolution);
342 // HeartEventHandler::EndEvent(HeartEventHandler::COMMUNICATION);
343 //
344 // // The AssembleOnElement type methods will determine if a current solution or
345 // // current guess exists by looking at the size of the replicated vector, so
346 // // check the size is zero if there isn't a current solution.
347 // assert(mCurrentSolutionOrGuessReplicated.GetSize() > 0);
348 //}
349 
350 template<unsigned DIM, bool CAN_ASSEMBLE_VECTOR, bool CAN_ASSEMBLE_MATRIX>
352 {
353  assert(this->mAssembleMatrix || this->mAssembleVector);
354  if (this->mAssembleMatrix)
355  {
356  if(this->mMatrixToAssemble==NULL)
357  {
358  EXCEPTION("Matrix to be assembled has not been set");
359  }
360  if( PetscMatTools::GetSize(this->mMatrixToAssemble) != (DIM+1)*mpMesh->GetNumNodes() )
361  {
362  EXCEPTION("Matrix provided to be assembled has size " << PetscMatTools::GetSize(this->mMatrixToAssemble) << ", not expected size of " << (DIM+1)*mpMesh->GetNumNodes() << " ((dim+1)*num_nodes)");
363  }
364  }
365 
366  if (this->mAssembleVector)
367  {
368  if(this->mVectorToAssemble==NULL)
369  {
370  EXCEPTION("Vector to be assembled has not been set");
371  }
372  if( PetscVecTools::GetSize(this->mVectorToAssemble) != (DIM+1)*mpMesh->GetNumNodes() )
373  {
374  EXCEPTION("Vector provided to be assembled has size " << PetscVecTools::GetSize(this->mVectorToAssemble) << ", not expected size of " << (DIM+1)*mpMesh->GetNumNodes() << " ((dim+1)*num_nodes)");
375  }
376  }
377 
378  // Zero the matrix/vector if it is to be assembled
379  if (this->mAssembleVector && this->mZeroVectorBeforeAssembly)
380  {
381  // Note PetscVecTools::Finalise(this->mVectorToAssemble); on an unused matrix
382  // would "compress" data and make any pre-allocated entries redundant.
383  PetscVecTools::Zero(this->mVectorToAssemble);
384  }
385  if (this->mAssembleMatrix && this->mZeroMatrixBeforeAssembly)
386  {
387  // Note PetscMatTools::Finalise(this->mMatrixToAssemble); on an unused matrix
388  // would "compress" data and make any pre-allocated entries redundant.
389  PetscMatTools::Zero(this->mMatrixToAssemble);
390  }
391 
392  c_matrix<double, STENCIL_SIZE, STENCIL_SIZE> a_elem = zero_matrix<double>(STENCIL_SIZE,STENCIL_SIZE);
393  c_vector<double, STENCIL_SIZE> b_elem = zero_vector<double>(STENCIL_SIZE);
394 
395 
396  // Loop over elements
398  iter != mpMesh->GetElementIteratorEnd();
399  ++iter)
400  {
401  Element<DIM, DIM>& r_element = *iter;
402 
403  // Test for ownership first, since it's pointless to test the criterion on something which we might know nothing about.
404  if ( r_element.GetOwnership() == true /*&& ElementAssemblyCriterion(r_element)==true*/ )
405  {
406  #define COVERAGE_IGNORE
407  // note: if assemble matrix only
408  if(CommandLineArguments::Instance()->OptionExists("-mech_very_verbose") && this->mAssembleMatrix)
409  {
410  std::cout << "\r[" << PetscTools::GetMyRank() << "]: Element " << r_element.GetIndex() << " of " << mpMesh->GetNumElements() << std::flush;
411  }
412  #undef COVERAGE_IGNORE
413 
414 
415  AssembleOnElement(r_element, a_elem, b_elem);
416 
417  // Note that a different ordering is used for the elemental matrix compared to the global matrix.
418  // See comments about ordering above.
419  unsigned p_indices[STENCIL_SIZE];
420  // Work out the mapping for spatial terms
421  for (unsigned i=0; i<NUM_NODES_PER_ELEMENT; i++)
422  {
423  for (unsigned j=0; j<DIM; j++)
424  {
425  // DIM+1 on the right-hand side here is the problem dimension
426  p_indices[DIM*i+j] = (DIM+1)*r_element.GetNodeGlobalIndex(i) + j;
427  }
428  }
429  // Work out the mapping for pressure terms
430  for (unsigned i=0; i<NUM_VERTICES_PER_ELEMENT; i++)
431  {
432  p_indices[DIM*NUM_NODES_PER_ELEMENT + i] = (DIM+1)*r_element.GetNodeGlobalIndex(i)+DIM;
433  }
434 
435 
436  if (this->mAssembleMatrix)
437  {
438  PetscMatTools::AddMultipleValues<STENCIL_SIZE>(this->mMatrixToAssemble, p_indices, a_elem);
439  }
440 
441  if (this->mAssembleVector)
442  {
443  PetscVecTools::AddMultipleValues<STENCIL_SIZE>(this->mVectorToAssemble, p_indices, b_elem);
444  }
445  }
446  }
447 }
448 
449 template<unsigned DIM, bool CAN_ASSEMBLE_VECTOR, bool CAN_ASSEMBLE_MATRIX>
451  c_matrix<double, STENCIL_SIZE, STENCIL_SIZE >& rAElem,
452  c_vector<double, STENCIL_SIZE>& rBElem)
453 {
454  static c_matrix<double,DIM,DIM> jacobian;
455  static c_matrix<double,DIM,DIM> inverse_jacobian;
456  double jacobian_determinant;
457 
458  mpMesh->GetInverseJacobianForElement(rElement.GetIndex(), jacobian, jacobian_determinant, inverse_jacobian);
459 
460  if (this->mAssembleMatrix)
461  {
462  rAElem.clear();
463  }
464 
465  if (this->mAssembleVector)
466  {
467  rBElem.clear();
468  }
469 
470 
471  // Allocate memory for the basis functions values and derivative values
472  static c_vector<double, NUM_VERTICES_PER_ELEMENT> linear_phi;
473  static c_vector<double, NUM_NODES_PER_ELEMENT> quad_phi;
474  static c_matrix<double, DIM, NUM_NODES_PER_ELEMENT> grad_quad_phi;
475  static c_matrix<double, DIM, NUM_VERTICES_PER_ELEMENT> grad_linear_phi;
476 
477  c_vector<double,DIM> body_force;
478 
479  // Loop over Gauss points
480  for (unsigned quadrature_index=0; quadrature_index < mpQuadRule->GetNumQuadPoints(); quadrature_index++)
481  {
482  double wJ = jacobian_determinant * mpQuadRule->GetWeight(quadrature_index);
483  const ChastePoint<DIM>& quadrature_point = mpQuadRule->rGetQuadPoint(quadrature_index);
484 
485  // Set up basis function info
486  LinearBasisFunction<DIM>::ComputeBasisFunctions(quadrature_point, linear_phi);
487  QuadraticBasisFunction<DIM>::ComputeBasisFunctions(quadrature_point, quad_phi);
488  QuadraticBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_quad_phi);
489  LinearBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_linear_phi);
490 
491  // interpolate X (ie physical location of this quad point).
492  c_vector<double,DIM> X = zero_vector<double>(DIM);
493  for (unsigned vertex_index=0; vertex_index<NUM_VERTICES_PER_ELEMENT; vertex_index++)
494  {
495  for(unsigned j=0; j<DIM; j++)
496  {
497  X(j) += linear_phi(vertex_index)*rElement.GetNode(vertex_index)->rGetLocation()(j);
498  }
499  }
500 
501  if(this->mAssembleVector)
502  {
503  c_vector<double,SPATIAL_BLOCK_SIZE_ELEMENTAL> b_spatial
504  = ComputeSpatialVectorTerm(quad_phi, grad_quad_phi, X, &rElement);
505  c_vector<double,PRESSURE_BLOCK_SIZE_ELEMENTAL> b_pressure = ComputePressureVectorTerm(linear_phi, grad_linear_phi, X, &rElement);
506 
507  for (unsigned i=0; i<SPATIAL_BLOCK_SIZE_ELEMENTAL; i++)
508  {
509  rBElem(i) += b_spatial(i)*wJ;
510  }
511 
512 
513  for (unsigned i=0; i<PRESSURE_BLOCK_SIZE_ELEMENTAL; i++)
514  {
515  rBElem(SPATIAL_BLOCK_SIZE_ELEMENTAL + i) += b_pressure(i)*wJ;
516  }
517  }
518 
519 
520  if(this->mAssembleMatrix)
521  {
522  c_matrix<double,SPATIAL_BLOCK_SIZE_ELEMENTAL,SPATIAL_BLOCK_SIZE_ELEMENTAL> a_spatial_spatial
523  = ComputeSpatialSpatialMatrixTerm(quad_phi, grad_quad_phi, X, &rElement);
524 
525  c_matrix<double,SPATIAL_BLOCK_SIZE_ELEMENTAL,PRESSURE_BLOCK_SIZE_ELEMENTAL> a_spatial_pressure
526  = ComputeSpatialPressureMatrixTerm(quad_phi, grad_quad_phi, linear_phi, grad_linear_phi, X, &rElement);
527 
528  c_matrix<double,PRESSURE_BLOCK_SIZE_ELEMENTAL,SPATIAL_BLOCK_SIZE_ELEMENTAL> a_pressure_spatial;
529  if(!BLOCK_SYMMETRIC_MATRIX)
530  {
531  NEVER_REACHED; // to-come: non-mixed problems
532  //a_pressure_spatial = ComputeSpatialPressureMatrixTerm(quad_phi, grad_quad_phi, lin_phi, grad_lin_phi, x, &rElement);
533  }
534 
535  c_matrix<double,PRESSURE_BLOCK_SIZE_ELEMENTAL,PRESSURE_BLOCK_SIZE_ELEMENTAL> a_pressure_pressure
536  = ComputePressurePressureMatrixTerm(linear_phi, grad_linear_phi, X, &rElement);
537 
538  for (unsigned i=0; i<SPATIAL_BLOCK_SIZE_ELEMENTAL; i++)
539  {
540  for(unsigned j=0; j<SPATIAL_BLOCK_SIZE_ELEMENTAL; j++)
541  {
542  rAElem(i,j) += a_spatial_spatial(i,j)*wJ;
543  }
544 
545  for(unsigned j=0; j<PRESSURE_BLOCK_SIZE_ELEMENTAL; j++)
546  {
547  rAElem(i, SPATIAL_BLOCK_SIZE_ELEMENTAL + j) += a_spatial_pressure(i,j)*wJ;
548  }
549  }
550 
551  for(unsigned i=0; i<PRESSURE_BLOCK_SIZE_ELEMENTAL; i++)
552  {
553  if(BLOCK_SYMMETRIC_MATRIX)
554  {
555  for(unsigned j=0; j<SPATIAL_BLOCK_SIZE_ELEMENTAL; j++)
556  {
557  rAElem(SPATIAL_BLOCK_SIZE_ELEMENTAL + i, j) += a_spatial_pressure(j,i)*wJ;
558  }
559  }
560  else
561  {
562  NEVER_REACHED; // to-come: non-mixed problems
563  }
564 
565  for(unsigned j=0; j<PRESSURE_BLOCK_SIZE_ELEMENTAL; j++)
566  {
567  rAElem(SPATIAL_BLOCK_SIZE_ELEMENTAL + i, SPATIAL_BLOCK_SIZE_ELEMENTAL + j) += a_pressure_pressure(i,j)*wJ;
568  }
569  }
570  }
571  }
572 }
573 
574 
575 #endif // ABSTRACTCONTINUUMMECHANICSASSEMBLER_HPP_
ElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
virtual c_matrix< double, SPATIAL_BLOCK_SIZE_ELEMENTAL, SPATIAL_BLOCK_SIZE_ELEMENTAL > ComputeSpatialSpatialMatrixTerm(c_vector< double, NUM_NODES_PER_ELEMENT > &rQuadPhi, c_matrix< double, DIM, NUM_NODES_PER_ELEMENT > &rGradQuadPhi, c_vector< double, DIM > &rX, Element< DIM, DIM > *pElement)
static unsigned GetSize(Vec vector)
unsigned GetNodeGlobalIndex(unsigned localIndex) const
#define EXCEPTION(message)
Definition: Exception.hpp:143
static void ComputeTransformedBasisFunctionDerivatives(const ChastePoint< ELEMENT_DIM > &rPoint, const c_matrix< double, ELEMENT_DIM, ELEMENT_DIM > &rInverseJacobian, c_matrix< double, ELEMENT_DIM,(ELEMENT_DIM+1)*(ELEMENT_DIM+2)/2 > &rReturnValue)
AbstractContinuumMechanicsAssembler(AbstractTetrahedralMesh< DIM, DIM > *pMesh)
#define NEVER_REACHED
Definition: Exception.hpp:206
bool OptionExists(const std::string &rOption)
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
static void ComputeTransformedBasisFunctionDerivatives(const ChastePoint< ELEMENT_DIM > &rPoint, const c_matrix< double, ELEMENT_DIM, ELEMENT_DIM > &rInverseJacobian, c_matrix< double, ELEMENT_DIM, ELEMENT_DIM+1 > &rReturnValue)
bool GetOwnership() const
static void Zero(Mat matrix)
static void ComputeBasisFunctions(const ChastePoint< ELEMENT_DIM > &rPoint, c_vector< double,(ELEMENT_DIM+1)*(ELEMENT_DIM+2)/2 > &rReturnValue)
virtual c_matrix< double, PRESSURE_BLOCK_SIZE_ELEMENTAL, PRESSURE_BLOCK_SIZE_ELEMENTAL > ComputePressurePressureMatrixTerm(c_vector< double, NUM_VERTICES_PER_ELEMENT > &rLinearPhi, c_matrix< double, DIM, NUM_VERTICES_PER_ELEMENT > &rGradLinearPhi, c_vector< double, DIM > &rX, Element< DIM, DIM > *pElement)
static void ComputeBasisFunctions(const ChastePoint< ELEMENT_DIM > &rPoint, c_vector< double, ELEMENT_DIM+1 > &rReturnValue)
virtual c_vector< double, PRESSURE_BLOCK_SIZE_ELEMENTAL > ComputePressureVectorTerm(c_vector< double, NUM_VERTICES_PER_ELEMENT > &rLinearPhi, c_matrix< double, DIM, NUM_VERTICES_PER_ELEMENT > &rGradLinearPhi, c_vector< double, DIM > &rX, Element< DIM, DIM > *pElement)
virtual c_matrix< double, SPATIAL_BLOCK_SIZE_ELEMENTAL, PRESSURE_BLOCK_SIZE_ELEMENTAL > ComputeSpatialPressureMatrixTerm(c_vector< double, NUM_NODES_PER_ELEMENT > &rQuadPhi, c_matrix< double, DIM, NUM_NODES_PER_ELEMENT > &rGradQuadPhi, c_vector< double, NUM_VERTICES_PER_ELEMENT > &rLinearPhi, c_matrix< double, DIM, NUM_VERTICES_PER_ELEMENT > &rGradLinearPhi, c_vector< double, DIM > &rX, Element< DIM, DIM > *pElement)
static void Zero(Vec vector)
unsigned GetIndex() const
static CommandLineArguments * Instance()
void AssembleOnElement(Element< DIM, DIM > &rElement, c_matrix< double, STENCIL_SIZE, STENCIL_SIZE > &rAElem, c_vector< double, STENCIL_SIZE > &rBElem)
virtual c_vector< double, SPATIAL_BLOCK_SIZE_ELEMENTAL > ComputeSpatialVectorTerm(c_vector< double, NUM_NODES_PER_ELEMENT > &rQuadPhi, c_matrix< double, DIM, NUM_NODES_PER_ELEMENT > &rGradQuadPhi, c_vector< double, DIM > &rX, Element< DIM, DIM > *pElement)
static unsigned GetMyRank()
Definition: PetscTools.cpp:114
static unsigned GetSize(Mat matrix)