examples.elphf.phase¶
A simple 1D example to test the setup of the phase field equation.
We rearrange Eq. (1) to
The single-component phase field governing equation can be represented as
where \(\xi\) is the phase field, \(t\) is time, \(M_\xi\) is the phase field mobility, \(\kappa_\xi\) is the phase field gradient energy coefficient, and \(W\) is the phase field barrier energy.
We solve the problem on a 1D mesh
>>> from fipy import CellVariable, Grid1D, TransientTerm, DiffusionTerm, ImplicitSourceTerm, Viewer
>>> from fipy.tools import numerix
>>> nx = 400
>>> dx = 0.01
>>> L = nx * dx
>>> mesh = Grid1D(dx = dx, nx = nx)
We create the phase field
>>> phase = CellVariable(mesh = mesh, name = 'xi')
>>> phase.mobility = numerix.inf
>>> phase.gradientEnergy = 0.025
Although we are not interested in them for this problem, we create one field to represent the “solvent” component (1 everywhere)
>>> class ComponentVariable(CellVariable):
... def copy(self):
... new = self.__class__(mesh = self.mesh,
... name = self.name,
... value = self.value)
... new.standardPotential = self.standardPotential
... new.barrier = self.barrier
... return new
>>> solvent = ComponentVariable(mesh = mesh, name = 'Cn', value = 1.)
>>> solvent.standardPotential = 0.
>>> solvent.barrier = 1.
and one field to represent the electrostatic potential (0 everywhere)
>>> potential = CellVariable(mesh = mesh, name = 'phi', value = 0.)
>>> permittivityPrime = 0.
We’ll have no substitutional species and no interstitial species in this first example
>>> substitutionals = []
>>> interstitials = []
>>> for component in substitutionals:
... solvent -= component
>>> phase.equation = TransientTerm(coeff = 1/phase.mobility) \
... == DiffusionTerm(coeff = phase.gradientEnergy) \
... - (permittivityPrime / 2.) \
... * potential.grad.dot(potential.grad)
>>> enthalpy = solvent.standardPotential
>>> barrier = solvent.barrier
>>> for component in substitutionals + interstitials:
... enthalpy += component * component.standardPotential
... barrier += component * component.barrier
We linearize the source term in the same way as in examples.phase.simple
.
>>> mXi = -(30 * phase * (1. - phase) * enthalpy \
... + 4 * (0.5 - phase) * barrier)
>>> dmXidXi = (-60 * (0.5 - phase) * enthalpy + 4 * barrier)
>>> S1 = dmXidXi * phase * (1 - phase) + mXi * (1 - 2 * phase)
>>> S0 = mXi * phase * (1 - phase) - phase * S1
>>> phase.equation -= S0 + ImplicitSourceTerm(coeff = S1)
Note
Adding a Term
to an equation formed with ==
will
add to the left-hand side of the equation and subtracting a
Term
will add to the right-hand side of the
equation
We separate the phase field into electrode and electrolyte regimes
>>> phase.setValue(1.)
>>> phase.setValue(0., where=mesh.cellCenters[0] > L / 2)
Even though we are solving the steady-state problem (\(M_\phi = \infty\)) we still must sweep the solution several times to equilibrate
>>> from builtins import range
>>> for step in range(10):
... phase.equation.solve(var = phase, dt=1.)
Since we have only a single component \(n\), with \(\Delta\mu_n^\circ = 0\), and the electrostatic potential is uniform, Eq. (1) reduces to
which we know from examples.phase.simple
has the analytical
solution
with an interfacial thickness \(d = \sqrt{\kappa_{\xi}/2W_n}\).
We verify that the correct equilibrium solution is attained
>>> x = mesh.cellCenters[0]
>>> d = numerix.sqrt(phase.gradientEnergy / (2 * solvent.barrier))
>>> analyticalArray = (1. - numerix.tanh((x - L/2.)/(2 * d))) / 2.
>>> phase.allclose(analyticalArray, rtol = 1e-4, atol = 1e-4).value
1
If we are running interactively, we plot the error
>>> if __name__ == '__main__':
... viewer = Viewer(vars = (phase - \
... CellVariable(name = "analytical", mesh = mesh,
... value = analyticalArray),))
... viewer.plot()