EASIFEM Blog https://www.easifem.com/blog EASIFEM Blog Mon, 08 Dec 2025 00:01:37 GMT https://validator.w3.org/feed/docs/rss2.html https://github.com/jpmonette/feed en <![CDATA[Understanding FEDOF in easifem (Part 1)]]> https://www.easifem.com/blog/FEDOF/understanding-fedof-1 https://www.easifem.com/blog/FEDOF/understanding-fedof-1 Mon, 08 Dec 2025 00:01:37 GMT FEDOF

FEDOF denotes the finite element degree of freedom. The concept of degree of freedom depends upon the type of finite element approximation (basis functions) used for the problem.

note

These series of notes will present the FEDOF concept for for H1 conforming Hierarchical and Lagrange basis functions.

  • For Lagrange polynomials the degree of freedoms are associatd with the nodes of the mesh. In this case, a node implies a point in the mesh. This point can be a vertex, somewhere on the edge, face, or interior of the element.
  • For Hierarchical polynmials the degree of freedoms are associated with the modes. They can be associated with nodes, edges, faces and interior of the elements. In this case the node has an abstract meaning. But we will associcate them with the vertex, edge, face, and interior basis functions.
  • In the case of H1 conforming basis functions with Hierarchical polynomials, the orientation of edge and faces with respect to the master element is very important. However, for Lagrange polynomials the orientation is not so much needed if we generate the nodes correctly for higher order mesh.

In this note we will focus on 2D mesh with quadrilateral.

Quadrilateral mesh

Information about this domain is given below:

engine: GMSH 4.1 0 8
version: 4.1
nsd: 2
minNptrs: 1
maxNptrs: 9
minElemNum: 1
maxElemNum: 16
tNodes: 9
tEntitiesForNodes: 9
tEntitiesForElements: 9
tElements: 4, 8, 4, 0
total mesh of volume: 0
total mesh of surface: 1
total mesh of curve: 4
total mesh of point: 4

The information about the cell mesh is given below.

total nodes: 9
total elements: 4
tEdges: 0
tFaces: 12
info

For a 2D-mesh, we do not define the edges. For 2D mesh we only have Cell, faces, and vertices.

From the domain we can get the pointer to mesh of cells. In this case a cell is made of quadrilateral elements.

cellMesh => dom%GetMeshPointer()

We can get the maximum number of nodes in an element by using GetMaxNNE() method.

maxNNE = cellMesh%GetMaxNNE()

For this mesh the value of maxNNE will be 4, as the maximum number of nodes in a quadrilateral element is 4.

info

You can think maxNNE as the maximum number of vertex connectivity for elements of mesh.

We can get the global element number of a local element number by using following.

iel = cellMesh%GetGlobalElemNumber(localElement=1)
"GlobalElement vs LocalElement Number"

Global element number is what you see in the mesh file, it the element number assigned by the mesh generator. Whereas, local element number is the location of this element inside the cellMesh object. We do not hope you to know the local element number, therefore, you can convert global element number to local element by using the method GetLocalElemNumber().

In this case iel will be 13.

Vertex connectivity of mesh

The vertex connectivity of this element can be obtained by using the following code.

con = cellMesh%GetConnectivity(globalElement=1, islocal=yes, opt="V")
note

To get vertex connectivity we use opt="V", for face connectivity we will use opt="F", for edge connectivity we will use opt="E", and for cell connectivity we will use opt="C", and for all connectivity we will use opt="A".

The vertex connectivity of global element 13 (local element 1) is given below.

1 5 9 8
caution

Note that the vertex connectivity is returned in terms of global vertex numbers.

Face connectivity of mesh

Now lets get the face connectivity for this element.

We can enquire about the total number of faces in the mesh by using the method GetTotalFaces() as shown below.

CALL Display(cellMesh%GetTotalFaces(), "Total faces in mesh:")

The face connectivity of global Element 13 (local element 1) can be obtained by using the following.

con = cellMesh%GetConnectivity(globalElement=1, islocal=yes, opt="F")
CALL Display(con, "Connectivity of faces for globalElement=1: ")

The result is given below.

1 2 3 4
note

These are local face numbers. Note that there are no global face numbers because the mesh file does not provide face number. Therefore, the concept of global or local face number is not valid here.

We can get the orientation of these faces by using GetFaceOrientation(), the face orientation is a two dimensional array, where first dimension contains the face orientation flag in two dimensions and the second dimension contains the local face number. The following code is related to getting the face orientation.

caution

The number of rows in faceOrientation should be 3.

CALL Reallocate(orient, 3, SIZE(con))
CALL cellMesh%GetFaceOrientation(globalElement=1, islocal=yes, ans=orient, &
nrow=nrow, ncol=ncol)
CALL Display(orient(1:nrow, 1:ncol), &
"Face orientations for globalElement=1: ")

The result is given below.

Face orientations for globalElement=1:
---------------------------------------
1 1 -1 -1

Let's understand the concept of face orientation.

The vertex connectivity of global element 13 is given by [1 5 9 8]. The faces are oriented in the counter clockwise direction.

  • Local face 1 is form vertex 1 to vertex 5
  • Local face 2 is form vertex 5 to vertex 9
  • Local face 3 is form vertex 9 to vertex 8
  • Local face 4 is form vertex 8 to vertex 1

In 2D, positive orientation of a face means from small to large vertex number. Therefore,

  • Local face 1, form vertex 1 to vertex 5, has positive orientation.
  • Local face 2, form vertex 5 to vertex 9, has positive orientation.
  • Local face 3, form vertex 9 to vertex 8, has negative orientation.
  • Local face 4, form vertex 8 to vertex 1, has negative orientation.
warning

If you try to get the edge data for 2D mesh you will get nothing because edge data is note defined for 2D mesh.

Getting vertex connectivity of the a face. To get the node numbers of a face we need to describe the globalElement and the local face number as shown below.

con = cellMesh%GetFacetConnectivity(globalElement=1, iface=1, islocal=yes)
CALL Display(con, "Node numbers of face 1 of element 1: ")

con = cellMesh%GetFacetConnectivity(globalElement=1, iface=2, islocal=yes)
CALL Display(con, "Node numbers of face 2 of element 1: ")

con = cellMesh%GetFacetConnectivity(globalElement=1, iface=3, islocal=yes)
CALL Display(con, "Node numbers of face 3 of element 1: ")

con = cellMesh%GetFacetConnectivity(globalElement=1, iface=4, islocal=yes)
CALL Display(con, "Node numbers of face 4 of element 1: ")

The result is given below.

Node numbers of face 1 of element 1: [1 5]
Node numbers of face 2 of element 1: [5 9]
Node numbers of face 3 of element 1: [9 8]
Node numbers of face 4 of element 1: [8 1]
caution

Note these face connectivities are for local face of element.

Getting all connectivity of mesh

we can get all connectivity of an element by using the following code.

con = cellMesh%GetConnectivity(globalElement=1, islocal=yes, opt='V')
CALL Display(con, "Connectivity of vertex for localElement=1: ")

Scalar FEDOF

Getting the degree of freedom for scalar field.

  • Vertex DOF connectivity for localElement=1:
1 5 9 8
  • Face DOF connectivity for localElement=1:
10 11 12 13
  • Cell DOF connectivity for localElement=1:
22
  • All DOF connectivity for localElement=1:
1 5 9 8 10 11 12 13 22

There is another way to get FEDOF connectivity.

From the cellMesh we get the connectivity with opt "V", "F", "C", and "A" for vertex, face, cell, and all DOF connectivity respectively. Then for each of these abstract node number we can call following methods on FEDOF object.

  • GetVertextDOF()
  • GetFaceDOF()
  • GetCellDOF()
]]>
mesh fedof basis shapeFunctions
<![CDATA[Understanding FEDOF in easifem (Part 2)]]> https://www.easifem.com/blog/FEDOF/understanding-fedof-2 https://www.easifem.com/blog/FEDOF/understanding-fedof-2 Mon, 08 Dec 2025 00:01:37 GMT FEDOF
note

Read the part 1 of this series here before proceeding further.

In this note we will study the FEDOF for scalar field using H1 conforming Hierarchical basis functions. The main focus on the degree of freedom associated with vertex, face, and cell of the element.

In this note we will focus on 2D mesh with quadrilateral.

Quadrilateral mesh

Scalar FEDOF

Initiate the FEDOF object and scalar field by using the following code.

CALL u%ImportFromToml(tomlName="u", fedof=fedof, dom=dom, filename=tomlFileName)
CALL fedof%Display(msg="FEDOF info: ")

Getting all the connectivity

Scalar FEDOF in global element 13

The following code gets all the DOF in an element.

