> Could you possible post your code so I can play around with it? I > can't think offhand what could be the issue or exactly what the error > is that you're calculating, it seems like the normalized residual or > something.
hi daniel, thanks for your reply. the way i was calculating the residual
is to export the solution on a grid, import that into mathematica,
interpolate in space and time and have mathematica calculate
||lhs-rhs|| / ||lhs|| where ||...|| is the L2 norm. what i saw was that
the relative error was low in the beginning but increased over time,
especially in places where the solution starts to grow exponentially.
i am sure this relative error can be gotten from fipy using
residualVector or some other methods of the terms but i have not been
able to understand the docs well enough to replicate that.
so i am posting pretty much the (streamlined but otherwise unchanged)
example script i am having trouble with below. h5py is needed only for
the export, you can comment that out if you dont export. Lambda3 is the
source term coefficient -- by increasing it's values the solution
becomes more rapidly growing and the errors also increase.
i hope the indentation survives emailing....
cheers, n.
from __future__ import division
import fipy as fp
from fipy import numerix as nx
# the module for the bare numpy functions
# this is essentially numpy!
NX = nx.NUMERIX
from time import sleep
# this is where we are going to write results
import os
datadir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'pde_test_data/'
)
datadir = os.path.realpath(datadir)
try: os.mkdir(datadir)
except OSError: pass
# utility
def nsq(v):
return nx.dot(v, v)
class Domain(object):
upper = 300.
lower = 0
size = upper - lower
class Mesh(Domain):
def __init__(self):
delta = 1.5
Lx, Ly = (self.size,) * 2
dx, dy = (delta,) * 2
nx, ny = (self.size // delta,) * 2
self.mesh = fp.Grid2D(dx=dx, dy=dx, nx=nx, ny=ny, Lx=Lx, Ly=Ly)
# from Mma, the last iteration of the potential was this:
class Phi(object):
scale = 100.
x1, y1 = [100., 100]
x2, y2 = [180., 180]
s1 = 4.
s2 = 12.
dp = 3.
strength = 30.
sharpness_a = 120.
# the following should act on the position field x
# this should be a cell varable in fipy.
@classmethod
def itp(cls, z):
t = nx.tanh(z/cls.sharpness_a**2) + 1
return 0.5 * t
@classmethod
def scalephi(cls, x, y):
[d1, d2] = [(x-cls.x1)**2 + (y-cls.y1)**2,
(x-cls.x2)**2 + (y-cls.y2)**2]
[f2, f1] = [cls.itp(d1 - d2), cls.itp(d2 - d1)]
p1 = 0.5 * cls.s1 * d1 / cls.scale**2
p2 = 0.5 * cls.s2 * d2 / cls.scale**2 - cls.dp
return f1 * p1 + f2 * p2
@classmethod
def __call__(cls, x, y):
return cls.strength * cls.scalephi(x, y)
class Lambda3(object):
def __init__(self, curvature=None, direction=None, start=None):
if curvature is None: curvature = 0.2 / Domain.size**2
if direction is None: direction = nx.array([1, 1.])
direction /= nx.sqrt(nsq(nx.array(direction)))
if start is None: start = nx.array([0.,0])
max_pos = 0.5 * nx.sqrt(2) * Domain.size
max_ht = 1./2 * max_pos**2 * curvature # for 0 at the origin
max_ht = 0.1 * max_ht # tame the beast
(self.curvature, self.direction, self.max_pos, self.max_ht,
self.start) = (
curvature, direction, max_pos, max_ht, start)
def __call__(self, x, y):
distance2 = (NX.dot(self.direction,
(nx.array([x, y]).T - self.start).T) - self.max_pos)**2
lam = self.max_ht - 1./2 * self.curvature * distance2
return lam
class InitialRho(object):
'''Gaussian-like initial condition'''
c = [[100], [100]]
covm = NX.eye(2) * 30**2 # bare numpy
stm = NX.linalg.inv(covm)
Z = (2. * NX.linalg.det(covm))**(1./2)
@classmethod
def __call__(cls, x, y):
p = NX.array([x,y])
return 1. / cls.Z * NX.exp(-1./2 * NX.diag(
cls.stm.dot(p - cls.c).T.dot(p - cls.c)))
mesh = Mesh().mesh
x, y = mesh.cellCenters
x1d, y1d = x[:mesh.nx], y[::mesh.ny] # yes!
# diffusion
Dtensor = nx.eye(2) * 10.
# constant diff coeff fields
Dtensor = fp.CellVariable(mesh, 'dtensor', Dtensor)
# make the input fields
phi = fp.CellVariable(name=r'$\phi$', mesh=mesh, value=Phi()(x, y))
v = - phi.grad # on the cells, not the faces.
lambda_ = fp.CellVariable(name=r'$\lambda$', mesh=mesh,
value=Lambda3(curvature=1./Domain.size**2, direction=[1.,-1],
start=[0.,Domain.size])(x, y))
# terms
def diff(DD):
# Correction actually needed? in any case it seems to work.
# list of diffusion coefficients of different orders
return fp.DiffusionTermCorrection(coeff=[DD,])
def drift(v):
return - fp.PowerLawConvectionTerm(coeff=v)
def prolif(lambda_):
# implicit = multiplies the var
return fp.ImplicitSourceTerm(coeff=lambda_)
def lhs():
return fp.TransientTerm()
# construct the density
rho = fp.CellVariable(name=r'$\rho$', mesh=mesh, hasOld=True)
# boundary conditions
border = mesh.exteriorFaces
bc = 'dirichlet'
bc = 'no-flux'
# Dirichlet bc
if bc == 'dirichlet':
rho.constrain(0., where=border)
# no-flux: set convection and diffusion 0
elif bc == 'no-flux':
pass # no flux is standard b.c. in fipy.
else: raise ValueError("invalid bc type")
eq_diffprolif = lhs() == diff(Dtensor) + drift(v) + prolif(lambda_)
# initial condition
ic = fp.CellVariable(name=r'$\rho_0$', mesh=mesh, value=InitialRho()(x,y))
variants = { 'diffprolif': (eq_diffprolif, Dtensor, phi, lambda_, ic) }
# set up visualization for it
viewer = fp.Viewer(rho, datamax = 3. * ic.max(), datamin = -0.1 * ic.max())
# solver parameters
class Solve(object):
sampling = 5
step = sampling / 5
# T = 10
T = 80
sample_t = NX.arange(0, T + sampling / 2., sampling)
sweeps = 0.01
# customSolver = fp.LinearCGSSolver()
customSolver = None
assert NX.isclose(0, NX.modf(sampling / step)[0])
# do it for all the equations of interest:
snapshots, residuals = {}, {}
eq, _, _, _, ic_ = variants['diffprolif']
# initialize
rho.setValue(ic_.value)
snapshots = []
residuals = []
# first
snapshots.append(rho.value.copy())
viewer.plot()
for (st, stnext) in zip(Solve.sample_t, Solve.sample_t[1:]):
for tt in nx.arange(st, stnext, Solve.step):
rho.updateOld()
res = 1.
while res > Solve.sweeps:
res = eq.sweep(var=rho, dt=Solve.step,
solver=Solve.customSolver)
residuals.append(res)
snapshots.append(rho.value.copy())
sleep(0.1)
viewer.plot()
# Now finally we have to export this. Use hdf5 as from Mma to reproduce
the output.
# sampling for export
sampling_delta = 1.5
ex_x1, ex_y1 = NX.arange(1., 280, sampling_delta), NX.arange(1., 280,
sampling_delta)
ex_x, ex_y = NX.meshgrid(ex_x1, ex_y1)
# interpolate the intermediates at these values!
def sample_field(cellvar, value=None, xs=None, ys=None, normalize=False):
from scipy.interpolate import RectBivariateSpline
val = cellvar.value if value is None else value
f = val.reshape(cellvar.mesh.nx, -1)
x1 = cellvar.mesh.x[:cellvar.mesh.nx]
y1 = cellvar.mesh.y[::cellvar.mesh.ny] # sic.
field = RectBivariateSpline(x1, y1, f,
bbox=[Domain.lower, Domain.upper] * 2, s=0)
xs = ex_x1 if xs is None else xs
ys = ex_y1 if ys is None else ys
sf = field(xs, ys)
area_el = (xs[1]-xs[0]) * (ys[1]-ys[0])
int_ = sf.sum() * area_el
return (sf / int_, int_) if normalize else sf
# write to HDF5 file
import h5py as h5
outfile = os.path.join(datadir, 'twohump.h5')
eq, Dtens, ph, lam, ic_ = variants['diffprolif']
Dt = Dtens[:,:,0] # get back constant array from cell variable
with h5.File(name=outfile, mode='w') as f:
f['phi'] = sample_field(ph)
f['lambda'] = sample_field(lam)
dNs = [sample_field(rho, value=snap, normalize=True)
for snap in snapshots]
f['density'] = NX.array([dn[0] for dn in dNs])
f['Nt'] = NX.array([dn[1] for dn in dNs])
f['diffcoeff'] = NX.diag(Dt)
f['timeline'] = Solve.sample_t
assert f['density'].shape[0] == f['timeline'].shape[0]
f['xs'], f['ys'] = ex_x1, ex_y1
0xF0A5C638.asc
Description: application/pgp-keys
_______________________________________________ fipy mailing list [email protected] http://www.ctcms.nist.gov/fipy [ NIST internal ONLY: https://email.nist.gov/mailman/listinfo/fipy ]
