Electrostatic Application Elements

Introduction

The electrostatic Kratos element is the implementation code of the Elemental Finite Element Formulation for the Electrostatic PDE. This element can be used for any already existing application in Kratos, by including it in their corresponding files.

In this point we will need just to know the name of the application for their corresponding files ("kElectrostatic"), and the name of the application methods "ElectrostaticApplication".

Our first element in Kratos will be the implementation of the Poisson's equation in Electrostatic, so we will call it "electrostatic_2d". The class name for the element will be called "Electrostatic2D".

The most basic electrostatic element implemented in Kratos only needs two files:

• electrostatic_2d.h
• electrostatic_2d.cpp

A quick way to create an element is to select one of the existing ones close to our formulation and modifying some specific parts. We are going to show the code of these two files with some comments in those lines you probably would want to customise.

The following code includes the most usual sections needed to operate with a Kratos element. Most of the code is common in all the elements and it doesn't need any change. Specific customisation is remarked using bold characters, therefore in general terms you should pay attention just to these parts and keep the rest unchanged.

2D electrostatic elements

First lines: legal issues

The first lines are just the typical legal issues related to Kratos as a framework to create simulation programs. You should keep them as they are. The only sentence to customise is the name of the application (in bold characters) in the third line, KratosR1ElectrostaticApplication:

/*
==============================================================================
KratosR1ElectrostaticApplication
A library based on:
Kratos
A General Purpose Software for Multi-Physics Finite Element Analysis
Version 1.0 (Released on march 05, 2007).

Pooyan Dadvand, Riccardo Rossi, Javier Mora
pooyan@cimne.upc.edu
rrossi@cimne.upc.edu
mora@cimne.upc.edu
- CIMNE (International Center for Numerical Methods in Engineering),
Gran Capita' s/n, 08034 Barcelona, Spain

Permission is hereby granted, free  of charge, to any person obtaining
a  copy  of this  software  and  associated  documentation files  (the
"Software"), to  deal in  the Software without  restriction, including
without limitation  the rights to  use, copy, modify,  merge, publish,
distribute,  sublicense and/or  sell copies  of the  Software,  and to
permit persons to whom the Software  is furnished to do so, subject to
the following condition:

Distribution of this code for  any  commercial purpose  is permissible
ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNERS.

The  above  copyright  notice  and  this permission  notice  shall  be
included in all copies or substantial portions of the Software.

THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT  SHALL THE AUTHORS OR COPYRIGHT HOLDERS  BE LIABLE FOR ANY
CLAIM, DAMAGES OR  OTHER LIABILITY, WHETHER IN AN  ACTION OF CONTRACT,
TORT  OR OTHERWISE, ARISING  FROM, OUT  OF OR  IN CONNECTION  WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

==============================================================================
*/


The following lines (comments again) indicate the author's name (your name), date and hour of the last revision, and revision number, as usual in other codes.

//
//   Project Name:        Kratos
//   Last modified by:    $Author: jmora$
//   Date:                $Date: 2009-09-08 23:58:38$
//   Revision:            $Revision: 1.0$
//
//


In order to avoid class redefinition, you have to define a label for your element definition, as follows:

#if !defined(KRATOS_ELECTROSTATIC_2D_ELEM_H_INCLUDED )
#define  KRATOS_ELECTROSTATIC_2D_ELEM_H_INCLUDED


the #endif is at the end of the code (the rest of the lines must be included between these ones):

#endif // KRATOS_ELECTROSTATIC_2D_ELEM_H_INCLUDED  defined


Now you have to add the "include" files (copy as it is):

// System includes

// External includes
#include "boost/smart_ptr.hpp"

// Project includes
#include "includes/define.h"
#include "includes/element.h"
#include "includes/ublas_interface.h"
#include "includes/variables.h"


Declare the namespace Kratos (copy at it is):

namespace Kratos
{

///@name Kratos Globals
///@{

///@}
///@name Type Definitions
///@{

///@}
///@name  Enum's
///@{

///@}
///@name  Functions
///@{

///@}
///@name Kratos Classes
///@{

/// Short class definition.
/** Detail class definition.
*/


The namespace is closed at the end of the file (just before the #endif):

}  // namespace Kratos.


Our class definition starts now. We will call it "Electrostatic2D":