1   5   9   8  10  11  12  13  14  15  16  17  18  19  20  21  46  47  48  49  50  51  52  53  54

Vertex connectivity

Following code gets the vertex DOF connectivity.

nrow = fedof%GetTotalDOF(globalElement=1, islocal=yes, opt="V")
CALL Display(nrow, "Total Vertex DOF for localElement=1:")

con = fedof%GetConnectivity(globalElement=1, islocal=yes, opt="V")
CALL Display(con, "Vertex DOF connectivity for localElement=1:")
1 5 9 8
note

These are local DOF numbers, and it has nothing to do with the mesh connectivity.

We can get the DOF of a single vertex by using GetVertexDOF() as shown below.

CALL Reallocate(con, 100)
call cellMesh%GetConnectivity_(globalElement=1, islocal=yes, opt="V", ans=con, tsize=tsize)
CALL fedof%GetVertexDOF(globalNode=con(1), islocal=.false., ans=con, tsize=tsize)
CALL Display(con(1), "DOF of vertex 1 of element 1:")

Face connectivity

Following code gets the connectivity of all face DOF in an element. Note that these face DOF are local. In addition, they are related to the positive oriented faces only.

nrow = fedof%GetTotalDOF(globalElement=1, islocal=yes, opt="F")
CALL Display(nrow, "Total Face DOF for localElement=1:")

con = fedof%GetConnectivity(globalElement=1, islocal=yes, opt="F")
CALL Display(con, "Face DOF connectivity for localElement=1:", full=.TRUE., &
orient="R")
10  11  12  13  14  15  16  17  18  19  20  21

We can get the DOF of a face by using GetFaceDOF() as shown below.

CALL Reallocate(con, 100, isExpand=.TRUE., expandFactor=2)
CALL cellMesh%GetConnectivity_(globalElement=1, islocal=yes, opt="F", &
ans=con, tsize=tsize)
nrow = con(1)
CALL fedof%GetFaceDOF(globalFace=nrow, islocal=.TRUE., ans=con, &
tsize=tsize)
CALL Display(con(1:tsize), "DOF on face: "//ToString(nrow), full=.TRUE., &
orient="R")
DOF on face: 1
10  11  12

Getting dof on local face 3 on element 1:

CALL Reallocate(con, 100, isExpand=.TRUE., expandFactor=2)
CALL cellMesh%GetConnectivity_(globalElement=1, islocal=yes, opt="F", &
ans=con, tsize=tsize)
nrow = con(3)
CALL fedof%GetFaceDOF(globalFace=nrow, islocal=.TRUE., ans=con, &
tsize=tsize)
CALL Display(con(1:tsize), "DOF on face: "//ToString(nrow), full=.TRUE., &
orient="R")
DOF of face: 3
16 17 18

Cell connectivity

Following code gets the connectivity of cell in an element. Note that these DOFs are local.

nrow = fedof%GetTotalDOF(globalElement=1, islocal=yes, opt="C")
CALL Display(nrow, "Total Cell DOF for localElement=1:")

con = fedof%GetConnectivity(globalElement=1, islocal=yes, opt="C")
CALL Display(con, "Cell DOF connectivity for localElement=1:")
Result
46  47  48  49  50  51  52  53  54

We can get the cell DOF by using GetCellDOF() as shown below.

CALL Reallocate(con, 100, isExpand=.TRUE., expandFactor=2)
CALL cellMesh%GetConnectivity_(globalElement=1, islocal=yes, opt="C", &
ans=con, tsize=tsize)
nrow = con(1)
CALL fedof%GetCellDOF(globalCell=nrow, islocal=.FALSE., ans=con, &
tsize=tsize)
CALL Display(con(1:tsize), "DOF in cell: "//ToString(nrow), full=.TRUE., &
orient="R")
DOF in cell: 13
46  47  48  49  50  51  52  53  54
]]>
mesh fedof
<![CDATA[Understanding FEDOF in easifem (Part 3)]]> https://www.easifem.com/blog/FEDOF/understanding-fedof-3 https://www.easifem.com/blog/FEDOF/understanding-fedof-3 Mon, 08 Dec 2025 00:01:37 GMT FEDOF
note
  • Read the part 1 of this series here before proceeding further.
  • Read the part 2 of this series here before proceeding further.

In this note we will study the FEDOF for scalar field using H1 conforming Hierarchical basis functions. The main focus is on generating quadrature points and shape functions.

In this note we will focus on 2D mesh with quadrilateral.

Quadrilateral mesh

Scalar FEDOF

Initiate the FEDOF object and scalar field by using the following code.

CALL u%ImportFromToml(tomlName="u", fedof=fedof, dom=dom, filename=tomlFileName)
CALL fedof%Display(msg="FEDOF info: ")

Getting quadrature points

The following code gets the quadrature points in an element.

CALL fedof%GetQuadraturePoints(quad=qp, globalElement=1, islocal=yes)
CALL Display(qp, "Quadrature points: ")
Quadrature points
x1x2x3x4x5x6x7x8x9x10x11x12x13x14x15x16
-0.861136-0.861136-0.861136-0.861136-0.339981-0.339981-0.339981-0.3399810.3399810.3399810.3399810.3399810.8611360.8611360.8611360.861136
-0.861136-0.3399810.3399810.861136-0.861136-0.3399810.3399810.861136-0.861136-0.3399810.3399810.861136-0.861136-0.3399810.3399810.861136
0.1210030.2268520.2268520.1210030.2268520.4252930.4252930.2268520.2268520.4252930.4252930.2268520.1210030.2268520.2268520.121003

Getting shape functions

]]>
mesh fedof
<![CDATA[Handling Dirichlet Bounding Conditions in easifem (Part 1)]]> https://www.easifem.com/blog/DirichletBC/understanding-dirichletbc-1 https://www.easifem.com/blog/DirichletBC/understanding-dirichletbc-1 Mon, 08 Dec 2025 00:01:27 GMT Introduction

To apply boundary condition in FEM computation, EASIFEM, provides a class called DirichletBC_.

info

DirichletBC_ is a subclass of AbstractBC.

To understand how DirichletBC works, lets consider an example of linear elasticity. Let's say we want to apply the following boundary condition.

u=U0, on Γ\mathbf{u} = \mathbf{U}_{0}, \text{ on } \Gamma

We may think that there is only one boundary condition. But in easifem this is not the case. Actually, u\mathbf{u}, has three components in 3D (and two components in 2D). Therefore, the above boundary condition is actually boundary condition for uxu_x, uyu_y, and uzu_z. So, we have three boundary condition on a given boundary Γ\Gamma.

The second point, which is quite obvious, is that every boundary condition has two things:

  • The boundary
  • The value (condition)

To define the boundary EASIFEM employs the MeshSelection class. The value can be specified in several ways as mentioned below in this section.

note

Several instances of DirichletBC can have same boundary but different condition.

Mesh and degrees of freedom

Degree of freedom in mesh

Import from toml

We can initiate an instance of DirichletBC_ by importing the information from a toml-file. To do so, we will use the method called ImportFromToml.

Let us consider the following toml file

[domain]
filename = "./mesh/square_3x3.h5"
group = "/"
totalMedium = 1

[u]
name = "u"
engine = "NATIVE_SERIAL"
fieldType = "Normal"
spaceCompo = 1
timeCompo = 1
fedofName = "space"
geofedofName = "geofedof"

[u.geofedof]
baseContinuity = "H1"
baseInterpolation = "Lagrange"
ipType = "Equidistance"
baseType = "Monomial"

[u.space]
baseContinuity = "H1"
baseInterpolation = "Hierarchical"
# ipType = "Equidistance"
# baseType = "Monomial"
order = 4
scaleForQuadOrder = 2
quadOptName = "quadOpt"

[u.space.quadOpt]
isHomogeneous = true
quadratureType = "GaussLegendre"
# order = 2

[dbc]
name = "left_bottom_fix"
idof = 1
nodalValueType = "Constant"
value = 10.0
[dbc.boundary]
isSelectionByMeshID = true
[dbc.boundary.meshID]
line = [1, 4]

We use the following code to import the boundary condition from the above toml file.

!> author: Vikas Sharma, Ph. D.
! date: 2025-10-23
! summary: Study of DirichletBC class

PROGRAM main
USE GlobalData, ONLY: DFP, I4B, LGT
USE ExceptionHandler_Class, ONLY: e, EXCEPTION_INFORMATION
USE DirichletBC_Class
USE FEDomain_Class
USE AbstractMesh_Class
USE FEDOF_Class
USE ScalarField_Class
USE Display_Method
USE ReallocateUtility

IMPLICIT NONE

!----------------------------------------------------------------------------
! Parameters
!----------------------------------------------------------------------------
CHARACTER(*), PARAMETER :: tomlFileName = "./test1.toml"

