List of tutorials
All tutorials are written assuming that you are reasonably familiar with MOOSE. If you find most of the tutorials difficult to follow, please refer to the official MOOSE website for learning resources.
Tutorial 1: Multidomain state of a FE block
This tutorial (and others) covers the basic usage of the ferroelectric phase field method implemented in FERRET.
This simple problem considers solving the time-dependent LGD equation with just bulk and gradient contributions to the free energy in the presence of a depolarization phenomena (via solving the Poisson equation iteratively).
Consider a computational domain with a geometry which we define with the Mesh block.
[Mesh]
type = GeneratedMesh
dim = 3
nx = 32
ny = 32
nz = 6
xmin = -15
xmax = 15
ymin = -15
ymax = 15
zmin = -2
zmax = 2
elem_type = HEX8
[]
In general, the geometry defined in the 'Mesh' block never carries units. The length scale is introduced through Materials
, Kernels
, or other MOOSE objects. For this problem, the length scale is introduced through the units in the Materials
objects that connect to the Kernels
. Ferret uses a special base units system for a number of problems. This reduces the load quite extensively on the PETSc solvers to iterate the problem.
In the total free energy density, we include the bulk free energy density,
(1)
to the sixth order and the wall free energy density, ,
(2)
where and are suitable coefficients for lead-titanate. To solve the problem, we need the time-dependent Landau-Ginzburg equation,
(3)
which has variational derivatives of the total free energy,
(4)
The computations of the two relevant energy terms are introduced through a number of Kernels
in the input files as below.
[Kernels]
#---------------------------------------#
# #
# Bulk (homogeneous) free energy #
# #
#---------------------------------------#
[./bed_x]
type = BulkEnergyDerivativeEighth
variable = polar_x
component = 0
[../]
[./bed_y]
type = BulkEnergyDerivativeEighth
variable = polar_y
component = 1
[../]
[./bed_z]
type = BulkEnergyDerivativeEighth
variable = polar_z
component = 2
[../]
#---------------------------------------#
# #
# Gradient free energy #
# #
#---------------------------------------#
[./walled_x]
type = WallEnergyDerivative
variable = polar_x
component = 0
[../]
[./walled_y]
type = WallEnergyDerivative
variable = polar_y
component = 1
[../]
[./walled_z]
type = WallEnergyDerivative
variable = polar_z
component = 2
[../]
[./walled2_x]
type = Wall2EnergyDerivative
variable = polar_x
component = 0
[../]
[./walled2_y]
type = Wall2EnergyDerivative
variable = polar_y
component = 1
[../]
[./walled2_z]
type = Wall2EnergyDerivative
variable = polar_z
component = 2
[../]
#---------------------------------------#
# #
# Poissons equation and P*E #
# #
#---------------------------------------#
[./polar_x_electric_E]
type = PolarElectricEStrong
variable = potential_int
[../]
[./FE_E_int]
type = Electrostatics
variable = potential_int
[../]
[./polar_electric_px]
type = PolarElectricPStrong
variable = polar_x
component = 0
[../]
[./polar_electric_py]
type = PolarElectricPStrong
variable = polar_y
component = 1
[../]
[./polar_electric_pz]
type = PolarElectricPStrong
variable = polar_z
component = 2
[../]
#---------------------------------------#
# #
# Time dependence of Pj #
# #
#---------------------------------------#
[./polar_x_time]
type = TimeDerivativeScaled
variable = polar_x
time_scale = 1.0
[../]
[./polar_y_time]
type = TimeDerivativeScaled
variable = polar_y
time_scale = 1.0
[../]
[./polar_z_time]
type = TimeDerivativeScaled
variable = polar_z
time_scale = 1.0
[../]
[]
We need one Kernel for each variable (, and ). The computation of Eq. \ref{varderiv} is split into three Kernel contributions for each of the three components of . The FERRET Syntax page details the algebra needed to turn these terms (BulkEnergyDerivativeEighth
, WallEnergyDerivative
, and Wall2EnergyDerivative
) into weak forms.
The reader will notice that we also have included terms related to the free energy due to an applied field in PolarElectricPStrong
and also terms related to the coupling to the electric field. The Poisson equation for our system reads,
(5)
which is satisfied at every step of the time evolution. The left hand side is computed by Electrostatics and the right hand side computed by PolarElectricPStrong
. Note that corresponds to a background dielectric strength which is assigned to high frequency (core) electrons. Next, we have the time derivatives of , and with TimeDerivativeScaled
. We implement some reduction of input file length by using the GlobalParams
block.
[GlobalParams]
len_scale = 1.0
polar_x = polar_x
polar_y = polar_y
polar_z = polar_z
potential_E_int = potential_int
[]
which adds these lines automatically in the relevant Kernel
and Materials
objects. To seed the relevant materials coefficients to the problem, we use the Materials system in MOOSE,
[Materials]
## Use coefficients for lead-titanate in this convienient unit system (aC, kg, sec, nm)
[./Landau_P_FE]
type = GenericConstantMaterial
prop_names = 'alpha1 alpha11 alpha12 alpha111 alpha112 alpha123 alpha1111 alpha1112 alpha1122 alpha1123'
prop_values = '-0.1722883 -0.073 0.75 0.26 0.61 -3.67 0.0 0.0 0.0 0.0'
block = '0'
[../]
[./Landau_G_FE]
type = GenericConstantMaterial
prop_names = 'G110 G11_G110 G12_G110 G44_G110 G44P_G110'
prop_values = '0.173 0.6 0.0 0.3 0.3'
block = '0'
[../]
[./permitivitty_1]
###############################################
##
## so-called background dielectric constant
## (it encapsulates the motion of core electrons
## at high frequency) = e_b*e_0 (here we use
## e_b = 10), see PRB. 74, 104014, (2006)
## This does not influence results much.
##
###############################################
type = GenericConstantMaterial
prop_names = 'permittivity'
prop_values = '0.08854187'
[../]
[]
In this problem, the boundary conditions are left empty. This is equivalently known as a natural boundary condition in MOOSe. As such, large depolarizing fields are generated from the surface charges () where is some surface normal. The action of the depolarization drives the polar topology into a classic flux-closure domain pattern as shown in the below figure.
A few other FERRET and MOOSE objects exist in the input file. We use the Postprocessors
system to track various aspects of the simulation.
[Postprocessors]
[./avePz]
type = ElementAverageValue
variable = polar_z
execute_on = 'timestep_end'
[../]
[./FbP]
type = BulkEnergyEighth
execute_on = 'initial timestep_end'
[../]
[./FgP]
type = WallEnergy
execute_on = 'initial timestep_end'
[../]
[./Ftot]
type = LinearCombinationPostprocessor
pp_names = 'FbP FgP'
pp_coefs = ' 1 1'
execute_on = 'timestep_end'
##########################################
#
# NOTE: Ferret output is in attojoules
#
##########################################
[../]
[./perc_change]
type = PercentChangePostprocessor
postprocessor = Ftot
[../]
[]
The bulk and gradient energy contributions are listed (computed volume integral over the simulation box). Their sum is computed with the LinearCombinationPostprocessor to be equal to . An astute user should ask why the electric field contribution is also not included here. We leave that as an exercise for the user to verify that the total energy with this contribution still is decreasing with every time step as gauranteed by the mathematical form of Eq. \refLGD.
Then, the relative change of this energy between adjacent time steps is calculated with PercentChangePostprocessor as,
(6)
The Terminator located in the UserObjects block
[UserObjects]
[./kill]
type = Terminator
expression = 'perc_change <= 1.0e-4'
[../]
[]
which tracks when the postprocessed value is less than . This is a useful tool to end the simulation when the suspected ground state has been reached. In general, this value seems to be sufficient for these types of problems.
Finally, we share our PETSc and Executioner
options that seem to be the most efficient for polar domain prediction problems.
[Preconditioning]
[./smp]
type = SMP
full = true
petsc_options = '-snes_ksp_ew'
petsc_options_iname = '-ksp_gmres_restart -snes_atol -snes_rtol -ksp_rtol -pc_type'
petsc_options_value = ' 120 1e-10 1e-8 1e-4 bjacobi'
[../]
[]
and
[Executioner]
type = Transient
solve_type = 'NEWTON'
scheme = 'implicit-euler'
dtmax = 0.7
[]
These flags can be optimized to lead to less wall clock times but this is a subject of future work.