 class Electrostatic2D
: public Element
{
public:
///@name Type Definitions
///@{

/// Counted pointer of Electrostatic2D
KRATOS_CLASS_POINTER_DEFINITION(Electrostatic2D);

///@}
///@name Life Cycle
///@{


Constructor and destructor declaration (you should just change the class name):

    /// Default constructor.
Electrostatic2D(IndexType NewId, GeometryType::Pointer pGeometry);
Electrostatic2D(IndexType NewId, GeometryType::Pointer pGeometry,  PropertiesType::Pointer pProperties);

/// Destructor.
virtual ~ Electrostatic2D();

///@}
///@name Operators
///@{

///@}
///@name Operations
///@{


And we add the class methods we need to perform the implementation of the element. By now we will just declare the methods, leaving a more detailed explanation in the implementation file (.cpp). Note that for this simple element you don't need change anything:

      Element::Pointer Create(IndexType NewId, NodesArrayType const& ThisNodes,  PropertiesType::Pointer pProperties) const;

void CalculateLocalSystem(MatrixType& rLeftHandSideMatrix, VectorType& rRightHandSideVector, ProcessInfo& rCurrentProcessInfo);

void CalculateRightHandSide(VectorType& rRightHandSideVector, ProcessInfo& rCurrentProcessInfo);
//virtual void CalculateLeftHandSide(MatrixType& rLeftHandSideMatrix, ProcessInfo& rCurrentProcessInfo);

void EquationIdVector(EquationIdVectorType& rResult, ProcessInfo& rCurrentProcessInfo);

void GetDofList(DofsVectorType& ElementalDofList,ProcessInfo& CurrentProcessInfo);

void InitializeSolutionStep(ProcessInfo& CurrentProcessInfo);

void CalculateOnIntegrationPoints( const Variable<array_1d<double,3> >& rVariable,
std::vector<array_1d<double,3> >& rValues,
const ProcessInfo& rCurrentProcessInfo);

void GetValueOnIntegrationPoints(const Variable<array_1d<double,3>>& rVariable, std::vector<array_1d<double,3> >& rValues, const ProcessInfo& rCurrentProcessInfo);



Additional lines not important for this element (you don't need to modify anything):

    ///@}
///@name Access
///@{

///@}
///@name Inquiry
///@{

///@}
///@name Input and output
///@{

///@}
///@name Friends
///@{

///@}

protected:
///@name Protected static Member Variables
///@{

///@}
///@name Protected member Variables
///@{

///@}
///@name Protected Operators
///@{

///@}
///@name Protected Operations
///@{

///@}
///@name Protected  Access
///@{

///@}
///@name Protected Inquiry
///@{

///@}
///@name Protected LifeCycle
///@{

///@}


The declaration of the class's attributes we will know to perform the element computation (note again the use of the class name):

   private:
///@name Static Member Variables
///@{
static boost::numeric::ublas::bounded_matrix<double,3,2> msDN_DX;
static boost::numeric::ublas::bounded_matrix<double,2,2> msD;
static array_1d<double,3> msN; //dimension = number of nodes
static array_1d<double,3> ms_temp; //dimension = number of nodes
static array_1d<double,3> point_sources; //dimension = number of nodes


and to close the class definition:

     ///@}
///@name Member Variables
///@{

///@}
///@name Private Operators
///@{

///@}
///@name Private Operations
///@{

///@}
///@name Private  Access
///@{

///@}
///@name Private Inquiry
///@{

///@}
///@name Un accessible methods
///@{

/// Assignment operator.
// Electrostatic2D& operator=(const Poisson2D& rOther);

/// Copy constructor.
// Electrostatic2D(const Poisson2D& rOther);

///@}

}; // Class Electrostatic2D


To finalise the header code, we should add (no changes are needed):

 ///@}

///@name Type Definitions
///@{

///@}
///@name Input and output
///@{

/// input stream function

/// output stream function

///@}


Element implementation methods file: electrostatic_2d.cpp

First lines: legal issues

The first lines are just again the typical legal issues related to Kratos as a framework to create simulation programs. You should keep them as they are. The only sentence to customise is the name of the application (in bold characters) in the third line, KratosR1PoissonApplication:

/*
==============================================================================
KratosR1ElectrostaticApplication
A library based on:
Kratos
A General Purpose Software for Multi-Physics Finite Element Analysis
Version 1.0 (Released on march 05, 2007).

Pooyan Dadvand, Riccardo Rossi, Javier Mora
pooyan@cimne.upc.edu
rrossi@cimne.upc.edu
mora@cimne.upc.edu
- CIMNE (International Center for Numerical Methods in Engineering),
Gran Capita' s/n, 08034 Barcelona, Spain

Permission is hereby granted, free  of charge, to any person obtaining
a  copy  of this  software  and  associated  documentation files  (the
"Software"), to  deal in  the Software without  restriction, including
without limitation  the rights to  use, copy, modify,  merge, publish,
distribute,  sublicense and/or  sell copies  of the  Software,  and to
permit persons to whom the Software  is furnished to do so, subject to
the following condition:

Distribution of this code for  any  commercial purpose  is permissible
ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNERS.

The  above  copyright  notice  and  this permission  notice  shall  be
included in all copies or substantial portions of the Software.

THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT  SHALL THE AUTHORS OR COPYRIGHT HOLDERS  BE LIABLE FOR ANY
CLAIM, DAMAGES OR  OTHER LIABILITY, WHETHER IN AN  ACTION OF CONTRACT,
TORT  OR OTHERWISE, ARISING  FROM, OUT  OF OR  IN CONNECTION  WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

==============================================================================
*/


The following lines (comments again) indicate the author's name (your name), date and hour of the last revision, and revision number, as usual in other codes.

//
//   Project Name:        Kratos
//   Last modified by:    $Author: jmora$
//   Date:                $Date: 2009-09-08 23:58:38$
//   Revision:            $Revision: 1.0$
//
//


Now you have to add the "include" files. Note that you have to change the element header file name (electrostatic_2d.h) and the application header file name where you want to include your element (in this case, kElectrostatic.h, not yet created). The related subdirectories will be explained in the application section:

// System includes

// External includes

// Project includes

#include "includes/define.h"
#include "custom_elements/electrostatic_2d.h"
#include "kElectrostatic.h"
#include "utilities/math_utils.h"
#include "utilities/geometry_utilities.h"


Declare the namespace Kratos (copy at it is). List the static variables, and create the main class methods as follows. Just note that you have to be sure that you are using your element class name (Elecrostatic2D in this example, in bold characters):

namespace Kratos
{
namespace Electrostatic2Dauxiliaries
{
//static variables
boost::numeric::ublas::bounded_matrix<double,3,2> msDN_DX = ZeroMatrix(3,2);
boost::numeric::ublas::bounded_matrix<double,2,3> msB = ZeroMatrix(2,3);
boost::numeric::ublas::bounded_matrix<double,2,2> msD = ZeroMatrix(2,2);

array_1d<double,3> msN = ZeroVector(3); //dimension = number of nodes
array_1d<double,3> ms_temp = ZeroVector(3); //dimension = number of nodes
array_1d<double,3> point_sources = ZeroVector(3); //dimension = number of nodes

array_1d<double,3> surface_sources = ZeroVector(3); //dimension = number of nodes
}
using  namespace Electrostatic2Dauxiliaries;

//************************************************************************************
//************************************************************************************
Electrostatic2D::Electrostatic2D(IndexType NewId, GeometryType::Pointer pGeometry)
: Element(NewId, pGeometry)
{
}

//************************************************************************************
//************************************************************************************
Electrostatic2D::Electrostatic2D(IndexType NewId, GeometryType::Pointer pGeometry,  PropertiesType::Pointer pProperties)
: Element(NewId, pGeometry, pProperties)
{

}

Element::Pointer Electrostatic2D::Create(IndexType NewId, NodesArrayType const& ThisNodes,  PropertiesType::Pointer pProperties) const
{
return Element::Pointer(new Electrostatic2D(NewId, GetGeometry().Create(ThisNodes), pProperties));
}

Electrostatic2D::~Electrostatic2D()
{
}


Remember to close the namespace at the end of the file.

}  // namespace Kratos.


Now, it's the critical moment to transfer your Electrostatic Finite Element Formulation to the Kratos code. Some preliminary comments are:

• Kratos allows different ways to implement the formulation, therefore don't worry if you find different Kratos elements with different implementation approaches, even if you leave some methods unused, or parts without contents. Just check the coherence of your formulation.
• Kratos has been designed to allow you to focus your attention to the element formulation. That is, again, don't worry about the other programming aspects of your simulation program, such as the matrix assembly, solvers, etc... Of course, you are able to customise them, but for basic applications is not necessary at all.

Copy the following code as it is, with two exceptions (in bold characters):

• use your element class name (Electrostatic2D in this example);
• your variable names (we will use ELECTROSTATIC_POTENTIAL for the unknown, ELECTRICAL_PERMITTIVITY for the material property, ELECTRIC_FIELD and ELECTRIC_DISPLACEMENT_FIELD for the gradient of the unknown, INFINIT_COEFFICIENT for the infinit boundary condition, ELECTROSTATIC_SURFACE_CHARGE and ELECTROSTATIC_POINT_CHARGE for the source terms);
	//************************************************************************************
//************************************************************************************
void Electrostatic2D::CalculateLocalSystem(MatrixType& rLeftHandSideMatrix, VectorType& rRightHandSideVector, ProcessInfo& rCurrentProcessInfo)
{
KRATOS_TRY

const unsigned int number_of_points = GetGeometry().size();

if(rLeftHandSideMatrix.size1() != number_of_points)
rLeftHandSideMatrix.resize(number_of_points,number_of_points,false);

if(rRightHandSideVector.size() != number_of_points)
rRightHandSideVector.resize(number_of_points,false);

//getting data for the given geometry
double Area;
GeometryUtils::CalculateGeometryData(GetGeometry(), msDN_DX, msN, Area);

array_1d<double,3> permittivity = GetProperties()[ELECTRICAL_PERMITTIVITY];
msD(0,0)=permittivity[0];
msD(1,1)=permittivity[1];
msD(1,0)=0.0;
msD(0,1)=0.0;

//double surface_sources = (this)->GetValue(ELECTROSTATIC_SURFACE_CHARGE);
surface_sources[0] = (this)->GetValue(ELECTROSTATIC_SURFACE_CHARGE);
surface_sources[1] = (this)->GetValue(ELECTROSTATIC_SURFACE_CHARGE);
surface_sources[2] = (this)->GetValue(ELECTROSTATIC_SURFACE_CHARGE);

// main loop
const GeometryType::IntegrationPointsArrayType& integration_points = GetGeometry().IntegrationPoints();

noalias(rLeftHandSideMatrix) = prod(msDN_DX,Matrix(prod(msD,trans(msDN_DX))));


Note that this line is related to the following term of the stiffness matrix:

$\mathbf{K}^{(e)}= \int_{\Omega^{(e)}} \mathbf{B^T} \mathbf{\varepsilon} \mathbf{B} \partial \Omega^{(e)}$

		rLeftHandSideMatrix *= Area;

noalias(rRightHandSideVector) = surface_sources*Area/3.0;


and here we add the surface term to the right side of the expression:

$\mathbf{f}^{(e)}= \int_{\Omega^{(e)}} \mathbf{N^T} \rho_v \partial \Omega^{(e)}$
		//subtracting the dirichlet term
// RHS -= LHS*ELECTROSTATIC_POTENTIALs
for(unsigned int iii = 0; iii<number_of_points; iii++)
ms_temp[iii] = GetGeometry()[iii].FastGetSolutionStepValue(ELECTROSTATIC_POTENTIAL);
noalias(rRightHandSideVector) -= prod(rLeftHandSideMatrix,ms_temp);

KRATOS_CATCH("");
}



The code is self-explanatory, but just a few comments to link it with our basic finite element formulation:

• CalculateGeometryData obtains msDN_DX, msN and the elemental Area, that is, the B and N matrix;
• msD is the material properties matrix (the D matrix);
• With prod(msDN_DX,Matrix(prod(msD,trans(msDN_DX)))) we obtain the stiffness matrix (the K matrix);
• With noalias(rRightHandSideVector) = surface_sources we add the source_source contribution to the source vector f;
• Finally, with prod(rLeftHandSideMatrix,ms_temp) we compute the Dirichlet contribution to the source vector f;

The following code is not used for this specific element (keep at it is, just changing the class name):

	//************************************************************************************
//************************************************************************************
void Electrostatic2D::CalculateRightHandSide(VectorType& rRightHandSideVector, ProcessInfo& rCurrentProcessInfo)
{
KRATOS_ERROR(std::logic_error,  "method not implemented" , "");
}

//************************************************************************************
//************************************************************************************
// this subroutine calculates the nodal contributions for the explicit steps of the
// fractional step procedure
void Electrostatic2D::InitializeSolutionStep(ProcessInfo& CurrentProcessInfo)
{
KRATOS_TRY

KRATOS_CATCH("");
}


The next lines give us the unknows (you have just change the class and variables' names):

	//************************************************************************************
//************************************************************************************
void Electrostatic2D::EquationIdVector(EquationIdVectorType& rResult, ProcessInfo& CurrentProcessInfo)
{
unsigned int number_of_nodes = GetGeometry().PointsNumber();
if(rResult.size() != number_of_nodes)
rResult.resize(number_of_nodes,false);

for (unsigned int i=0;i<number_of_nodes;i++)
rResult[i] = GetGeometry()[i].GetDof(ELECTROSTATIC_POTENTIAL).EquationId();
}

//************************************************************************************
//************************************************************************************
void Electrostatic2D::GetDofList(DofsVectorType& ElementalDofList,ProcessInfo& CurrentProcessInfo)
{
unsigned int number_of_nodes = GetGeometry().PointsNumber();
if(ElementalDofList.size() != number_of_nodes)
ElementalDofList.resize(number_of_nodes);

for (unsigned int i=0;i<number_of_nodes;i++)
ElementalDofList[i] = GetGeometry()[i].pGetDof(ELECTROSTATIC_POTENTIAL);

}


And, finally, the following methods compute the solution for the gradient of the unknowns:

	//************************************************************************************
//************************************************************************************
void Electrostatic2D::CalculateOnIntegrationPoints(const Variable<array_1d<double,3>>& rVariable, std::vector<array_1d<double,3> >& Output, const ProcessInfo& rCurrentProcessInfo)
{
IntegrationMethod mThisIntegrationMethod;

mThisIntegrationMethod= GetGeometry().GetDefaultIntegrationMethod();
const GeometryType::IntegrationPointsArrayType& integration_points = GetGeometry().IntegrationPoints(mThisIntegrationMethod);
Vector vect_tmp(3);
vect_tmp[0] = GetGeometry()[0].FastGetSolutionStepValue(ELECTROSTATIC_POTENTIAL);
vect_tmp[1] = GetGeometry()[1].FastGetSolutionStepValue(ELECTROSTATIC_POTENTIAL);
vect_tmp[2] = GetGeometry()[2].FastGetSolutionStepValue(ELECTROSTATIC_POTENTIAL);

array_1d<double,3> permittivity = GetProperties()[ELECTRICAL_PERMITTIVITY];
msD(0,0)=permittivity[0];
msD(1,1)=permittivity[1];
msD(1,0)=0.0;
msD(0,1)=0.0;

double Area;
GeometryUtils::CalculateGeometryData(GetGeometry(), msDN_DX, msN, Area);

if(Output.size() != GetGeometry().IntegrationPoints(mThisIntegrationMethod).size())
Output.resize(GetGeometry().IntegrationPoints(mThisIntegrationMethod).size());

for(unsigned int PointNumber = 0; PointNumber<integration_points.size(); PointNumber++)
{
if(rVariable==ELECTRIC_FIELD)
{
//KRATOS_WATCH("GiD Post Electrostatic - calc - elec - field");
noalias(Output[PointNumber])=-prod(trans(msDN_DX),vect_tmp);
Output[PointNumber][2]=0.0;
//				KRATOS_WATCH(Output[PointNumber]);
}


That is:

$E_x = -\frac{\partial V(x,y,z)}{\partial x} \qquad E_y = -\frac{\partial V(x,y,z)}{\partial y} \qquad E_z = -\frac{\partial V(x,y,z)}{\partial z}$

			else if(rVariable==ELECTRIC_DISPLACEMENT_FIELD)
{
//KRATOS_WATCH(rValues[PointNumber]);
noalias(Output[PointNumber])=-prod(prod(msD,trans(msDN_DX)),vect_tmp);
//				KRATOS_WATCH(Output[PointNumber]);
}


equivalent to:

$D_x = -\varepsilon_{x} \frac{\partial V(x,y,z)}{\partial x} \quad D_y = -\varepsilon_{y} \frac{\partial V(x,y,z)}{\partial y} \quad D_z = -\varepsilon_{z} \frac{\partial V(x,y,z)}{\partial z}$
			else
{
//				KRATOS_WATCH("GiD Post Electrostatic - calc - else");
Output[PointNumber][0]=msD(0,0);
Output[PointNumber][1]=msD(1,1);
Output[PointNumber][2]=0.0;
//				KRATOS_WATCH(Output[PointNumber]);
}
}

}

//************************************************************************************
//************************************************************************************

void Electrostatic2D::GetValueOnIntegrationPoints(const Variable<array_1d<double,3> >& rVariable,
std::vector<array_1d<double,3> >& rValues, const ProcessInfo& rCurrentProcessInfo)
{
//KRATOS_WATCH("GiD Post Electrostatic - GetValueOnIntegrationPoints");

if(rVariable==ELECTRIC_FIELD)
{
//			KRATOS_WATCH("GiD Post Electrostatic - get - elec-field");
CalculateOnIntegrationPoints( rVariable, rValues, rCurrentProcessInfo );
}
else if(rVariable==ELECTRIC_DISPLACEMENT_FIELD)
{
//			KRATOS_WATCH("GiD Post Electrostatic - get - elec-disp");
CalculateOnIntegrationPoints( rVariable, rValues, rCurrentProcessInfo );
}
else
{
//			KRATOS_WATCH("GiD Post Electrostatic - get - else");
CalculateOnIntegrationPoints( rVariable, rValues, rCurrentProcessInfo );
}
}