!----------------------------------------------------------------------------
! Types and variables
!----------------------------------------------------------------------------
TYPE(DirichletBC_) :: obj
TYPE(FEDomain_) :: dom
TYPE(ScalarField_) :: u
TYPE(FEDOF_) :: fedof, geofedof
INTEGER(I4B) :: tsize, iNodeOnNode, iNodeOnEdge, iNodeOnFace, nrow, ncol

!----------------------------------------------------------------------------
! Allocatables and pointers
!----------------------------------------------------------------------------
CLASS(AbstractMesh_), POINTER :: cellMesh, boundaryMesh
INTEGER(I4B), ALLOCATABLE :: nodeNum(:)
REAL(DFP), ALLOCATABLE :: nodalValue(:, :)

!----------------------------------------------------------------------------
! Setting the verbosity
! In Debug mode, there will many messages printed to the screen
! Following code suppress information-exception.
!----------------------------------------------------------------------------

CALL e%setQuietMode(EXCEPTION_INFORMATION, .TRUE.)

!----------------------------------------------------------------------------
! Domain: Initiate FEDomain and print info
!----------------------------------------------------------------------------

CALL dom%ImportFromToml(tomlName="domain", filename=tomlFileName)
! CALL dom%DisplayDomainInfo(msg="DomainInfo: ")

!----------------------------------------------------------------------------
! ScalarField and FEDOF: Initiate scalarField and fedof
!----------------------------------------------------------------------------

CALL u%ImportFromToml(tomlName="u", fedof=fedof, geofedof=geofedof, dom=dom, &
filename=tomlFileName)
! CALL fedof%Display(msg="FEDOF info: ")

!----------------------------------------------------------------------------
! DirichletBC: Import from toml
!----------------------------------------------------------------------------

CALL obj%ImportFromToml(filename=tomlFileName, dom=dom, tomlName="dbc")
! CALL obj%Display("DirichletBC Info: ")

!----------------------------------------------------------------------------
! DirichletBC: Get the total node numbers
!----------------------------------------------------------------------------
tsize = obj%GetTotalNodeNum(fedof=fedof)
CALL Display(tsize, "Total Node Num: ")

!----------------------------------------------------------------------------
! DirichletBC: Get the node numbers
!----------------------------------------------------------------------------

CALL Reallocate(nodeNum, tsize, isExpand=.TRUE., expandFactor=2)
CALL obj%Get(fedof=fedof, nodeNum=nodeNum, tsize=tsize, &
iNodeOnNode=iNodeOnNode, iNodeOnEdge=iNodeOnEdge, &
iNodeOnFace=iNodeOnFace)
CALL Display(tsize, "tsize = ")
CALL Display(nodeNum(1:tsize), "nodeNum", full=.TRUE., orient="ROW")
CALL Display(iNodeOnNode, "iNodeOnNode = ")
CALL Display(iNodeOnFace, "iNodeOnFace = ")
CALL Display(iNodeOnEdge, "iNodeOnEdge = ")

!----------------------------------------------------------------------------
! DirichletBC: Get the nodal values
!----------------------------------------------------------------------------

CALL Reallocate(nodalValue, tsize, 2, isExpand=.TRUE., expandFactor=2)
CALL obj%Get(fedof=fedof, nodeNum=nodeNum, nodalValue=nodalValue, &
nrow=nrow, ncol=ncol)
CALL Display(nodalValue(1:nrow, 1:ncol), "nodalValue: ", &
full=.TRUE.)
CALL Display([nrow, ncol], "[nrow, ncol]: ")

!----------------------------------------------------------------------------
! Cleanup
!----------------------------------------------------------------------------
CALL dom%DEALLOCATE()
END PROGRAM main

We obtain the following information about the boundary condition.

PropertyValue
isInitTRUE
nameleft_bottom_fix
idof1
nodalValueTypeCONSTANT
isUserFunctionFALSE
isUseExternalFALSE
IsInitiatedTRUE
IsSelectionByMeshIDTRUE
IsSelectionByElemNumFALSE
IsSelectionByNodeNumFALSE
IsSelectionByBoxFALSE
MeshID PointFALSE
MeshID CurveTRUE
MeshID SurfaceFALSE
MeshID VolumeFALSE
ElemNum PointFALSE
ElemNum CurveFALSE
ElemNum SurfaceFALSE
ElemNum VolumeFALSE
NodeNum PointFALSE
NodeNum CurveFALSE
NodeNum SurfaceFALSE
NodeNum VolumeFALSE
Box PointFALSE
Box CurveFALSE
Box SurfaceFALSE
Box VolumeFALSE

Getting Total Node Numbers

To get the total number of nodes where the boundary condition is applied, we can use the method called GetTotalNodeNum.

CALL Display(obj%GetTotalNodeNum(fedof=fedof), "Total Node Num: ")
Results

Total Node Num: 18

There is a total of 18 nodes where the boundary condition is applied. Actually, node number 1 is counted two times because it is common to bottom and left boundaries.

Getting Node Numbers

To get the node numbers we call Get method. These node numbers are degrees of freedom numbers for FEDOF. You can use these indices to set and get the values in node fields.

CALL obj%Get(fedof=fedof, nodeNum=nodeNum, tsize=tsize, &
iNodeOnNode=iNodeOnNode, iNodeOnEdge=iNodeOnEdge, &
iNodeOnFace=iNodeOnFace)
CALL Display(tsize, "tsize = ")
CALL Display(nodeNum(1:tsize), "nodeNum", full=.TRUE., orient="ROW")
CALL Display(iNodeOnNode, "iNodeOnNode = ")
CALL Display(iNodeOnFace, "iNodeOnFace = ")
CALL Display(iNodeOnEdge, "iNodeOnEdge = ")
Results
                                nodeNum
----------------------------------------------------------------------
1 5 2 4 8 1 10 11 12 31 32 33 28 29 30 19 20 21

iNodeOnNode = 1
iNodeOnFace = 7
iNodeOnEdge = 19
  • Node number 1 to 6 (7-1) are vertex dof numbers.
  • Node number 7 to 18 (19-1) are face dof numbers
  • Node number 19 to 18 are edge dof numbers (There are no edges)
]]>
mesh fedof dirichletBC
<![CDATA[Handling Dirichlet Bounding Conditions in easifem (Part 2)]]> https://www.easifem.com/blog/DirichletBC/understanding-dirichletbc-2 https://www.easifem.com/blog/DirichletBC/understanding-dirichletbc-2 Mon, 08 Dec 2025 00:01:27 GMT Introduction
note

Before reading this post, it is recommended to read Handling Dirichlet Bounding Conditions in easifem (Part 1) first.

In this example, Dirichlet boundary conditions is given by a user defined function.

Mesh and degrees of freedom

Degree of freedom in mesh

Import from toml

We can initiate an instance of DirichletBC_ by importing the information from a toml-file. To do so, we will use the method called ImportFromToml.

Let us consider the following toml file

[domain]
filename = "./mesh/square_3x3.h5"
group = "/"
totalMedium = 1

[u]
name = "u"
engine = "NATIVE_SERIAL"
fieldType = "Normal"
spaceCompo = 1
timeCompo = 1
fedofName = "space"
geofedofName = "geofedof"

[u.geofedof]
baseContinuity = "H1"
baseInterpolation = "Lagrange"
ipType = "Equidistance"
baseType = "Monomial"

[u.space]
baseContinuity = "H1"
baseInterpolation = "Hierarchical"
# ipType = "Equidistance"
# baseType = "Monomial"
order = 4
scaleForQuadOrder = 2
quadOptName = "quadOpt"

[u.space.quadOpt]
isHomogeneous = true
quadratureType = "GaussLegendre"
# order = 2

[dbc]
name = "Bottom Space UserFunction"
idof = 1
nodalValueType = "Space"
isUserFunction = true

[dbc.function]
name = "func"
returnType = "Scalar"
numReturns = 1
argType = "Space"
numArgs = 3
luaScript = "test2.lua"
luaFunctionName = "BottomFunc"

[dbc.boundary]
isSelectionByMeshID = true

[dbc.boundary.meshID]
line = [1]

We use the following code to import the boundary condition from the above toml file.

!> author: Vikas Sharma, Ph. D.
! date: 2025-10-23
! summary: Study of DirichletBC class

PROGRAM main
USE GlobalData, ONLY: DFP, I4B, LGT
USE ExceptionHandler_Class, ONLY: e, EXCEPTION_INFORMATION
USE DirichletBC_Class
USE FEDomain_Class
USE AbstractMesh_Class
USE FEDOF_Class
USE ScalarField_Class
USE Display_Method
USE ReallocateUtility

IMPLICIT NONE

!----------------------------------------------------------------------------
! Parameters
!----------------------------------------------------------------------------
CHARACTER(*), PARAMETER :: tomlFileName = "./test2.toml"

!----------------------------------------------------------------------------
! Types and variables
!----------------------------------------------------------------------------
TYPE(DirichletBC_) :: obj
TYPE(FEDomain_) :: dom
TYPE(ScalarField_) :: u
TYPE(FEDOF_) :: fedof, geofedof
INTEGER(I4B) :: tsize, iNodeOnNode, iNodeOnEdge, iNodeOnFace, nrow, ncol

!----------------------------------------------------------------------------
! Allocatables and pointers
!----------------------------------------------------------------------------
CLASS(AbstractMesh_), POINTER :: cellMesh, boundaryMesh
INTEGER(I4B), ALLOCATABLE :: nodeNum(:)
REAL(DFP), ALLOCATABLE :: nodalValue(:, :)

!----------------------------------------------------------------------------
! Setting the verbosity
! In Debug mode, there will many messages printed to the screen
! Following code suppress information-exception.
!----------------------------------------------------------------------------

! CALL e%setQuietMode(EXCEPTION_INFORMATION, .TRUE.)

!----------------------------------------------------------------------------
! Domain: Initiate FEDomain and print info
!----------------------------------------------------------------------------

CALL dom%ImportFromToml(tomlName="domain", filename=tomlFileName)
! CALL dom%DisplayDomainInfo(msg="DomainInfo: ")

!----------------------------------------------------------------------------
! ScalarField and FEDOF: Initiate scalarField and fedof
!----------------------------------------------------------------------------

CALL u%ImportFromToml(tomlName="u", fedof=fedof, geofedof=geofedof, dom=dom, &
filename=tomlFileName)
! CALL fedof%Display(msg="FEDOF info: ")

!----------------------------------------------------------------------------
! DirichletBC: Import from toml
!----------------------------------------------------------------------------

CALL obj%ImportFromToml(filename=tomlFileName, dom=dom, tomlName="dbc")
! CALL obj%Display("DirichletBC Info: ")

!----------------------------------------------------------------------------
! DirichletBC: Get the total node numbers
!----------------------------------------------------------------------------
tsize = obj%GetTotalNodeNum(fedof=fedof)
CALL Display(tsize, "Total Node Num: ")

!----------------------------------------------------------------------------
! DirichletBC: Get the node numbers
!----------------------------------------------------------------------------

CALL Reallocate(nodeNum, tsize, isExpand=.TRUE., expandFactor=2)
CALL obj%Get(fedof=fedof, nodeNum=nodeNum, tsize=tsize, &
iNodeOnNode=iNodeOnNode, iNodeOnEdge=iNodeOnEdge, &
iNodeOnFace=iNodeOnFace)
CALL Display(tsize, "tsize = ")
CALL Display(nodeNum(1:tsize), "nodeNum", full=.TRUE., orient="ROW")
CALL Display(iNodeOnNode, "iNodeOnNode = ")
CALL Display(iNodeOnFace, "iNodeOnFace = ")
CALL Display(iNodeOnEdge, "iNodeOnEdge = ")

!----------------------------------------------------------------------------
! DirichletBC: Get the nodal values
!----------------------------------------------------------------------------

CALL Reallocate(nodalValue, tsize, 2, isExpand=.TRUE., expandFactor=2)
CALL Display(SHAPE(nodalValue), "shape(nodalValue) before Get: ")
nrow = 0; ncol = 0
CALL obj%Get(fedof=fedof, geofedof=geofedof, nodeNum=nodeNum, &
nodalValue=nodalValue, nrow=nrow, ncol=ncol)

CALL Display(nodeNum(1:nrow), "nodeNum(1:nrow): ", &
full=.TRUE., advance="NO")
CALL Display(nodalValue(1:nrow, 1:ncol), "nodalValue: ", &
full=.TRUE.)
CALL Display([nrow, ncol], "[nrow, ncol]: ")

!----------------------------------------------------------------------------
! Cleanup
!----------------------------------------------------------------------------

CALL dom%DEALLOCATE()

END PROGRAM main

We obtain the following information about the boundary condition.

Results

DirichletBC Info

PropertyValue
isInitTRUE
nameBottom Space UserFunction
idof1
nodalValueTypeSPACE
isUserFunctionTRUE
isUseExternalFALSE

Boundary

PropertyValue
IsInitiatedTRUE
IsSelectionByMeshIDTRUE
IsSelectionByElemNumFALSE
IsSelectionByNodeNumFALSE
IsSelectionByBoxFALSE

Allocation Status

Element TypePointCurveSurfaceVolume
MeshID ALLOCATEDFALSETRUEFALSEFALSE
ElemNum ALLOCATEDFALSEFALSEFALSEFALSE
NodeNum ALLOCATEDFALSEFALSEFALSEFALSE
Box ALLOCATEDFALSEFALSEFALSEFALSE

Getting Total Node Numbers

To get the total number of nodes where the boundary condition is applied, we can use the method called GetTotalNodeNum.

CALL Display(obj%GetTotalNodeNum(fedof=fedof), "Total Node Num: ")
Results

Total Node Num: 9

Getting Node Numbers

To get the node numbers we call Get method. These node numbers are degrees of freedom numbers for FEDOF. You can use these indices to set and get the values in node fields.

CALL obj%Get(fedof=fedof, nodeNum=nodeNum, tsize=tsize, &
iNodeOnNode=iNodeOnNode, iNodeOnEdge=iNodeOnEdge, &
iNodeOnFace=iNodeOnFace)
CALL Display(tsize, "tsize = ")
CALL Display(nodeNum(1:tsize), "nodeNum", full=.TRUE., orient="ROW")
CALL Display(iNodeOnNode, "iNodeOnNode = ")
CALL Display(iNodeOnFace, "iNodeOnFace = ")
CALL Display(iNodeOnEdge, "iNodeOnEdge = ")
Results
              nodeNum
----------------------------------
1 5 2 10 11 12 31 32 33

iNodeOnNode = 1
iNodeOnFace = 4
iNodeOnEdge = 10
  • Node number 1 to 3 are vertex dof numbers.
  • Node number 4 to 9 are face dof numbers
]]>
mesh fedof dirichletBC
<![CDATA[Creating structured mesh in 2D using EASIFEM]]> https://www.easifem.com/blog/generating-2d-mesh-in-easifem https://www.easifem.com/blog/generating-2d-mesh-in-easifem Mon, 02 Jun 2025 04:27:15 GMT Creating a structured mesh of quadrangles

To create a structured mesh of quadrangles in easifem, we will need following classes:

  • GmshStructuredMesh_Class: This is high-level interface on top of Gmsh library to create structured meshes.
  • Gmsh_Class: Interface to Gmsh library.
  • MSHFile_Class: Reading gmsh files
  • HDF5File_Class: Writing gmsh files to HDF5 file.

Very small mesh of quadrangles

PROGRAM main
USE GmshStructuredMesh_Class
USE Gmsh_Class
USE FPL
USE GlobalData
USE MSHFIle_Class
USE HDF5File_Class

IMPLICIT NONE

TYPE(GmshStructuredMesh_) :: obj
TYPE(Gmsh_) :: gmsh
TYPE(ParameterList_) :: param
! CHARACTER(*), PARAMETER :: title = "small_quad4_mesh"
CHARACTER(*), PARAMETER :: title = "very_small_quad4_mesh"
REAL(DFP), PARAMETER :: pointsOnAxis1(2) = [0.0, 1.0]
REAL(DFP), PARAMETER :: pointsOnAxis2(2) = [0.0, 1.0]

INTEGER(I4B), PARAMETER :: transfinitePointsOnAxis1(1) = [3]
INTEGER(I4B), PARAMETER :: transfinitePointsOnAxis2(1) = [3]

INTEGER(I4B) :: ierr

TYPE(MSHFile_) :: mshFile
TYPE(HDF5File_) :: hdf5file

CALL FPL_Init()
CALL param%Initiate()

CALL SetGmshStructuredMeshParam(param=param, &
filename=title//".msh", &
pointsOnAxis1=pointsOnAxis1, &
pointsOnAxis2=pointsOnAxis2, &
transfinitePointsOnAxis1=transfinitePointsOnAxis1, &
transfinitePointsOnAxis2=transfinitePointsOnAxis2, &
recombineAll=.TRUE.)

CALL obj%Initiate(param)

ierr = gmsh%initialize()
ierr = gmsh%model%add("GmshStructuredMesh2D")
CALL obj%Generate(gmsh)
! ierr = gmsh%fltk%run()
ierr = gmsh%finalize()

CALL param%DEALLOCATE()
CALL obj%DEALLOCATE()

CALL mshFile%Initiate(filename=title//'.msh', STATUS="OLD", ACTION="READ")
CALL mshFile%OPEN()
CALL mshFile%READ()
CALL hdf5file%Initiate(title//'.h5', MODE="NEW")
CALL hdf5file%OPEN()
CALL mshFile%Export(hdf5=hdf5file, group="")
CALL mshFile%DEALLOCATE()
CALL hdf5file%DEALLOCATE()

END PROGRAM main

Creating a structured mesh of quadrangles with two regions

Very small mesh of quadrangles with two regions

PROGRAM main
USE GmshStructuredMesh_Class
USE Gmsh_Class
USE FPL
USE GlobalData
USE MSHFile_Class
USE HDF5File_Class

IMPLICIT NONE

TYPE(GmshStructuredMesh_) :: obj
TYPE(Gmsh_) :: gmsh
TYPE(ParameterList_) :: param
CHARACTER(*), PARAMETER :: title = "very_small_quad4_mesh_two_region"
REAL(DFP), PARAMETER :: pointsOnAxis1(3) = [0.0, 1.0, 2.0]
REAL(DFP), PARAMETER :: pointsOnAxis2(2) = [0.0, 1.0]

INTEGER(I4B), PARAMETER :: transfinitePointsOnAxis1(2) = [3, 3]
INTEGER(I4B), PARAMETER :: transfinitePointsOnAxis2(1) = [2]

INTEGER(I4B) :: ierr

TYPE(MSHFile_) :: mshFile
TYPE(HDF5File_) :: hdf5file

CALL FPL_Init()
CALL param%Initiate()

CALL SetGmshStructuredMeshParam( &
param=param, &
filename=title//".msh", &
pointsOnAxis1=pointsOnAxis1, &
pointsOnAxis2=pointsOnAxis2, &
transfinitePointsOnAxis1=transfinitePointsOnAxis1, &
transfinitePointsOnAxis2=transfinitePointsOnAxis2, &
recombineAll=.TRUE.)

CALL obj%Initiate(param)

ierr = gmsh%initialize()
ierr = gmsh%model%add("GmshStructuredMesh2D")
CALL obj%Generate(gmsh)
! ierr = gmsh%fltk%run()
ierr = gmsh%finalize()

CALL param%DEALLOCATE()
CALL obj%DEALLOCATE()

CALL mshFile%Initiate(filename=title//'.msh', STATUS="OLD", ACTION="READ")
CALL mshFile%OPEN()
CALL mshFile%READ()
CALL hdf5file%Initiate(title//'.h5', MODE="NEW")
CALL hdf5file%OPEN()
CALL mshFile%Export(hdf5=hdf5file, group="")
CALL mshFile%DEALLOCATE()
CALL hdf5file%DEALLOCATE()

END PROGRAM main

Creating a structured mesh of triangles

Small mesh of linear triangles

PROGRAM main
USE easifemBase
USE Gmsh_Class
USE HDF5File_Class
USE MSHFile_Class

TYPE(Gmsh_) :: gmsh

CHARACTER(LEN=*), PARAMETER :: title = "small_tri3_mesh"
REAL(DFP), PARAMETER :: lx = 2.0
REAL(DFP), PARAMETER :: ly = 2.0
INTEGER(I4B), PARAMETER :: order = 1

REAL(DFP), PARAMETER :: meshSize = 1.0
REAL(DFP), PARAMETER :: corner(3) = 0.0_DFP
INTEGER(I4B) :: ierr
REAL(DFP) :: x, y, z, lc

TYPE(MSHFile_) :: mshFile
TYPE(HDF5File_) :: hdf5file

ierr = gmsh%Initialize()
ierr = gmsh%model%add(title)

x = corner(1); y = corner(2); z = corner(3); lc = meshSize
ierr = gmsh%model%geo%addPoint(x=x, y=y, z=z, meshSize=lc, tag=1)

x = x + lx; y = y; z = z; lc = lc
ierr = gmsh%model%geo%addPoint(x=x, y=y, z=z, meshSize=lc, tag=2)

x = x; y = y + ly; z = z; lc = lc
ierr = gmsh%model%geo%addPoint(x=x, y=y, z=z, meshSize=lc, tag=3)

x = corner(1); y = y; z = z; lc = lc
ierr = gmsh%model%geo%addPoint(x=x, y=y, z=z, meshSize=lc, tag=4)

ierr = gmsh%model%geo%addLine(1, 2, 1)
ierr = gmsh%model%geo%addLine(2, 3, 2)
ierr = gmsh%model%geo%addLine(3, 4, 3)
ierr = gmsh%model%geo%addLine(4, 1, 4)

ierr = gmsh%model%geo%addCurveLoop([1, 2, 3, 4], tag=1)

ierr = gmsh%model%geo%addPlaneSurface([1], 1)

ierr = gmsh%model%geo%synchronize()

ierr = gmsh%option%setNumber(VALUE=1, name="Mesh.SaveAll")
ierr = gmsh%model%mesh%generate(2)

ierr = gmsh%model%mesh%setOrder(order)

ierr = gmsh%WRITE(title//'.msh')

ierr = gmsh%Finalize()

CALL mshFile%Initiate(filename=title//'.msh', STATUS="OLD", ACTION="READ")
CALL mshFile%OPEN()
CALL mshFile%READ()
CALL hdf5file%Initiate(title//'.h5', MODE="NEW")
CALL hdf5file%OPEN()
CALL mshFile%Export(hdf5=hdf5file, group="")
CALL mshFile%DEALLOCATE()
CALL hdf5file%DEALLOCATE()

END PROGRAM main
]]>
mesh gmsh tutorial
<![CDATA[Setting up a new kernel using EASIFEM]]> https://www.easifem.com/blog/how-to-setup-a-new-kernel-in-easifem https://www.easifem.com/blog/how-to-setup-a-new-kernel-in-easifem Sun, 11 May 2025 09:05:24 GMT This post explains the meaning of kernel in EASIFEM platform. It also describe the steps to create a new kernel in EASIFEM.

What is a kernel?

A kernel is a computer program written in object oriented programming paradigm which attempts to solve a partial differential equation. In easifem, the term kernel is used for solving a particular PDE.

Why particular PDE?

In our experience when we focus on solving a particular PDE, we can design the kernel quickly and more efficiently. This is because the kernel has specific tasks to perform with limited number of kernel parameters. We believe that this is a good balance between flexibility, efficiency and speed of development. In the past, we have tried to create a generic PDE solver type kernel but after few years the kernel became too complex and difficult to maintain by a group of developers. Therefore, we want to define a kernel for a specific PDE.

Is kernel flexible?

Yes, the kernel has sufficient amount of flexibility. For example, the coefficient of PDE can be constant, spatially changing or time dependent. The boundary condition can also be constant, space, time, or space-time dependent.

Kernel is blend of procedural programming and object oriented programming?

A kernel is a blend of procedural programming and object oriented programming. The procedural programming comes into the picture because a kernel is trying to solve a specific PDE by using a specific method. In this sense, the kernel's design is driven keeping procedure programming in mind. However, a kernel can solve several problems governed by the same PDE. These problems can have different types of boundary conditions and material properties. Also, we can use several numerical methods to solve the problem. In order to facilitate these objectives, we use object oriented programming.

Git ignore file

Add the following .gitignore file in the root directory of your kernel. This will help you to ignore the unnecessary files which are not required for the kernel.

Git ignore file for EASIFEM projects
*.a
*.mod
*.smod
*.o
*.out
*.i90
*.if90
*.DS_Store
*.prj
*.drawio
*.log
*.eps
*.h5
*.gp
*.msh
*.vtu
*.ipynb
*.dat
*.mtx
*.eps
*.pdf
*.geo
*.ps
*.png
*.jpg
*.plt
*.csv

*/build/
**/build
build/

_packages/
__*

log.txt
test.txt

## Obsidian related
.obsidian

# VSCode related
vscode-settings
.vscode/
settings.json

## Docusaurus related

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

.DS_Store
.$*

~*

Directory structure

Create following directory in the root directory of your kernel.

  1. src: This directory will contain the source code of your kernel.
  2. docs: This directory will contain the documentation of your kernel.
  3. tests: This directory will contain the test code of your kernel.
  4. examples: This directory will contain the example code and tutorials of your kernel.
  5. cmake: This directory will contain the cmake files necessary to build your kernel.
  6. docker: This directory will contain the docker files.
  7. bin: This directory will contain the binary files of application based on your kernel
  8. media: This directory will contain the figures and images used for the documentation
  9. pages: This directory will contain the pages for documentation by using ford.

Structure of src directory

The src directory will contain the following directories and files.

  • modules: This directory contains the header files of the kernel. These header files are called modules.
  • submodules: This directory contains the submodules of the kernel. These submodules are contains the implementation of the header files.

Let us say you want to develop a class called Abstract1DUVSTFEM_ (note that all user defined data types end with an underscore). Then you should create a directory called Abstract1DUVSTFEM in the src/modules directory.

Now in Abstract1DUVSTFEM directory create another directory called src which will contain the header file. In this source directory you will create Abstract1DUVSTFEM_Class.F90.

In Abstract1DUVSTFEM directory create a file called CMakeLists.txt. The content of this is given below.

modules/Abstract1DUVSTFEM/CMakeLists.txt
set(src_path "${CMAKE_CURRENT_LIST_DIR}/src/")
target_sources(${PROJECT_NAME} PRIVATE ${src_path}/Abstract1DUVSTFEM_Class.F90)

If you follow the above steps the src directory will look like the following.

src
├── modules
│   └── Abstract1DUVSTFEM
│   ├── CMakeLists.txt
│   └── src
│   └── Abstract1DUVSTFEM_Class.F90
└── submodules

Now we will implement the methods defined in Abstract1DUVSTFEM_Class.F90 in the src/submodules directory. Create a directory called Abstract1DUVSTFEM in the src/submodules directory. In this directory create a file called CMakeLists.txt. In this directory create a directory called src which will contain the submodules of module Abstract1DUVSTFEM_Class. The structure of the directory will look like this.

The content of CMakeLists.txt file will be as follows.

submodules/Abstract1DUVSTFEM/CMakeLists.txt
set(src_path "${CMAKE_CURRENT_LIST_DIR}/src/")
target_sources(
${PROJECT_NAME}
PRIVATE ${src_path}/[email protected]
src
├── modules
│   └── Abstract1DUVSTFEM
│   ├── CMakeLists.txt
│   └── src
│   └── Abstract1DUVSTFEM_Class.F90
└── submodules
├── Abstract1DUVSTFEM
│   └── src
│   ├── [email protected]
└── CMakeLists.txt

Module and Submodule CMake file

In modules and submodules directory create a file called CMakeLists.txt. The CMake file will look like this.

modules/CMakeLists.txt
# AbstractSTFEM
include(${CMAKE_CURRENT_LIST_DIR}/Abstract1DUVSTFEM/CMakeLists.txt)
submodules/CMakeLists.txt
# AbstractSTFEM
include(${CMAKE_CURRENT_LIST_DIR}/Abstract1DUVSTFEM/CMakeLists.txt)

Now the structure of the src directory will look like this.

src
├── modules
│   ├── Abstract1DUVSTFEM
│   │   ├── CMakeLists.txt
│   │   └── src
│   │   └── Abstract1DUVSTFEM_Class.F90
│   └── CMakeLists.txt
└── submodules
├── Abstract1DUVSTFEM
│   ├── CMakeLists.txt
│   └── src
│   ├── [email protected]
├── CMakeLists.txt

Now we need to write the main CMakeLists.txt file in the root of our project. That is, the parent directory of src directory. We will describe the content of this file in another blog post.

Click here to see the content
CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0 FATAL_ERROR)
set(PROJECT_NAME "easifemOneDimElasticity")
project(${PROJECT_NAME})

enable_language(Fortran C CXX)

set(VERSION_MAJOR "25")
set(VERSION_MINOR "04")
set(VERSION_BugFix "1")
set(PROJECT_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_BugFix})

set(CMAKE_PROJECT_DESCRIPTION
"${PROJECT_NAME} is part of EASIFEM platform.
EASIFEM: Expandable and Scalable Infrastructure for Finite Element Methods.
This program solves one dimensional wave propogation problems using finite element
methods including space-time finite element methods.
")

set(CMAKE_PROJECT_HOMEPAGE_URL "https://www.easifem.com")

set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}")

include(CMakePrintHelpers)
include(FortranCInterface)

FortranCInterface_VERIFY()

list(APPEND TARGET_COMPILE_DEF "-DUSE_CMAKE")

# find my cmake modules here...
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)

# check error
if(" ${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL " ${CMAKE_CURRENT_BINARY_DIR}")
message(
FATAL_ERROR
"[ERROR] :: Build directory and Source directory cannot be same.")
endif()

# make directories include(${PROJECT_SOURCE_DIR}/cmake/makeDirs.cmake)
#
include(GNUInstallDirs)

set(CMAKE_Fortran_MODULE_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR})

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})

set(INSTALL_LIBDIR
${CMAKE_INSTALL_LIBDIR}
CACHE PATH "Installation location of lib")

set(INSTALL_INCLUDEDIR
${CMAKE_INSTALL_INCLUDEDIR}
CACHE PATH "Installation location of module files")

set(INSTALL_BINDIR
${CMAKE_INSTALL_BINDIR}
CACHE PATH "Installation location of binary files")

if(WIN32 AND NOT CYGWIN)
set(DEF_INSTALL_CMAKEDIR CMake)
else()
set(DEF_INSTALL_CMAKEDIR share/cmake/${PROJECT_NAME})
endif()

set(INSTALL_CMAKEDIR
${DEF_INSTALL_CMAKEDIR}
CACHE PATH "Installation directory for CMake files")

foreach(p LIB BIN INCLUDE CMAKE)
file(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${INSTALL_${p}DIR} _path)
message(STATUS "Installing ${p} componenets to ${_path}")
endforeach()

option(BUILD_SHARED_LIBS "Build shared library" ON)

if(BUILD_SHARED_LIBS)
message(STATUS "${PROJECT_NAME} will be built as a shared library.")
add_library(${PROJECT_NAME} SHARED "")
set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE TRUE)
else()
message(STATUS "${PROJECT_NAME} will be built as a static library.")
add_library(${PROJECT_NAME} STATIC "")
endif()

message(
STATUS
"[INFO] :: Is the Fortran compiler loaded? ${CMAKE_Fortran_COMPILER_LOADED}"
)

if(CMAKE_Fortran_COMPILER_LOADED)
message(STATUS "[INFO] :: Fortran compiler: ${CMAKE_Fortran_COMPILER_ID}")
message(
STATUS
"[INFO] :: Fortran compiler version is: ${CMAKE_Fortran_COMPILER_VERSION}"
)
endif()

if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE
Release
CACHE STRING "Build type" FORCE)
endif()

if(${CMAKE_Fortran_COMPILER_ID} STREQUAL "GNU" OR Fortran_COMPILER_NAME MATCHES
"gfortran*")

list(
APPEND
FORTRAN_FLAGS
"-ffree-form"
"-ffree-line-length-none"
"-std=f2018"
"-fimplicit-none"
"-fno-range-check")

list(APPEND FORTRAN_FLAGS_RELEASE "-O3")

if(APPLE)
list(
APPEND
FORTRAN_FLAGS_DEBUG
"-fbounds-check"
"-g"
"-fbacktrace"
"-Wextra"
"-Wall"
# "-fprofile-arcs"
"-ftest-coverage"
"-Wimplicit-interface")

else()
list(
APPEND
FORTRAN_FLAGS_DEBUG
"-fbounds-check"
"-g"
"-fbacktrace"
"-Wextra"
"-Wall"
# "-fprofile-arcs"
"-ftest-coverage"
"-Wimplicit-interface")
endif()

elseif(${CMAKE_Fortran_COMPILER_ID} STREQUAL "Intel" OR Fortran_COMPILER_NAME
MATCHES "ifort*")
list(APPEND FORTRAN_FLAGS "-r8" "-W1")
list(APPEND FORTRAN_FLAGS_RELEASE "-O3")
list(
APPEND
FORTRAN_FLAGS_DEBUG
"-O0"
"-traceback"
"-g"
"-debug all"
"-check all"
"-ftrapuv"
"-warn"
"nointerfaces")

elseif(${CMAKE_Fortran_COMPILER_ID} STREQUAL "XL" OR Fortran_COMPILER_NAME
MATCHES "xlf*")

list(APPEND FORTRAN_FLAGS "-q64" "-qrealsize=8" "-qsuffix=f=f90:cpp=f90")
list(APPEND FORTRAN_FLAGS_RELEASE "-O3" "-qstrict")
list(APPEND FORTRAN_FLAGS_DEBUG "-O0" "-g" "-qfullpath" "-qkeepparm")

else()
message(ERROR "[ERROR] :: No optimized Fortran compiler flags are known")
endif()

cmake_print_variables(FORTRAN_FLAGS)
cmake_print_variables(FORTRAN_FLAGS_RELEASE)
cmake_print_variables(FORTRAN_FLAGS_DEBUG)

# include(${PROJECT_SOURCE_DIR}/cmake/targetCompileOpts.cmake)
target_compile_options(
${PROJECT_NAME}
PRIVATE ${TARGET_COMPILE_OPT} ${FORTRAN_FLAGS}
"$<$<CONFIG:Debug>:${FORTRAN_FLAGS_DEBUG}>"
"$<$<CONFIG:Release>:${FORTRAN_FLAGS_RELEASE}>")

# include(${PROJECT_SOURCE_DIR}/cmake/targetIncludeDirs.cmake)
target_include_directories(
${PROJECT_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_Fortran_MODULE_DIRECTORY}>
$<INSTALL_INTERFACE:${INSTALL_INCLUDEDIR}>)

# include(${PROJECT_SOURCE_DIR}/cmake/targetProperties.cmake) target properties
set_target_properties(
${PROJECT_NAME}
PROPERTIES POSITION_INDEPENDENT_CODE 1
SOVERSION ${VERSION_MAJOR}
OUTPUT_NAME ${PROJECT_NAME}
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
MACOSX_RPATH ON
WINDOWS_EXPORT_ALL_SYMBOLS ON)

# include(${PROJECT_SOURCE_DIR}/cmake/targetCompileDefs.cmake)
option(USE_REAL32 OFF)
option(USE_REAL64 ON)

if(USE_REAL32)
list(APPEND TARGET_COMPILE_DEF "-DUSE_Real32")
endif()

if(USE_REAL64)
list(APPEND TARGET_COMPILE_DEF "-DUSE_Real64")
endif()

option(USE_INT32 ON)
if(USE_INT32)
list(APPEND TARGET_COMPILE_DEF "-DUSE_Int32")
endif()

option(USE_INT64 OFF)
if(USE_INT64)
list(APPEND TARGET_COMPILE_DEF "-DUSE_Int64")
endif()

list(APPEND TARGET_COMPILE_DEF "-D${CMAKE_HOST_SYSTEM_NAME}_SYSTEM")

# DEFINE DEBUG
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
list(APPEND TARGET_COMPILE_DEF "-DDEBUG_VER")
endif()

# include(${PROJECT_SOURCE_DIR}/cmake/install.cmake) Installation
install(
DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY}
DESTINATION "./"
COMPONENT "${PROJECT_NAME}")

install(
EXPORT ${TARGETS_EXPORT_NAME}
FILE "${TARGETS_EXPORT_NAME}.cmake"
NAMESPACE ${namespace}::
DESTINATION ${INSTALL_CMAKEDIR}
COMPONENT "${PROJECT_NAME}")

include(CMakePackageConfigHelpers)

write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
VERSION "${PROJECT_VERSION}"
COMPATIBILITY AnyNewerVersion)

configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION ${INSTALL_CMAKEDIR}
PATH_VARS INSTALL_INCLUDEDIR)

install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION ${INSTALL_CMAKEDIR}
COMPONENT "${PROJECT_NAME}-dev")

# linking easifemClasses,
# this easifemClasses library is already linked with easifemBase
# so no need to link with easifemBase again.
find_package(easifemClasses REQUIRED)
if(easifemBase_FOUND)
message(STATUS "FOUND easifemClasses")
else()
message(ERROR "NOT FOUND easifemClasses")
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC easifemClasses::easifemClasses)

# Add source files
include(src/modules/CMakeLists.txt)
include(src/submodules/CMakeLists.txt)

# this should be in the end.
target_compile_definitions(${PROJECT_NAME} PUBLIC ${TARGET_COMPILE_DEF})
message(STATUS "[INFO] :: Compiler definition : ${TARGET_COMPILE_DEF}")

install(
TARGETS ${PROJECT_NAME} ${C_PROJECTS}
EXPORT ${TARGETS_EXPORT_NAME}
COMPONENT "${PROJECT_NAME}"
ARCHIVE DESTINATION ${INSTALL_LIBDIR}
RUNTIME DESTINATION ${INSTALL_BINDIR}
LIBRARY DESTINATION ${INSTALL_LIBDIR})

# include(${PROJECT_SOURCE_DIR}/cmake/packaging.cmake)

The above see make file needs Config.cmake.in file. The content of this file is given below.

Config.cmake.in
@PACKAGE_INIT@

FIND_PACKAGE(easifemClasses REQUIRED)

set_and_check(
"@PROJECT_NAME@_INCLUDE_DIR" "@PACKAGE_INSTALL_INCLUDEDIR@")

include(
"${CMAKE_CURRENT_LIST_DIR}/@[email protected]")

check_required_components(
"@PROJECT_NAME@"
)

Configuration for easifem CLI.

Now that we have added the source code and setup the CMake files. We need to specify the toml configuration files for the kernel, so that we can build and install easifemOneDimElasticity kernel using easifem CLI. To do so, create a file called oneDimElasticity.toml in the directory where easifem plugins are kept. In our case it is kept at ~/.config/easifem/plugins/oneDimElasticity.toml. The content of this file is given below.

oneDimElasticity.toml
name = "oneDimElasticity"
isActive = true
buildSystem = "cmake"
git = 'github.com/easifem/oneDimElasticity.git'
sourceDir = "${HOME}/Dropbox/easifem/oneDimElasticity"
installDir = "${HOME}/.easifem/install/oneDimElasticity/"
buildType = "Debug"
buildSharedLibs = true
buildStaticLibs = true
libName = "easifemOneDimElasticity"
targetName = "easifemOneDimElasticity"
projectName = "easifemOneDimElasticity"

runtest = true
license = "GPL3"
# buildOptions = [ "-D USE_GMSH_SDK:BOOL=ON"]

dependencies = ["classes"]

Building the kernel by using easifem CLI

To build the kernel using easifem CLI, run the following command.

easifem dev oneDimElasticity

Installing the kernel by using easifem CLI

To install the kernel using easifem CLI, run the following command.

easifem install oneDimElasticity

To install the kernel with downloading the files from git, that is, by using the local source files, use the following command. In this case you must specify the sourceDir option in the above toml file.

easifem install oneDimElasticity --no-download
]]>
kernel guide tutorial
<![CDATA[Handling Dirichlet Bounding Conditions in easifem (Part 1)]]> https://www.easifem.com/blog/DirichletBC/understanding-dirichletbc-3 https://www.easifem.com/blog/DirichletBC/understanding-dirichletbc-3 Thu, 22 Aug 2024 05:33:02 GMT Introduction

To apply boundary condition in FEM computation, EASIFEM, provides a class called DirichletBC_.

info

DirichletBC_ is a subclass of AbstractBC.

To understand how DirichletBC works, lets consider an example of linear elasticity. Let's say we want to apply the following boundary condition.

u=U0, on Γ\mathbf{u} = \mathbf{U}_{0}, \text{ on } \Gamma

We may think that there is only one boundary condition. But in easifem this is not the case. Actually, u\mathbf{u}, has three components in 3D (and two components in 2D). Therefore, the above boundary condition is actually boundary condition for uxu_x, uyu_y, and uzu_z. So, we have three boundary condition on a given boundary Γ\Gamma.

The second point, which is quite obvious, is that every boundary condition has two things:

  • The boundary
  • The value (condition)

To define the boundary EASIFEM employs the MeshSelection class. The value can be specified in several ways as mentioned below in this section.

note

Several instances of DirichletBC can have same boundary but different condition.

Import from toml

We can initiate an instance of DirichletBC_ by importing the information from a toml-file. To do so, we will use the method called ImportFromToml.

Let us consider the following toml file

[domain]
filename = "./mesh/square_3x3.h5"
group = "/"
totalMedium = 1

[u]
name = "u"
engine = "NATIVE_SERIAL"
fieldType = "Normal"
spaceCompo = 1
timeCompo = 1
fedofName = "space"
geofedofName = "geofedof"

[u.geofedof]
baseContinuity = "H1"
baseInterpolation = "Lagrange"
ipType = "Equidistance"
baseType = "Monomial"

[u.space]
baseContinuity = "H1"
baseInterpolation = "Hierarchical"
# ipType = "Equidistance"
# baseType = "Monomial"
order = 4
scaleForQuadOrder = 2
quadOptName = "quadOpt"

[u.space.quadOpt]
isHomogeneous = true
quadratureType = "GaussLegendre"
# order = 2

[dbc]
name = "left_bottom_fix"
idof = 1
nodalValueType = "Constant"
value = 10.0
[dbc.boundary]
isSelectionByMeshID = true
[dbc.boundary.meshID]
line = [1, 4]

We use the following code to import the boundary condition from the above toml file.

!> author: Vikas Sharma, Ph. D.
! date: 2025-10-23
! summary: Study of DirichletBC class

PROGRAM main
USE GlobalData, ONLY: DFP, I4B, LGT
USE ExceptionHandler_Class, ONLY: e, EXCEPTION_INFORMATION
USE DirichletBC_Class
USE FEDomain_Class
USE AbstractMesh_Class
USE FEDOF_Class
USE ScalarField_Class
USE Display_Method
USE ReallocateUtility

IMPLICIT NONE

!----------------------------------------------------------------------------
! Parameters
!----------------------------------------------------------------------------
CHARACTER(*), PARAMETER :: tomlFileName = "./test1.toml"

!----------------------------------------------------------------------------
! Types and variables
!----------------------------------------------------------------------------
TYPE(DirichletBC_) :: obj
TYPE(FEDomain_) :: dom
TYPE(ScalarField_) :: u
TYPE(FEDOF_) :: fedof, geofedof
INTEGER(I4B) :: tsize, iNodeOnNode, iNodeOnEdge, iNodeOnFace, nrow, ncol

!----------------------------------------------------------------------------
! Allocatables and pointers
!----------------------------------------------------------------------------
CLASS(AbstractMesh_), POINTER :: cellMesh, boundaryMesh
INTEGER(I4B), ALLOCATABLE :: nodeNum(:)
REAL(DFP), ALLOCATABLE :: nodalValue(:, :)

!----------------------------------------------------------------------------
! Setting the verbosity
! In Debug mode, there will many messages printed to the screen
! Following code suppress information-exception.
!----------------------------------------------------------------------------

CALL e%setQuietMode(EXCEPTION_INFORMATION, .TRUE.)

!----------------------------------------------------------------------------
! Domain: Initiate FEDomain and print info
!----------------------------------------------------------------------------

CALL dom%ImportFromToml(tomlName="domain", filename=tomlFileName)
! CALL dom%DisplayDomainInfo(msg="DomainInfo: ")

!----------------------------------------------------------------------------
! ScalarField and FEDOF: Initiate scalarField and fedof
!----------------------------------------------------------------------------

CALL u%ImportFromToml(tomlName="u", fedof=fedof, geofedof=geofedof, dom=dom, &
filename=tomlFileName)
! CALL fedof%Display(msg="FEDOF info: ")

!----------------------------------------------------------------------------
! DirichletBC: Import from toml
!----------------------------------------------------------------------------

CALL obj%ImportFromToml(filename=tomlFileName, dom=dom, tomlName="dbc")
! CALL obj%Display("DirichletBC Info: ")

!----------------------------------------------------------------------------
! DirichletBC: Get the total node numbers
!----------------------------------------------------------------------------
tsize = obj%GetTotalNodeNum(fedof=fedof)
CALL Display(tsize, "Total Node Num: ")

!----------------------------------------------------------------------------
! DirichletBC: Get the node numbers
!----------------------------------------------------------------------------

CALL Reallocate(nodeNum, tsize, isExpand=.TRUE., expandFactor=2)
CALL obj%Get(fedof=fedof, nodeNum=nodeNum, tsize=tsize, &
iNodeOnNode=iNodeOnNode, iNodeOnEdge=iNodeOnEdge, &
iNodeOnFace=iNodeOnFace)
CALL Display(tsize, "tsize = ")
CALL Display(nodeNum(1:tsize), "nodeNum", full=.TRUE., orient="ROW")
CALL Display(iNodeOnNode, "iNodeOnNode = ")
CALL Display(iNodeOnFace, "iNodeOnFace = ")
CALL Display(iNodeOnEdge, "iNodeOnEdge = ")

!----------------------------------------------------------------------------
! DirichletBC: Get the nodal values
!----------------------------------------------------------------------------

CALL Reallocate(nodalValue, tsize, 2, isExpand=.TRUE., expandFactor=2)
CALL obj%Get(fedof=fedof, nodeNum=nodeNum, nodalValue=nodalValue, &
nrow=nrow, ncol=ncol)
CALL Display(nodalValue(1:nrow, 1:ncol), "nodalValue: ", &
full=.TRUE.)
CALL Display([nrow, ncol], "[nrow, ncol]: ")

!----------------------------------------------------------------------------
! Cleanup
!----------------------------------------------------------------------------
CALL dom%DEALLOCATE()
END PROGRAM main

Learn from example

Let's consider the following example, in which we will specify the constant boundary condition.

Click here to see the example
PROGRAM main
USE easifemBase
USE easifemClasses
IMPLICIT NONE

TYPE(DirichletBC_) :: obj
TYPE(MeshSelection_) :: boundary
TYPE(ParameterList_) :: param
TYPE(Domain_) :: dom
TYPE(HDF5File_) :: domainfile
CHARACTER(*), PARAMETER :: domainfilename = "./mesh3D.h5"
INTEGER(I4B) :: bottom = 1, top = 2, left = 3, right = 4, &
front = 5, behind = 6, nsd
INTEGER(I4B), ALLOCATABLE :: nodeNum(:)
REAL(DFP), ALLOCATABLE :: nodalValue(:, :)

CALL FPL_Init; CALL param%Initiate()
CALL domainfile%Initiate(filename=domainfilename, mode="READ")
CALL domainfile%OPEN()
CALL dom%Initiate(domainfile, group="")

nsd = dom%GetNSD()

! We call Set SetAbstractBCParam to set the parameter for boundary condition
CALL SetAbstractBCParam(param=param, prefix=obj%GetPrefix(), &
& name="ZeroBC", idof=1, nodalValueType=Constant)

! We call SetMeshSelectionParam to set the parameter for boundary condition
CALL SetMeshSelectionParam(param=param, prefix=boundary%GetPrefix(), &
& isSelectionByMeshID=.TRUE.)

CALL boundary%Initiate(param)

CALL boundary%Add(dom=dom, dim=nsd - 1, meshID=[top])
CALL boundary%Set()

CALL obj%Initiate(param=param, boundary=boundary, dom=dom)

CALL obj%Set(constantNodalValue=0.0_DFP)

CALL obj%Get(nodeNum=nodeNum, nodalValue=nodalValue)

CALL Display(nodeNum, "nodeNum", advance="NO")
CALL Display(nodalValue, "nodalValue", advance="YES")

CALL domainfile%DEALLOCATE()
CALL dom%DEALLOCATE()
CALL param%DEALLOCATE(); CALL FPL_Finalize
END PROGRAM main

In the above code, to define the boundary condition, we follow the steps given below.

Step 1: Set the properties of the DirichletBC

We set the properties of DirichletBC_ by using the method called SetAbstractBCParam.

CALL SetAbstractBCParam(param=param, prefix=obj%GetPrefix(),  &
& name="ZeroBC", idof=1, nodalValueType=Constant)
note

Because we are setting constant boundary condition, we used nodalValueType=Constant.

You can learn more about this method here.

Step 2: Define a boundary

To define a boundary we will use the MeshSelection. In the above code, we select the boundary by specifing the meshID.

CALL SetMeshSelectionParam(param=param, prefix=boundary%GetPrefix(),  &
& isSelectionByMeshID=.TRUE.)

After setting the boundary parameter we call Initiate method.

CALL boundary%Initiate(param)

Subsequently, we call Add method to add the information of meshID.

CALL boundary%Add(dom=dom, dim=nsd - 1, meshID=[top])
CALL boundary%Set()
info

After adding the information of meshID we should call Set method, which means that we are done adding information to the boundary.

You can learn more about SetMeshSelectionParam here

Step 3: Initiate instance of DirichletBC

After initiating the boundary, call Initiate. To initiate an instance of DirichletBC_ we need to pass the boundary, paramters, and domain.

CALL obj%Initiate(param=param, boundary=boundary, dom=dom)

Step 4: Set the boundary condition

After initiating an instance of DirichletBC_, next step is to set the boundary condition. To do so, we will use the method Set.

While setting the value we should respect the configuration used while calling SetAbstractBCParam. For example, in the above example we configure boundary condition for nodalValueType=Constant. Therefore, we should set the constantNodalValue while calling the set method.

CALL obj%Set(constantNodalValue=0.0_DFP)

Step 5: Get the value of boundary condition

To get the boundary condition we will use the method Get. The Get function can take two arguments nodeNum(:) and nodalValue(:,:). The nodeNum(:) is compulsory, whereas nodalValue can be optional.

CALL obj%Get(nodeNum=nodeNum, nodalValue=nodalValue)
note

On return, the size of nodeNum and SIZE(nodalValue, 1) is same.

The columns in nodalValue denotes the boundary condition at different times. You can read more about this subroutine here.

Further reading

There is more to DirichletBC_, and you can learn about them from following pages. (Here DBC stands for DirichletBC_)

]]>
mesh fedof dirichletBC