Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • inducer/grudge
  • jdsteve2/grudge
  • eshoag2/grudge
  • mattwala/grudge
  • kaushikcfd/grudge
  • fikl2/grudge
6 results
Show changes
Showing
with 247 additions and 3234 deletions
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
import numpy.linalg as la
# modifies isentropic vortex solution so that rho->Arho, P->A^gamma rho^gamma
# this will be analytic solution if appropriate source terms are added
# to the RHS. coded for vel_x=1, vel_y=0
class Vortex:
def __init__(self, beta, gamma, center, velocity, densityA):
self.beta = beta
self.gamma = gamma
self.center = numpy.asarray(center)
self.velocity = numpy.asarray(velocity)
self.densityA = densityA
def __call__(self, t, x_vec):
vortex_loc = self.center + t*self.velocity
# coordinates relative to vortex center
x_rel = x_vec[0] - vortex_loc[0]
y_rel = x_vec[1] - vortex_loc[1]
# Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
# also JSH/TW Nodal DG Methods, p. 209
from math import pi
r = numpy.sqrt(x_rel**2+y_rel**2)
expterm = self.beta*numpy.exp(1-r**2)
u = self.velocity[0] - expterm*y_rel/(2*pi)
v = self.velocity[1] + expterm*x_rel/(2*pi)
rho = self.densityA*(1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
p = rho**self.gamma
e = p/(self.gamma-1) + rho/2*(u**2+v**2)
from grudge.tools import join_fields
return join_fields(rho, e, rho*u, rho*v)
def volume_interpolant(self, t, discr):
return discr.convert_volume(
self(t, discr.nodes.T
.astype(discr.default_scalar_type)),
kind=discr.compute_kind)
def boundary_interpolant(self, t, discr, tag):
return discr.convert_boundary(
self(t, discr.get_boundary(tag).nodes.T
.astype(discr.default_scalar_type)),
tag=tag, kind=discr.compute_kind)
class SourceTerms:
def __init__(self, beta, gamma, center, velocity, densityA):
self.beta = beta
self.gamma = gamma
self.center = numpy.asarray(center)
self.velocity = numpy.asarray(velocity)
self.densityA = densityA
def __call__(self,t,x_vec,q):
vortex_loc = self.center + t*self.velocity
# coordinates relative to vortex center
x_rel = x_vec[0] - vortex_loc[0]
y_rel = x_vec[1] - vortex_loc[1]
# sources written in terms of A=1.0 solution
# (standard isentropic vortex)
from math import pi
r = numpy.sqrt(x_rel**2+y_rel**2)
expterm = self.beta*numpy.exp(1-r**2)
u = self.velocity[0] - expterm*y_rel/(2*pi)
v = self.velocity[1] + expterm*x_rel/(2*pi)
rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
p = rho**self.gamma
#computed necessary derivatives
expterm_t = 2*expterm*x_rel
expterm_x = -2*expterm*x_rel
expterm_y = -2*expterm*y_rel
u_x = -expterm*y_rel/(2*pi)*(-2*x_rel)
v_y = expterm*x_rel/(2*pi)*(-2*y_rel)
#derivatives for rho (A=1)
facG=self.gamma-1
rho_t = (1/facG)*(1-(facG)/(16*self.gamma*pi**2)*expterm**2)**(1/facG-1)* \
(-facG/(16*self.gamma*pi**2)*2*expterm*expterm_t)
rho_x = (1/facG)*(1-(facG)/(16*self.gamma*pi**2)*expterm**2)**(1/facG-1)* \
(-facG/(16*self.gamma*pi**2)*2*expterm*expterm_x)
rho_y = (1/facG)*(1-(facG)/(16*self.gamma*pi**2)*expterm**2)**(1/facG-1)* \
(-facG/(16*self.gamma*pi**2)*2*expterm*expterm_y)
#derivatives for rho (A=1) to the power of gamma
rho_gamma_t = self.gamma*rho**(self.gamma-1)*rho_t
rho_gamma_x = self.gamma*rho**(self.gamma-1)*rho_x
rho_gamma_y = self.gamma*rho**(self.gamma-1)*rho_y
factorA=self.densityA**self.gamma-self.densityA
#construct source terms
source_rho = x_vec[0]-x_vec[0]
source_e = (factorA/(self.gamma-1))*(rho_gamma_t + self.gamma*(u_x*rho**self.gamma+u*rho_gamma_x)+ \
self.gamma*(v_y*rho**self.gamma+v*rho_gamma_y))
source_rhou = factorA*rho_gamma_x
source_rhov = factorA*rho_gamma_y
from grudge.tools import join_fields
return join_fields(source_rho, source_e, source_rhou, source_rhov, x_vec[0]-x_vec[0])
def volume_interpolant(self,t,q,discr):
return discr.convert_volume(
self(t,discr.nodes.T,q),
kind=discr.compute_kind)
def main(write_output=True):
from grudge.backends import guess_run_context
rcon = guess_run_context(
#["cuda"]
)
gamma = 1.4
# at A=1 we have case of isentropic vortex, source terms
# arise for other values
densityA = 2.0
from grudge.tools import EOCRecorder, to_obj_array
eoc_rec = EOCRecorder()
if rcon.is_head_rank:
from grudge.mesh import \
make_rect_mesh, \
make_centered_regular_rect_mesh
refine = 1
mesh = make_centered_regular_rect_mesh((0,-5), (10,5), n=(9,9),
post_refine_factor=refine)
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
for order in [4,5]:
discr = rcon.make_discretization(mesh_data, order=order,
debug=[#"cuda_no_plan",
#"print_op_code"
],
default_scalar_type=numpy.float64)
from grudge.visualization import SiloVisualizer, VtkVisualizer
#vis = VtkVisualizer(discr, rcon, "vortex-%d" % order)
vis = SiloVisualizer(discr, rcon)
vortex = Vortex(beta=5, gamma=gamma,
center=[5,0],
velocity=[1,0], densityA=densityA)
fields = vortex.volume_interpolant(0, discr)
sources=SourceTerms(beta=5, gamma=gamma,
center=[5,0],
velocity=[1,0], densityA=densityA)
from grudge.models.gas_dynamics import (
GasDynamicsOperator, GammaLawEOS)
from grudge.mesh import BTAG_ALL
op = GasDynamicsOperator(dimensions=2,
mu=0.0, prandtl=0.72, spec_gas_const=287.1,
equation_of_state=GammaLawEOS(vortex.gamma),
bc_inflow=vortex, bc_outflow=vortex, bc_noslip=vortex,
inflow_tag=BTAG_ALL, source=sources)
euler_ex = op.bind(discr)
max_eigval = [0]
def rhs(t, q):
ode_rhs, speed = euler_ex(t, q)
max_eigval[0] = speed
return ode_rhs
rhs(0, fields)
if rcon.is_head_rank:
print("---------------------------------------------")
print("order %d" % order)
print("---------------------------------------------")
print("#elements=", len(mesh.elements))
# limiter setup -------------------------------------------------------
from grudge.models.gas_dynamics import SlopeLimiter1NEuler
limiter = SlopeLimiter1NEuler(discr, gamma, 2, op)
# time stepper --------------------------------------------------------
from grudge.timestep import SSPRK3TimeStepper, RK4TimeStepper
#stepper = SSPRK3TimeStepper(limiter=limiter)
#stepper = SSPRK3TimeStepper()
stepper = RK4TimeStepper()
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
if write_output:
log_file_name = "euler-%d.dat" % order
else:
log_file_name = None
logmgr = LogManager(log_file_name, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
# timestep loop -------------------------------------------------------
t = 0
#fields = limiter(fields)
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=.1,
#max_steps=500,
logmgr=logmgr,
max_dt_getter=lambda t: 0.4*op.estimate_timestep(discr,
stepper=stepper, t=t, max_eigenvalue=max_eigval[0]))
for step, t, dt in step_it:
if step % 1 == 0 and write_output:
#if False:
visf = vis.make_file("vortex-%d-%04d" % (order, step))
true_fields = vortex.volume_interpolant(t, discr)
#rhs_fields = rhs(t, fields)
from pyvisfile.silo import DB_VARTYPE_VECTOR
vis.add_data(visf,
[
("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
("e", discr.convert_volume(op.e(fields), kind="numpy")),
("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
("u", discr.convert_volume(op.u(fields), kind="numpy")),
#("true_rho", discr.convert_volume(op.rho(true_fields), kind="numpy")),
#("true_e", discr.convert_volume(op.e(true_fields), kind="numpy")),
#("true_rho_u", discr.convert_volume(op.rho_u(true_fields), kind="numpy")),
#("true_u", discr.convert_volume(op.u(true_fields), kind="numpy")),
#("rhs_rho", discr.convert_volume(op.rho(rhs_fields), kind="numpy")),
#("rhs_e", discr.convert_volume(op.e(rhs_fields), kind="numpy")),
#("rhs_rho_u", discr.convert_volume(op.rho_u(rhs_fields), kind="numpy")),
],
expressions=[
#("diff_rho", "rho-true_rho"),
#("diff_e", "e-true_e"),
#("diff_rho_u", "rho_u-true_rho_u", DB_VARTYPE_VECTOR),
("p", "0.4*(e- 0.5*(rho_u*u))"),
],
time=t, step=step
)
visf.close()
fields = stepper(fields, t, dt, rhs)
true_fields = vortex.volume_interpolant(t, discr)
l2_error = discr.norm(fields-true_fields)
l2_error_rho = discr.norm(op.rho(fields)-op.rho(true_fields))
l2_error_e = discr.norm(op.e(fields)-op.e(true_fields))
l2_error_rhou = discr.norm(op.rho_u(fields)-op.rho_u(true_fields))
l2_error_u = discr.norm(op.u(fields)-op.u(true_fields))
eoc_rec.add_data_point(order, l2_error_rho)
print()
print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
logmgr.set_constant("l2_error", l2_error)
logmgr.set_constant("l2_error_rho", l2_error_rho)
logmgr.set_constant("l2_error_e", l2_error_e)
logmgr.set_constant("l2_error_rhou", l2_error_rhou)
logmgr.set_constant("l2_error_u", l2_error_u)
logmgr.set_constant("refinement", refine)
finally:
if write_output:
vis.close()
logmgr.close()
discr.close()
# after order loop
#assert eoc_rec.estimate_order_of_convergence()[0,1] > 6
if __name__ == "__main__":
main()
# entry points for py.test ----------------------------------------------------
from pytools.test import mark_test
@mark_test.long
def test_euler_vortex():
main(write_output=False)
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
def main(write_output=True):
from pytools import add_python_path_relative_to_script
add_python_path_relative_to_script("..")
from grudge.backends import guess_run_context
rcon = guess_run_context()
from grudge.tools import EOCRecorder
eoc_rec = EOCRecorder()
if rcon.is_head_rank:
from grudge.mesh.generator import \
make_rect_mesh, \
make_centered_regular_rect_mesh
refine = 4
mesh = make_centered_regular_rect_mesh((0,-5), (10,5), n=(9,9),
post_refine_factor=refine)
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
for order in [3, 4, 5]:
from gas_dynamics_initials import Vortex
flow = Vortex()
from grudge.models.gas_dynamics import (
GasDynamicsOperator, PolytropeEOS, GammaLawEOS)
from grudge.mesh import BTAG_ALL
# works equally well for GammaLawEOS
op = GasDynamicsOperator(dimensions=2, mu=flow.mu,
prandtl=flow.prandtl, spec_gas_const=flow.spec_gas_const,
equation_of_state=PolytropeEOS(flow.gamma),
bc_inflow=flow, bc_outflow=flow, bc_noslip=flow,
inflow_tag=BTAG_ALL, source=None)
discr = rcon.make_discretization(mesh_data, order=order,
default_scalar_type=numpy.float64,
quad_min_degrees={
"gasdyn_vol": 3*order,
"gasdyn_face": 3*order,
},
tune_for=op.sym_operator(),
debug=["cuda_no_plan"])
from grudge.visualization import SiloVisualizer, VtkVisualizer
vis = VtkVisualizer(discr, rcon, "vortex-%d" % order)
#vis = SiloVisualizer(discr, rcon)
fields = flow.volume_interpolant(0, discr)
euler_ex = op.bind(discr)
max_eigval = [0]
def rhs(t, q):
ode_rhs, speed = euler_ex(t, q)
max_eigval[0] = speed
return ode_rhs
rhs(0, fields)
if rcon.is_head_rank:
print("---------------------------------------------")
print("order %d" % order)
print("---------------------------------------------")
print("#elements=", len(mesh.elements))
# limiter ------------------------------------------------------------
from grudge.models.gas_dynamics import SlopeLimiter1NEuler
limiter = SlopeLimiter1NEuler(discr, flow.gamma, 2, op)
from grudge.timestep.runge_kutta import SSP3TimeStepper
#stepper = SSP3TimeStepper(limiter=limiter)
stepper = SSP3TimeStepper(
vector_primitive_factory=discr.get_vector_primitive_factory())
#from grudge.timestep import RK4TimeStepper
#stepper = RK4TimeStepper()
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
if write_output:
log_file_name = "euler-%d.dat" % order
else:
log_file_name = None
logmgr = LogManager(log_file_name, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
# timestep loop -------------------------------------------------------
try:
final_time = flow.final_time
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=final_time, logmgr=logmgr,
max_dt_getter=lambda t: op.estimate_timestep(discr,
stepper=stepper, t=t, max_eigenvalue=max_eigval[0]))
print("run until t=%g" % final_time)
for step, t, dt in step_it:
if step % 10 == 0 and write_output:
#if False:
visf = vis.make_file("vortex-%d-%04d" % (order, step))
#true_fields = vortex.volume_interpolant(t, discr)
from pyvisfile.silo import DB_VARTYPE_VECTOR
vis.add_data(visf,
[
("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
("e", discr.convert_volume(op.e(fields), kind="numpy")),
("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
("u", discr.convert_volume(op.u(fields), kind="numpy")),
#("true_rho", discr.convert_volume(op.rho(true_fields), kind="numpy")),
#("true_e", discr.convert_volume(op.e(true_fields), kind="numpy")),
#("true_rho_u", discr.convert_volume(op.rho_u(true_fields), kind="numpy")),
#("true_u", discr.convert_volume(op.u(true_fields), kind="numpy")),
#("rhs_rho", discr.convert_volume(op.rho(rhs_fields), kind="numpy")),
#("rhs_e", discr.convert_volume(op.e(rhs_fields), kind="numpy")),
#("rhs_rho_u", discr.convert_volume(op.rho_u(rhs_fields), kind="numpy")),
],
#expressions=[
#("diff_rho", "rho-true_rho"),
#("diff_e", "e-true_e"),
#("diff_rho_u", "rho_u-true_rho_u", DB_VARTYPE_VECTOR),
#("p", "0.4*(e- 0.5*(rho_u*u))"),
#],
time=t, step=step
)
visf.close()
fields = stepper(fields, t, dt, rhs)
#fields = limiter(fields)
assert not numpy.isnan(numpy.sum(fields[0]))
true_fields = flow.volume_interpolant(final_time, discr)
l2_error = discr.norm(fields-true_fields)
l2_error_rho = discr.norm(op.rho(fields)-op.rho(true_fields))
l2_error_e = discr.norm(op.e(fields)-op.e(true_fields))
l2_error_rhou = discr.norm(op.rho_u(fields)-op.rho_u(true_fields))
l2_error_u = discr.norm(op.u(fields)-op.u(true_fields))
eoc_rec.add_data_point(order, l2_error)
print()
print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
logmgr.set_constant("l2_error", l2_error)
logmgr.set_constant("l2_error_rho", l2_error_rho)
logmgr.set_constant("l2_error_e", l2_error_e)
logmgr.set_constant("l2_error_rhou", l2_error_rhou)
logmgr.set_constant("l2_error_u", l2_error_u)
logmgr.set_constant("refinement", refine)
finally:
if write_output:
vis.close()
logmgr.close()
discr.close()
# after order loop
assert eoc_rec.estimate_order_of_convergence()[0,1] > 6
if __name__ == "__main__":
main()
# entry points for py.test ----------------------------------------------------
from pytools.test import mark_test
@mark_test.long
def test_euler_vortex():
main(write_output=False)
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
import numpy
import numpy.linalg as la
from six.moves import range
class UniformMachFlow:
def __init__(self, mach=0.1, p=1, rho=1, reynolds=100,
gamma=1.4, prandtl=0.72, char_length=1, spec_gas_const=287.1,
angle_of_attack=None, direction=None, gaussian_pulse_at=None,
pulse_magnitude=0.1):
"""
:param direction: is a vector indicating the direction of the
flow. Only one of angle_of_attack and direction may be
specified. Only the direction, not the magnitude, of
direction is taken into account.
:param angle_of_attack: if not None, specifies the angle of
the flow along the Y axis, where the flow is
directed along the X axis.
"""
if angle_of_attack is not None and direction is not None:
raise ValueError("Only one of angle_of_attack and "
"direction may be specified.")
if angle_of_attack is None and direction is None:
angle_of_attack = 0
if direction is not None:
self.direction = direction/la.norm(direction)
else:
self.direction = None
self.mach = mach
self.p = p
self.rho = rho
self.gamma = gamma
self.prandtl = prandtl
self.reynolds = reynolds
self.length = char_length
self.spec_gas_const = spec_gas_const
self.angle_of_attack = angle_of_attack
self.gaussian_pulse_at = gaussian_pulse_at
self.pulse_magnitude = pulse_magnitude
self.c = (self.gamma * p / rho)**0.5
u = self.velocity = mach * self.c
self.e = p / (self.gamma - 1) + rho / 2 * u**2
if numpy.isinf(self.reynolds):
self.mu = 0
else:
self.mu = u * self.length * rho / self.reynolds
def direction_vector(self, dimensions):
# this must be done here because dimensions is not known above
if self.direction is None:
assert self.angle_of_attack is not None
direction = numpy.zeros(dimensions, dtype=numpy.float64)
direction[0] = numpy.cos(
self.angle_of_attack / 180. * numpy.pi)
direction[1] = numpy.sin(
self.angle_of_attack / 180. * numpy.pi)
return direction
else:
return self.direction
def __call__(self, t, x_vec):
ones = numpy.ones_like(x_vec[0])
rho_field = ones*self.rho
if self.gaussian_pulse_at is not None:
rel_to_pulse = [x_vec[i] - self.gaussian_pulse_at[i]
for i in range(len(x_vec))]
rho_field += self.pulse_magnitude * self.rho * numpy.exp(
- sum(rtp_i**2 for rtp_i in rel_to_pulse)/2)
direction = self.direction_vector(x_vec.shape[0])
from grudge.tools import make_obj_array
u_field = make_obj_array([ones*self.velocity*dir_i
for dir_i in direction])
from grudge.tools import join_fields
return join_fields(rho_field, self.e*ones, self.rho*u_field)
def volume_interpolant(self, t, discr):
return discr.convert_volume(
self(t, discr.nodes.T),
kind=discr.compute_kind,
dtype=discr.default_scalar_type)
def boundary_interpolant(self, t, discr, tag):
return discr.convert_boundary(
self(t, discr.get_boundary(tag).nodes.T),
tag=tag, kind=discr.compute_kind,
dtype=discr.default_scalar_type)
class Vortex:
def __init__(self):
self.beta = 5
self.gamma = 1.4
self.center = numpy.array([5, 0])
self.velocity = numpy.array([1, 0])
self.mu = 0
self.prandtl = 0.72
self.spec_gas_const = 287.1
def __call__(self, t, x_vec):
vortex_loc = self.center + t*self.velocity
# coordinates relative to vortex center
x_rel = x_vec[0] - vortex_loc[0]
y_rel = x_vec[1] - vortex_loc[1]
# Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
# also JSH/TW Nodal DG Methods, p. 209
from math import pi
r = numpy.sqrt(x_rel**2+y_rel**2)
expterm = self.beta*numpy.exp(1-r**2)
u = self.velocity[0] - expterm*y_rel/(2*pi)
v = self.velocity[1] + expterm*x_rel/(2*pi)
rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
p = rho**self.gamma
e = p/(self.gamma-1) + rho/2*(u**2+v**2)
from grudge.tools import join_fields
return join_fields(rho, e, rho*u, rho*v)
def volume_interpolant(self, t, discr):
return discr.convert_volume(
self(t, discr.nodes.T
.astype(discr.default_scalar_type)),
kind=discr.compute_kind)
def boundary_interpolant(self, t, discr, tag):
return discr.convert_boundary(
self(t, discr.get_boundary(tag).nodes.T
.astype(discr.default_scalar_type)),
tag=tag, kind=discr.compute_kind)
class Vortex:
def __init__(self):
self.beta = 5
self.gamma = 1.4
self.center = numpy.array([5, 0])
self.velocity = numpy.array([1, 0])
self.final_time = 0.5
self.mu = 0
self.prandtl = 0.72
self.spec_gas_const = 287.1
def __call__(self, t, x_vec):
vortex_loc = self.center + t*self.velocity
# coordinates relative to vortex center
x_rel = x_vec[0] - vortex_loc[0]
y_rel = x_vec[1] - vortex_loc[1]
# Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
# also JSH/TW Nodal DG Methods, p. 209
from math import pi
r = numpy.sqrt(x_rel**2+y_rel**2)
expterm = self.beta*numpy.exp(1-r**2)
u = self.velocity[0] - expterm*y_rel/(2*pi)
v = self.velocity[1] + expterm*x_rel/(2*pi)
rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
p = rho**self.gamma
e = p/(self.gamma-1) + rho/2*(u**2+v**2)
from grudge.tools import join_fields
return join_fields(rho, e, rho*u, rho*v)
def volume_interpolant(self, t, discr):
return discr.convert_volume(
self(t, discr.nodes.T
.astype(discr.default_scalar_type)),
kind=discr.compute_kind)
def boundary_interpolant(self, t, discr, tag):
return discr.convert_boundary(
self(t, discr.get_boundary(tag).nodes.T
.astype(discr.default_scalar_type)),
tag=tag, kind=discr.compute_kind)
# Copyright (C) 2011 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy as np
import numpy.linalg as la
from six.moves import range
def main(write_output=True, dtype=np.float32):
from grudge.backends import guess_run_context
rcon = guess_run_context()
from grudge.mesh.generator import make_rect_mesh
if rcon.is_head_rank:
h_fac = 1
mesh = make_rect_mesh(a=(0,0),b=(1,1), max_area=h_fac**2*1e-4,
periodicity=(True,True),
subdivisions=(int(70/h_fac), int(70/h_fac)))
from grudge.models.gas_dynamics.lbm import \
D2Q9LBMMethod, LatticeBoltzmannOperator
op = LatticeBoltzmannOperator(
D2Q9LBMMethod(), lbm_delta_t=0.001, nu=1e-4)
if rcon.is_head_rank:
print("%d elements" % len(mesh.elements))
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
discr = rcon.make_discretization(mesh_data, order=3,
default_scalar_type=dtype,
debug=["cuda_no_plan"])
from grudge.timestep.runge_kutta import LSRK4TimeStepper
stepper = LSRK4TimeStepper(dtype=dtype,
#vector_primitive_factory=discr.get_vector_primitive_factory()
)
from grudge.visualization import VtkVisualizer
if write_output:
vis = VtkVisualizer(discr, rcon, "fld")
from grudge.data import CompiledExpressionData
def ic_expr(t, x, fields):
from grudge.symbolic import CFunction
from pymbolic.primitives import IfPositive
from pytools.obj_array import make_obj_array
tanh = CFunction("tanh")
sin = CFunction("sin")
rho = 1
u0 = 0.05
w = 0.05
delta = 0.05
from grudge.symbolic.primitives import make_common_subexpression as cse
u = cse(make_obj_array([
IfPositive(x[1]-1/2,
u0*tanh(4*(3/4-x[1])/w),
u0*tanh(4*(x[1]-1/4)/w)),
u0*delta*sin(2*np.pi*(x[0]+1/4))]),
"u")
return make_obj_array([
op.method.f_equilibrium(rho, alpha, u)
for alpha in range(len(op.method))
])
# timestep loop -----------------------------------------------------------
stream_rhs = op.bind_rhs(discr)
collision_update = op.bind(discr, op.collision_update)
get_rho = op.bind(discr, op.rho)
get_rho_u = op.bind(discr, op.rho_u)
f_bar = CompiledExpressionData(ic_expr).volume_interpolant(0, discr)
from grudge.discretization import ExponentialFilterResponseFunction
from grudge.symbolic.operators import FilterOperator
mode_filter = FilterOperator(
ExponentialFilterResponseFunction(min_amplification=0.9, order=4))\
.bind(discr)
final_time = 1000
try:
lbm_dt = op.lbm_delta_t
dg_dt = op.estimate_timestep(discr, stepper=stepper)
print(dg_dt)
dg_steps_per_lbm_step = int(np.ceil(lbm_dt / dg_dt))
dg_dt = lbm_dt / dg_steps_per_lbm_step
lbm_steps = int(final_time // op.lbm_delta_t)
for step in range(lbm_steps):
t = step*lbm_dt
if step % 100 == 0 and write_output:
visf = vis.make_file("fld-%04d" % step)
rho = get_rho(f_bar)
rho_u = get_rho_u(f_bar)
vis.add_data(visf,
[ ("fbar%d" %i,
discr.convert_volume(f_bar_i, "numpy")) for i, f_bar_i in enumerate(f_bar)]+
[
("rho", discr.convert_volume(rho, "numpy")),
("rho_u", discr.convert_volume(rho_u, "numpy")),
],
time=t,
step=step)
visf.close()
print("step=%d, t=%f" % (step, t))
f_bar = collision_update(f_bar)
for substep in range(dg_steps_per_lbm_step):
f_bar = stepper(f_bar, t + substep*dg_dt, dg_dt, stream_rhs)
#f_bar = mode_filter(f_bar)
finally:
if write_output:
vis.close()
discr.close()
if __name__ == "__main__":
main(True)
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
import numpy.linalg as la
from six.moves import range
def make_nacamesh():
def round_trip_connect(seq):
result = []
for i in range(len(seq)):
result.append((i, (i+1)%len(seq)))
return result
pt_back = numpy.array([1,0])
#def max_area(pt):
#max_area_front = 1e-2*la.norm(pt)**2 + 1e-5
#max_area_back = 1e-2*la.norm(pt-pt_back)**2 + 1e-4
#return min(max_area_front, max_area_back)
def max_area(pt):
x = pt[0]
if x < 0:
return 1e-2*la.norm(pt)**2 + 1e-5
elif x > 1:
return 1e-2*la.norm(pt-pt_back)**2 + 1e-5
else:
return 1e-2*pt[1]**2 + 1e-5
def needs_refinement(vertices, area):
barycenter = sum(numpy.array(v) for v in vertices)/3
return bool(area > max_area(barycenter))
from meshpy.naca import get_naca_points
points = get_naca_points(naca_digits="2412", number_of_points=80)
from meshpy.geometry import GeometryBuilder, Marker
from meshpy.triangle import write_gnuplot_mesh
profile_marker = Marker.FIRST_USER_MARKER
builder = GeometryBuilder()
builder.add_geometry(points=points,
facets=round_trip_connect(points),
facet_markers=profile_marker)
builder.wrap_in_box(4, (10, 8))
from meshpy.triangle import MeshInfo, build
mi = MeshInfo()
builder.set(mi)
mi.set_holes([builder.center()])
mesh = build(mi, refinement_func=needs_refinement,
#allow_boundary_steiner=False,
generate_faces=True)
write_gnuplot_mesh("mesh.dat", mesh)
print("%d elements" % len(mesh.elements))
fvi2fm = mesh.face_vertex_indices_to_face_marker
face_marker_to_tag = {
profile_marker: "noslip",
Marker.MINUS_X: "inflow",
Marker.PLUS_X: "outflow",
Marker.MINUS_Y: "inflow",
Marker.PLUS_Y: "inflow"
#Marker.MINUS_Y: "minus_y",
#Marker.PLUS_Y: "plus_y"
}
def bdry_tagger(fvi, el, fn, all_v):
face_marker = fvi2fm[fvi]
return [face_marker_to_tag[face_marker]]
from grudge.mesh import make_conformal_mesh_ext
vertices = numpy.asarray(mesh.points, order="C")
from grudge.mesh.element import Triangle
return make_conformal_mesh_ext(
vertices,
[Triangle(i, el_idx, vertices)
for i, el_idx in enumerate(mesh.elements)],
bdry_tagger,
#periodicity=[None, ("minus_y", "plus_y")]
)
def main():
from grudge.backends import guess_run_context
rcon = guess_run_context()
if rcon.is_head_rank:
mesh = make_nacamesh()
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
from pytools import add_python_path_relative_to_script
add_python_path_relative_to_script("..")
for order in [4]:
from gas_dynamics_initials import UniformMachFlow
uniform_flow = UniformMachFlow()
from grudge.models.gas_dynamics import GasDynamicsOperator, GammaLawEOS
op = GasDynamicsOperator(dimensions=2,
equation_of_state=GammaLawEOS(uniform_flow.gamma),
prandtl=uniform_flow.prandtl,
spec_gas_const=uniform_flow.spec_gas_const, mu=uniform_flow.mu,
bc_inflow=uniform_flow, bc_outflow=uniform_flow, bc_noslip=uniform_flow,
inflow_tag="inflow", outflow_tag="outflow", noslip_tag="noslip")
discr = rcon.make_discretization(mesh_data, order=order,
debug=[
"cuda_no_plan",
#"cuda_dump_kernels",
#"dump_optemplate_stages",
#"dump_dataflow_graph",
#"print_op_code"
],
default_scalar_type=numpy.float32,
tune_for=op.sym_operator())
from grudge.visualization import SiloVisualizer, VtkVisualizer
#vis = VtkVisualizer(discr, rcon, "shearflow-%d" % order)
vis = SiloVisualizer(discr, rcon)
fields = uniform_flow.volume_interpolant(0, discr)
navierstokes_ex = op.bind(discr)
max_eigval = [0]
def rhs(t, q):
ode_rhs, speed = navierstokes_ex(t, q)
max_eigval[0] = speed
return ode_rhs
rhs(0, fields)
if rcon.is_head_rank:
print("---------------------------------------------")
print("order %d" % order)
print("---------------------------------------------")
print("#elements=", len(mesh.elements))
from grudge.timestep.runge_kutta import \
ODE23TimeStepper, LSRK4TimeStepper
stepper = ODE23TimeStepper(dtype=discr.default_scalar_type,
rtol=1e-6,
vector_primitive_factory=discr.get_vector_primitive_factory())
#stepper = LSRK4TimeStepper(dtype=discr.default_scalar_type)
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
logmgr = LogManager("cns-naca-%d.dat" % order, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
from pytools.log import LogQuantity
class ChangeSinceLastStep(LogQuantity):
"""Records the change of a variable between a time step and the previous
one"""
def __init__(self, name="change"):
LogQuantity.__init__(self, name, "1", "Change since last time step")
self.old_fields = 0
def __call__(self):
result = discr.norm(fields - self.old_fields)
self.old_fields = fields
return result
#logmgr.add_quantity(ChangeSinceLastStep())
# filter setup-------------------------------------------------------------
from grudge.discretization import Filter, ExponentialFilterResponseFunction
mode_filter = Filter(discr,
ExponentialFilterResponseFunction(min_amplification=0.9,order=4))
# timestep loop -------------------------------------------------------
logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=200,
#max_steps=500,
logmgr=logmgr,
max_dt_getter=lambda t: next_dt,
taken_dt_getter=lambda: taken_dt)
model_stepper = LSRK4TimeStepper()
next_dt = op.estimate_timestep(discr,
stepper=model_stepper, t=0,
max_eigenvalue=max_eigval[0])
for step, t, dt in step_it:
if step % 10 == 0:
visf = vis.make_file("naca-%d-%06d" % (order, step))
from pyvisfile.silo import DB_VARTYPE_VECTOR
vis.add_data(visf,
[
("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
("e", discr.convert_volume(op.e(fields), kind="numpy")),
("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
("u", discr.convert_volume(op.u(fields), kind="numpy")),
#("true_rho", op.rho(true_fields)),
#("true_e", op.e(true_fields)),
#("true_rho_u", op.rho_u(true_fields)),
#("true_u", op.u(true_fields)),
#("rhs_rho", discr.convert_volume(op.rho(rhs_fields), kind="numpy")),
#("rhs_e", discr.convert_volume(op.e(rhs_fields), kind="numpy")),
#("rhs_rho_u", discr.convert_volume(op.rho_u(rhs_fields), kind="numpy")),
],
expressions=[
#("diff_rho", "rho-true_rho"),
#("diff_e", "e-true_e"),
#("diff_rho_u", "rho_u-true_rho_u", DB_VARTYPE_VECTOR),
("p", "(0.4)*(e- 0.5*(rho_u*u))"),
],
time=t, step=step
)
visf.close()
fields, t, taken_dt, next_dt = stepper(fields, t, dt, rhs)
fields = mode_filter(fields)
finally:
vis.close()
logmgr.save()
discr.close()
if __name__ == "__main__":
main()
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
import numpy.linalg as la
class SteadyShearFlow:
def __init__(self):
self.gamma = 1.5
self.mu = 0.01
self.prandtl = 0.72
self.spec_gas_const = 287.1
def __call__(self, t, x_vec):
# JSH/TW Nodal DG Methods, p.326
rho = numpy.ones_like(x_vec[0])
rho_u = x_vec[1] * x_vec[1]
rho_v = numpy.zeros_like(x_vec[0])
e = (2 * self.mu * x_vec[0] + 10) / (self.gamma - 1) + x_vec[1]**4 / 2
from grudge.tools import join_fields
return join_fields(rho, e, rho_u, rho_v)
def properties(self):
return(self.gamma, self.mu, self.prandtl, self.spec_gas_const)
def volume_interpolant(self, t, discr):
return discr.convert_volume(
self(t, discr.nodes.T
.astype(discr.default_scalar_type)),
kind=discr.compute_kind)
def boundary_interpolant(self, t, discr, tag):
result = discr.convert_boundary(
self(t, discr.get_boundary(tag).nodes.T
.astype(discr.default_scalar_type)),
tag=tag, kind=discr.compute_kind)
return result
def main():
from grudge.backends import guess_run_context
rcon = guess_run_context(
#["cuda"]
)
from grudge.tools import EOCRecorder, to_obj_array
eoc_rec = EOCRecorder()
def boundary_tagger(vertices, el, face_nr, all_v):
return ["inflow"]
if rcon.is_head_rank:
from grudge.mesh import make_rect_mesh, \
make_centered_regular_rect_mesh
#mesh = make_rect_mesh((0,0), (10,1), max_area=0.01)
refine = 1
mesh = make_centered_regular_rect_mesh((0,0), (10,1), n=(20,4),
#periodicity=(True, False),
post_refine_factor=refine,
boundary_tagger=boundary_tagger)
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
for order in [3]:
discr = rcon.make_discretization(mesh_data, order=order,
default_scalar_type=numpy.float64)
from grudge.visualization import SiloVisualizer, VtkVisualizer
#vis = VtkVisualizer(discr, rcon, "shearflow-%d" % order)
vis = SiloVisualizer(discr, rcon)
shearflow = SteadyShearFlow()
fields = shearflow.volume_interpolant(0, discr)
gamma, mu, prandtl, spec_gas_const = shearflow.properties()
from grudge.models.gas_dynamics import GasDynamicsOperator
op = GasDynamicsOperator(dimensions=2, gamma=gamma, mu=mu,
prandtl=prandtl, spec_gas_const=spec_gas_const,
bc_inflow=shearflow, bc_outflow=shearflow, bc_noslip=shearflow,
inflow_tag="inflow", outflow_tag="outflow", noslip_tag="noslip")
navierstokes_ex = op.bind(discr)
max_eigval = [0]
def rhs(t, q):
ode_rhs, speed = navierstokes_ex(t, q)
max_eigval[0] = speed
return ode_rhs
# needed to get first estimate of maximum eigenvalue
rhs(0, fields)
if rcon.is_head_rank:
print("---------------------------------------------")
print("order %d" % order)
print("---------------------------------------------")
print("#elements=", len(mesh.elements))
from grudge.timestep import RK4TimeStepper
stepper = RK4TimeStepper()
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
logmgr = LogManager("navierstokes-cpu-%d-%d.dat" % (order, refine),
"w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
# timestep loop -------------------------------------------------------
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=0.3,
#max_steps=500,
logmgr=logmgr,
max_dt_getter=lambda t: op.estimate_timestep(discr,
stepper=stepper, t=t, max_eigenvalue=max_eigval[0]))
for step, t, dt in step_it:
if step % 10 == 0:
#if False:
visf = vis.make_file("shearflow-%d-%04d" % (order, step))
#true_fields = shearflow.volume_interpolant(t, discr)
from pyvisfile.silo import DB_VARTYPE_VECTOR
vis.add_data(visf,
[
("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
("e", discr.convert_volume(op.e(fields), kind="numpy")),
("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
("u", discr.convert_volume(op.u(fields), kind="numpy")),
#("true_rho", discr.convert_volume(op.rho(true_fields), kind="numpy")),
#("true_e", discr.convert_volume(op.e(true_fields), kind="numpy")),
#("true_rho_u", discr.convert_volume(op.rho_u(true_fields), kind="numpy")),
#("true_u", discr.convert_volume(op.u(true_fields), kind="numpy")),
],
expressions=[
#("diff_rho", "rho-true_rho"),
#("diff_e", "e-true_e"),
#("diff_rho_u", "rho_u-true_rho_u", DB_VARTYPE_VECTOR),
("p", "0.4*(e- 0.5*(rho_u*u))"),
],
time=t, step=step
)
visf.close()
fields = stepper(fields, t, dt, rhs)
true_fields = shearflow.volume_interpolant(t, discr)
l2_error = discr.norm(op.u(fields)-op.u(true_fields))
eoc_rec.add_data_point(order, l2_error)
print()
print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
logmgr.set_constant("l2_error", l2_error)
finally:
vis.close()
logmgr.save()
discr.close()
if __name__ == "__main__":
main()
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
import numpy.linalg as la
from six.moves import range
def make_squaremesh():
def round_trip_connect(seq):
result = []
for i in range(len(seq)):
result.append((i, (i+1)%len(seq)))
return result
def needs_refinement(vertices, area):
x = sum(numpy.array(v) for v in vertices)/3
max_area_volume = 0.7e-2 + 0.03*(0.05*x[1]**2 + 0.3*min(x[0]+1,0)**2)
max_area_corners = 1e-3 + 0.001*max(
la.norm(x-corner)**4 for corner in obstacle_corners)
return bool(area > 2.5*min(max_area_volume, max_area_corners))
from meshpy.geometry import make_box
points, facets, _, _ = make_box((-0.5,-0.5), (0.5,0.5))
obstacle_corners = points[:]
from meshpy.geometry import GeometryBuilder, Marker
profile_marker = Marker.FIRST_USER_MARKER
builder = GeometryBuilder()
builder.add_geometry(points=points, facets=facets,
facet_markers=profile_marker)
points, facets, _, facet_markers = make_box((-16, -22), (25, 22))
builder.add_geometry(points=points, facets=facets,
facet_markers=facet_markers)
from meshpy.triangle import MeshInfo, build
mi = MeshInfo()
builder.set(mi)
mi.set_holes([(0,0)])
mesh = build(mi, refinement_func=needs_refinement,
allow_boundary_steiner=True,
generate_faces=True)
print("%d elements" % len(mesh.elements))
from meshpy.triangle import write_gnuplot_mesh
write_gnuplot_mesh("mesh.dat", mesh)
fvi2fm = mesh.face_vertex_indices_to_face_marker
face_marker_to_tag = {
profile_marker: "noslip",
Marker.MINUS_X: "inflow",
Marker.PLUS_X: "outflow",
Marker.MINUS_Y: "inflow",
Marker.PLUS_Y: "inflow"
}
def bdry_tagger(fvi, el, fn, all_v):
face_marker = fvi2fm[fvi]
return [face_marker_to_tag[face_marker]]
from grudge.mesh import make_conformal_mesh_ext
vertices = numpy.asarray(mesh.points, dtype=float, order="C")
from grudge.mesh.element import Triangle
return make_conformal_mesh_ext(
vertices,
[Triangle(i, el_idx, vertices)
for i, el_idx in enumerate(mesh.elements)],
bdry_tagger)
def main():
import logging
logging.basicConfig(level=logging.INFO)
from grudge.backends import guess_run_context
rcon = guess_run_context()
if rcon.is_head_rank:
if True:
mesh = make_squaremesh()
else:
from grudge.mesh import make_rect_mesh
mesh = make_rect_mesh(
boundary_tagger=lambda fvi, el, fn, all_v: ["inflow"],
max_area=0.1)
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
from pytools import add_python_path_relative_to_script
add_python_path_relative_to_script(".")
for order in [3]:
from gas_dynamics_initials import UniformMachFlow
square = UniformMachFlow(gaussian_pulse_at=numpy.array([-2, 2]),
pulse_magnitude=0.003)
from grudge.models.gas_dynamics import (
GasDynamicsOperator,
GammaLawEOS)
op = GasDynamicsOperator(dimensions=2,
equation_of_state=GammaLawEOS(square.gamma), mu=square.mu,
prandtl=square.prandtl, spec_gas_const=square.spec_gas_const,
bc_inflow=square, bc_outflow=square, bc_noslip=square,
inflow_tag="inflow", outflow_tag="outflow", noslip_tag="noslip")
discr = rcon.make_discretization(mesh_data, order=order,
debug=["cuda_no_plan",
"cuda_dump_kernels",
#"dump_dataflow_graph",
#"dump_optemplate_stages",
#"dump_dataflow_graph",
#"dump_op_code"
#"cuda_no_plan_el_local"
],
default_scalar_type=numpy.float64,
tune_for=op.sym_operator(),
quad_min_degrees={
"gasdyn_vol": 3*order,
"gasdyn_face": 3*order,
}
)
from grudge.visualization import SiloVisualizer, VtkVisualizer
#vis = VtkVisualizer(discr, rcon, "shearflow-%d" % order)
vis = SiloVisualizer(discr, rcon)
from grudge.timestep.runge_kutta import (
LSRK4TimeStepper, ODE23TimeStepper, ODE45TimeStepper)
from grudge.timestep.dumka3 import Dumka3TimeStepper
#stepper = LSRK4TimeStepper(dtype=discr.default_scalar_type,
#vector_primitive_factory=discr.get_vector_primitive_factory())
stepper = ODE23TimeStepper(dtype=discr.default_scalar_type,
rtol=1e-6,
vector_primitive_factory=discr.get_vector_primitive_factory())
# Dumka works kind of poorly
#stepper = Dumka3TimeStepper(dtype=discr.default_scalar_type,
#rtol=1e-7, pol_index=2,
#vector_primitive_factory=discr.get_vector_primitive_factory())
#from grudge.timestep.dumka3 import Dumka3TimeStepper
#stepper = Dumka3TimeStepper(3, rtol=1e-7)
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
logmgr = LogManager("cns-square-sp-%d.dat" % order, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
from pytools.log import LogQuantity
class ChangeSinceLastStep(LogQuantity):
"""Records the change of a variable between a time step and the previous
one"""
def __init__(self, name="change"):
LogQuantity.__init__(self, name, "1", "Change since last time step")
self.old_fields = 0
def __call__(self):
result = discr.norm(fields - self.old_fields)
self.old_fields = fields
return result
#logmgr.add_quantity(ChangeSinceLastStep())
add_simulation_quantities(logmgr)
logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
# filter setup ------------------------------------------------------------
from grudge.discretization import Filter, ExponentialFilterResponseFunction
mode_filter = Filter(discr,
ExponentialFilterResponseFunction(min_amplification=0.95, order=6))
# timestep loop -------------------------------------------------------
fields = square.volume_interpolant(0, discr)
navierstokes_ex = op.bind(discr)
max_eigval = [0]
def rhs(t, q):
ode_rhs, speed = navierstokes_ex(t, q)
max_eigval[0] = speed
return ode_rhs
rhs(0, fields)
if rcon.is_head_rank:
print("---------------------------------------------")
print("order %d" % order)
print("---------------------------------------------")
print("#elements=", len(mesh.elements))
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=1000,
#max_steps=500,
logmgr=logmgr,
max_dt_getter=lambda t: next_dt,
taken_dt_getter=lambda: taken_dt)
model_stepper = LSRK4TimeStepper()
next_dt = op.estimate_timestep(discr,
stepper=model_stepper, t=0,
max_eigenvalue=max_eigval[0])
for step, t, dt in step_it:
#if (step % 10000 == 0): #and step < 950000) or (step % 500 == 0 and step > 950000):
#if False:
if step % 5 == 0:
visf = vis.make_file("square-%d-%06d" % (order, step))
#from pyvisfile.silo import DB_VARTYPE_VECTOR
vis.add_data(visf,
[
("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
("e", discr.convert_volume(op.e(fields), kind="numpy")),
("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
("u", discr.convert_volume(op.u(fields), kind="numpy")),
],
expressions=[
("p", "(0.4)*(e- 0.5*(rho_u*u))"),
],
time=t, step=step
)
visf.close()
if stepper.adaptive:
fields, t, taken_dt, next_dt = stepper(fields, t, dt, rhs)
else:
taken_dt = dt
fields = stepper(fields, t, dt, rhs)
dt = op.estimate_timestep(discr,
stepper=model_stepper, t=0,
max_eigenvalue=max_eigval[0])
#fields = mode_filter(fields)
finally:
vis.close()
logmgr.save()
discr.close()
if __name__ == "__main__":
main()
# Copyright (C) 2008 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
import numpy.linalg as la
from six.moves import zip
def make_wingmesh():
import numpy
from math import pi, cos, sin
from meshpy.tet import MeshInfo, build
from meshpy.geometry import GeometryBuilder, Marker, \
generate_extrusion, make_box
geob = GeometryBuilder()
profile_marker = Marker.FIRST_USER_MARKER
wing_length = 2
wing_subdiv = 5
rz_points = [
(0, -wing_length*1.05),
(0.7, -wing_length*1.05),
] + [
(r, x) for x, r in zip(
numpy.linspace(-wing_length, 0, wing_subdiv, endpoint=False),
numpy.linspace(0.8, 1, wing_subdiv, endpoint=False))
] + [(1,0)] + [
(r, x) for x, r in zip(
numpy.linspace(wing_length, 0, wing_subdiv, endpoint=False),
numpy.linspace(0.8, 1, wing_subdiv, endpoint=False))
][::-1] + [
(0.7, wing_length*1.05),
(0, wing_length*1.05)
]
from meshpy.naca import get_naca_points
geob.add_geometry(*generate_extrusion(
rz_points=rz_points,
base_shape=get_naca_points("0012", number_of_points=20),
ring_markers=(wing_subdiv*2+4)*[profile_marker]))
def deform_wing(p):
x, y, z = p
return numpy.array([
x + 0.8*abs(z/wing_length)** 1.2,
y + 0.1*abs(z/wing_length)**2,
z])
geob.apply_transform(deform_wing)
points, facets, facet_markers = make_box(
numpy.array([-1.5,-1,-wing_length-1], dtype=numpy.float64),
numpy.array([3,1,wing_length+1], dtype=numpy.float64))
geob.add_geometry(points, facets, facet_markers=facet_markers)
mesh_info = MeshInfo()
geob.set(mesh_info)
mesh_info.set_holes([(0.5,0,0)])
mesh = build(mesh_info)
print("%d elements" % len(mesh.elements))
fvi2fm = mesh.face_vertex_indices_to_face_marker
face_marker_to_tag = {
profile_marker: "noslip",
Marker.MINUS_X: "inflow",
Marker.PLUS_X: "outflow",
Marker.MINUS_Y: "inflow",
Marker.PLUS_Y: "inflow",
Marker.PLUS_Z: "inflow",
Marker.MINUS_Z: "inflow"
}
def bdry_tagger(fvi, el, fn, all_v):
face_marker = fvi2fm[fvi]
return [face_marker_to_tag[face_marker]]
from grudge.mesh import make_conformal_mesh
return make_conformal_mesh(mesh.points, mesh.elements, bdry_tagger)
def main():
from grudge.backends import guess_run_context
rcon = guess_run_context( ["cuda", "mpi"])
if rcon.is_head_rank:
mesh = make_wingmesh()
#from grudge.mesh import make_rect_mesh
#mesh = make_rect_mesh(
# boundary_tagger=lambda fvi, el, fn, all_v: ["inflow"])
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
for order in [3]:
from pytools import add_python_path_relative_to_script
add_python_path_relative_to_script("..")
from gas_dynamics_initials import UniformMachFlow
wing = UniformMachFlow(angle_of_attack=0)
from grudge.models.gas_dynamics import GasDynamicsOperator
op = GasDynamicsOperator(dimensions=3,
gamma=wing.gamma, mu=wing.mu,
prandtl=wing.prandtl, spec_gas_const=wing.spec_gas_const,
bc_inflow=wing, bc_outflow=wing, bc_noslip=wing,
inflow_tag="inflow", outflow_tag="outflow", noslip_tag="noslip")
discr = rcon.make_discretization(mesh_data, order=order,
debug=["cuda_no_plan",
#"cuda_dump_kernels",
#"dump_dataflow_graph",
#"dump_optemplate_stages",
#"dump_dataflow_graph",
#"print_op_code"
"cuda_no_metis",
],
default_scalar_type=numpy.float64,
tune_for=op.sym_operator())
from grudge.visualization import SiloVisualizer, VtkVisualizer
#vis = VtkVisualizer(discr, rcon, "shearflow-%d" % order)
vis = SiloVisualizer(discr, rcon)
fields = wing.volume_interpolant(0, discr)
navierstokes_ex = op.bind(discr)
max_eigval = [0]
def rhs(t, q):
ode_rhs, speed = navierstokes_ex(t, q)
max_eigval[0] = speed
return ode_rhs
rhs(0, fields)
if rcon.is_head_rank:
print("---------------------------------------------")
print("order %d" % order)
print("---------------------------------------------")
print("#elements=", len(mesh.elements))
from grudge.timestep import RK4TimeStepper
stepper = RK4TimeStepper()
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
logmgr = LogManager("navierstokes-%d.dat" % order, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
# timestep loop -------------------------------------------------------
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=200,
#max_steps=500,
logmgr=logmgr,
max_dt_getter=lambda t: 0.6 * op.estimate_timestep(discr,
stepper=stepper, t=t, max_eigenvalue=max_eigval[0]))
for step, t, dt in step_it:
if step % 200 == 0:
#if False:
visf = vis.make_file("wing-%d-%06d" % (order, step))
#rhs_fields = rhs(t, fields)
from pyvisfile.silo import DB_VARTYPE_VECTOR
from grudge.discretization import ones_on_boundary
vis.add_data(visf,
[
("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
("e", discr.convert_volume(op.e(fields), kind="numpy")),
("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
("u", discr.convert_volume(op.u(fields), kind="numpy")),
#("rhs_rho", discr.convert_volume(op.rho(rhs_fields), kind="numpy")),
#("rhs_e", discr.convert_volume(op.e(rhs_fields), kind="numpy")),
#("rhs_rho_u", discr.convert_volume(op.rho_u(rhs_fields), kind="numpy")),
],
expressions=[
("p", "(0.4)*(e- 0.5*(rho_u*u))"),
],
time=t, step=step
)
visf.close()
fields = stepper(fields, t, dt, rhs)
t += dt
finally:
vis.close()
logmgr.save()
discr.close()
if __name__ == "__main__":
main()
"""Minimal example of a grudge driver."""
"""Minimal example of viewing geometric quantities."""
from __future__ import division, print_function
__copyright__ = "Copyright (C) 2015 Andreas Kloeckner"
__copyright__ = """
Copyright (C) 2015 Andreas Kloeckner
Copyright (C) 2021 University of Illinois Board of Trustees
"""
__license__ = """
Permission is hereby granted, free of charge, to any person obtaining a copy
......@@ -24,37 +25,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
import numpy as np # noqa
import pyopencl as cl
from grudge import sym, bind, Discretization, shortcuts
import pyopencl.tools as cl_tools
from grudge import geometry, shortcuts
from grudge.array_context import PyOpenCLArrayContext
from grudge.discretization import make_discretization_collection
def main(write_output=True):
def main(write_output: bool = True) -> None:
cl_ctx = cl.create_some_context()
queue = cl.CommandQueue(cl_ctx)
from meshmode.mesh.generation import generate_warped_rect_mesh
mesh = generate_warped_rect_mesh(dim=2, order=4, n=6)
allocator = cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue))
actx = PyOpenCLArrayContext(queue, allocator=allocator)
discr = Discretization(cl_ctx, mesh, order=4)
from meshmode.mesh import BTAG_ALL
from meshmode.mesh.generation import generate_warped_rect_mesh
sym_op = sym.normal(sym.BTAG_ALL, mesh.dim)
#sym_op = sym.nodes(mesh.dim, where=sym.BTAG_ALL)
print(sym.pretty(sym_op))
op = bind(discr, sym_op)
print()
print(op.code)
mesh = generate_warped_rect_mesh(dim=2, order=4, nelements_side=6)
dcoll = make_discretization_collection(actx, mesh, order=4)
vec = op(queue)
nodes = actx.thaw(dcoll.nodes())
bdry_nodes = actx.thaw(dcoll.nodes(dd=BTAG_ALL))
bdry_normals = geometry.normal(actx, dcoll, dd=BTAG_ALL)
vis = shortcuts.make_visualizer(discr, 4)
vis.write_vtk_file("geo.vtu", [
])
if write_output:
vis = shortcuts.make_visualizer(dcoll)
vis.write_vtk_file("geo.vtu", [("nodes", nodes)])
bvis = shortcuts.make_boundary_visualizer(discr, 4)
bvis.write_vtk_file("bgeo.vtu", [
("normals", vec)
])
bvis = shortcuts.make_boundary_visualizer(dcoll)
bvis.write_vtk_file("bgeo.vtu", [("bdry normals", bdry_normals),
("bdry nodes", bdry_nodes)])
if __name__ == "__main__":
......
from __future__ import absolute_import
from __future__ import print_function
# Copyright (C) 2007 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy
import numpy.linalg as la
def main(write_output=True) :
from math import sin, cos, pi, exp, sqrt
from grudge.data import TimeConstantGivenFunction, \
ConstantGivenFunction
from grudge.backends import guess_run_context
rcon = guess_run_context()
dim = 2
def boundary_tagger(fvi, el, fn, all_v):
if el.face_normals[fn][0] > 0:
return ["dirichlet"]
else:
return ["neumann"]
if dim == 2:
if rcon.is_head_rank:
from grudge.mesh.generator import make_disk_mesh
mesh = make_disk_mesh(r=0.5, boundary_tagger=boundary_tagger)
elif dim == 3:
if rcon.is_head_rank:
from grudge.mesh.generator import make_ball_mesh
mesh = make_ball_mesh(max_volume=0.001)
else:
raise RuntimeError("bad number of dimensions")
if rcon.is_head_rank:
print("%d elements" % len(mesh.elements))
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
discr = rcon.make_discretization(mesh_data, order=3,
debug=["cuda_no_plan"],
default_scalar_type=numpy.float64)
if write_output:
from grudge.visualization import VtkVisualizer
vis = VtkVisualizer(discr, rcon, "fld")
def u0(x, el):
if la.norm(x) < 0.2:
return 1
else:
return 0
def coeff(x, el):
if x[0] < 0:
return 0.25
else:
return 1
def dirichlet_bc(t, x):
return 0
def neumann_bc(t, x):
return 2
from grudge.models.diffusion import DiffusionOperator
op = DiffusionOperator(discr.dimensions,
#coeff=coeff,
dirichlet_tag="dirichlet",
dirichlet_bc=TimeConstantGivenFunction(ConstantGivenFunction(0)),
neumann_tag="neumann",
neumann_bc=TimeConstantGivenFunction(ConstantGivenFunction(1))
)
u = discr.interpolate_volume_function(u0)
# diagnostics setup -------------------------------------------------------
from pytools.log import LogManager, \
add_general_quantities, \
add_simulation_quantities, \
add_run_info
if write_output:
log_file_name = "heat.dat"
else:
log_file_name = None
logmgr = LogManager(log_file_name, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
from grudge.log import LpNorm
u_getter = lambda: u
logmgr.add_quantity(LpNorm(u_getter, discr, 1, name="l1_u"))
logmgr.add_quantity(LpNorm(u_getter, discr, name="l2_u"))
logmgr.add_watches(["step.max", "t_sim.max", "l2_u", "t_step.max"])
# timestep loop -----------------------------------------------------------
from grudge.timestep.runge_kutta import LSRK4TimeStepper, ODE45TimeStepper
from grudge.timestep.dumka3 import Dumka3TimeStepper
#stepper = LSRK4TimeStepper()
stepper = Dumka3TimeStepper(3, rtol=1e-6, rcon=rcon,
vector_primitive_factory=discr.get_vector_primitive_factory(),
dtype=discr.default_scalar_type)
#stepper = ODE45TimeStepper(rtol=1e-6, rcon=rcon,
#vector_primitive_factory=discr.get_vector_primitive_factory(),
#dtype=discr.default_scalar_type)
stepper.add_instrumentation(logmgr)
rhs = op.bind(discr)
try:
next_dt = op.estimate_timestep(discr,
stepper=LSRK4TimeStepper(), t=0, fields=u)
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=0.1, logmgr=logmgr,
max_dt_getter=lambda t: next_dt,
taken_dt_getter=lambda: taken_dt)
for step, t, dt in step_it:
if step % 10 == 0 and write_output:
visf = vis.make_file("fld-%04d" % step)
vis.add_data(visf, [
("u", discr.convert_volume(u, kind="numpy")),
], time=t, step=step)
visf.close()
u, t, taken_dt, next_dt = stepper(u, t, next_dt, rhs)
#u = stepper(u, t, dt, rhs)
assert discr.norm(u) < 1
finally:
if write_output:
vis.close()
logmgr.close()
discr.close()
if __name__ == "__main__":
main()
# entry points for py.test ----------------------------------------------------
from pytools.test import mark_test
@mark_test.long
def test_heat():
main(write_output=False)
# Solves the PDE:
# \begin{cases}
# u_t + 2\pi u_x = 0, \\
# u(0, t) = -\sin(2\pi t), \\
# u(x, 0) = \sin(x),
# \end{cases}
# on the domain $x \in [0, 2\pi]$. We closely follow Chapter 3 of
# "Nodal Discontinuous Galerkin Methods" by Hesthaven & Warburton.
# BEGINEXAMPLE
import numpy as np
import pyopencl as cl
from meshmode.array_context import PyOpenCLArrayContext
from meshmode.mesh.generation import generate_box_mesh
import grudge.geometry as geo
import grudge.op as op
from grudge.discretization import make_discretization_collection
from grudge.dof_desc import FACE_RESTR_INTERIOR, BoundaryDomainTag, as_dofdesc
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
actx = PyOpenCLArrayContext(queue)
nel = 10
coords = np.linspace(0, 2*np.pi, nel)
mesh = generate_box_mesh((coords,),
boundary_tag_to_face={"left": ["-x"],
"right": ["+x"]})
dcoll = make_discretization_collection(actx, mesh, order=1)
def initial_condition(x):
# 'x' contains ndim arrays.
# 'x[0]' gets the first coordinate value of all the nodes
return actx.np.sin(x[0])
def left_boundary_condition(x, t):
return actx.np.sin(x[0] - 2 * np.pi * t)
def flux(dcoll, u_tpair):
dd = u_tpair.dd
velocity = np.array([2 * np.pi])
normal = geo.normal(actx, dcoll, dd)
v_dot_n = np.dot(velocity, normal)
u_upwind = actx.np.where(v_dot_n > 0,
u_tpair.int, u_tpair.ext)
return u_upwind * v_dot_n
vol_discr = dcoll.discr_from_dd("vol")
left_bndry = as_dofdesc(BoundaryDomainTag("left"))
right_bndry = as_dofdesc(BoundaryDomainTag("right"))
x_vol = actx.thaw(dcoll.nodes())
x_bndry = actx.thaw(dcoll.discr_from_dd(left_bndry).nodes())
uh = initial_condition(x_vol)
dt = 0.001
t = 0
t_final = 0.5
# timestepper loop
while t < t_final:
# extract the left boundary trace pair
lbnd_tpair = op.bv_trace_pair(dcoll,
dd=left_bndry,
interior=uh,
exterior=left_boundary_condition(x_bndry, t))
# extract the right boundary trace pair
rbnd_tpair = op.bv_trace_pair(dcoll,
dd=right_bndry,
interior=uh,
exterior=op.project(dcoll, "vol",
right_bndry, uh))
# extract the trace pairs on the interior faces
interior_tpair = op.local_interior_trace_pair(dcoll, uh)
Su = op.weak_local_grad(dcoll, uh)
lift = op.face_mass(dcoll,
# left boundary weak-flux terms
op.project(dcoll,
left_bndry, "all_faces",
flux(dcoll, lbnd_tpair))
# right boundary weak-flux terms
+ op.project(dcoll,
right_bndry, "all_faces",
flux(dcoll, rbnd_tpair))
# interior weak-flux terms
+ op.project(dcoll,
FACE_RESTR_INTERIOR, "all_faces",
flux(dcoll, interior_tpair)))
duh_by_dt = op.inverse_mass(dcoll,
np.dot([2 * np.pi], Su) - lift)
# forward euler time step
uh = uh + dt * duh_by_dt
t += dt
# ENDEXAMPLE
# Plot the solution:
def u_exact(x, t):
return actx.np.sin(x[0] - 2 * np.pi * t)
assert op.norm(dcoll,
uh - u_exact(x_vol, t_final),
p=2) <= 0.1
import matplotlib.pyplot as plt
plt.plot(actx.to_numpy(actx.np.ravel(x_vol[0][0])),
actx.to_numpy(actx.np.ravel(uh[0])), label="Numerical")
plt.plot(actx.to_numpy(actx.np.ravel(x_vol[0][0])),
actx.to_numpy(actx.np.ravel(u_exact(x_vol, t_final)[0])), label="Exact")
plt.xlabel("$x$")
plt.ylabel("$u$")
plt.legend()
plt.show()
bessel_zeros.py
2d_cavity
# Copyright (C) 2007 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
from grudge import sym
from six.moves import zip
def get_rectangular_3D_cavity_mode(E_0, mode_indices, dimensions=(1, 1, 1)):
"""A rectangular TM cavity mode."""
kx, ky, kz = factors = [n*numpy.pi/a for n, a in zip(mode_indices, dimensions)]
omega = numpy.sqrt(sum(f**2 for f in factors))
gamma_squared = ky**2 + kx**2
nodes = sym.nodes(3)
x = nodes[0]
y = nodes[1]
z = nodes[2]
sx = sym.sin(kx*x)
sy = sym.sin(ky*y)
sz = sym.sin(kz*z)
cx = sym.cos(kx*x)
cy = sym.cos(ky*y)
cz = sym.cos(kz*z)
tdep = sym.exp(-1j * omega * sym.ScalarVariable("t"))
result = sym.join_fields(
-kx * kz * E_0*cx*sy*sz*tdep / gamma_squared, # ex
-ky * kz * E_0*sx*cy*sz*tdep / gamma_squared, # ey
E_0 * sx*sy*cz*tdep, # ez
-1j * omega * ky*E_0*sx*cy*cz*tdep / gamma_squared, # hx
1j * omega * kx*E_0*cx*sy*cz*tdep / gamma_squared,
0,
)
return result
def get_rectangular_2D_cavity_mode(E_0, mode_indices):
"""A TM cavity mode.
Returns an expression depending on *epsilon*, *mu*, and *t*.
"""
kx, ky = factors = [n*numpy.pi for n in mode_indices]
omega = numpy.sqrt(sum(f**2 for f in factors))
nodes = sym.nodes(2)
x = nodes[0]
y = nodes[1]
tfac = sym.ScalarVariable("t") * omega
result = sym.join_fields(
0,
0,
sym.sin(kx * x) * sym.sin(ky * y) * sym.cos(tfac), # ez
-ky * sym.sin(kx * x) * sym.cos(ky * y) * sym.sin(tfac) / omega, # hx
kx * sym.cos(kx * x) * sym.sin(ky * y) * sym.sin(tfac) / omega, # hy
0,
)
return result
"""Minimal example of a grudge driver."""
from __future__ import division, print_function
__copyright__ = "Copyright (C) 2015 Andreas Kloeckner"
__copyright__ = """
Copyright (C) 2015 Andreas Kloeckner
Copyright (C) 2021 University of Illinois Board of Trustees
"""
__license__ = """
Permission is hereby granted, free of charge, to any person obtaining a copy
......@@ -25,25 +24,37 @@ THE SOFTWARE.
"""
import logging
import numpy as np
import pyopencl as cl
from grudge.shortcuts import set_up_rk4
from grudge import sym, bind, Discretization
import pyopencl.tools as cl_tools
from grudge import op
from grudge.array_context import PyOpenCLArrayContext
from grudge.discretization import make_discretization_collection
from grudge.models.em import get_rectangular_cavity_mode
from grudge.shortcuts import set_up_rk4
logger = logging.getLogger(__name__)
def main(dims, write_output=True, order=4):
cl_ctx = cl.create_some_context()
def main(ctx_factory, dim=3, order=4, visualize=False):
cl_ctx = ctx_factory()
queue = cl.CommandQueue(cl_ctx)
allocator = cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue))
actx = PyOpenCLArrayContext(queue, allocator=allocator)
from meshmode.mesh.generation import generate_regular_rect_mesh
mesh = generate_regular_rect_mesh(
a=(0.0,)*dims,
b=(1.0,)*dims,
n=(5,)*dims)
a=(0.0,)*dim,
b=(1.0,)*dim,
nelements_per_axis=(4,)*dim)
discr = Discretization(cl_ctx, mesh, order=order)
dcoll = make_discretization_collection(actx, mesh, order=order)
if 0:
epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
......@@ -55,77 +66,105 @@ def main(dims, write_output=True, order=4):
mu = 1
from grudge.models.em import MaxwellOperator
op = MaxwellOperator(epsilon, mu, flux_type=0.5, dimensions=dims)
queue = cl.CommandQueue(discr.cl_context)
if dims == 3:
sym_mode = get_rectangular_cavity_mode(1, (1, 2, 2))
fields = bind(discr, sym_mode)(queue, t=0, epsilon=epsilon, mu=mu)
else:
sym_mode = get_rectangular_cavity_mode(1, (2, 3))
fields = bind(discr, sym_mode)(queue, t=0)
maxwell_operator = MaxwellOperator(
dcoll,
epsilon,
mu,
flux_type=0.5,
dimensions=dim
)
# FIXME
#dt = op.estimate_rk4_timestep(discr, fields=fields)
def cavity_mode(x, t=0):
if dim == 3:
return get_rectangular_cavity_mode(actx, x, t, 1, (1, 2, 2))
else:
return get_rectangular_cavity_mode(actx, x, t, 1, (2, 3))
op.check_bc_coverage(mesh)
fields = cavity_mode(actx.thaw(dcoll.nodes()), t=0)
# print(sym.pretty(op.sym_operator()))
bound_op = bind(discr, op.sym_operator())
maxwell_operator.check_bc_coverage(mesh)
def rhs(t, w):
return bound_op(queue, t=t, w=w)
return maxwell_operator.operator(t, w)
if mesh.dim == 2:
dt = 0.004
elif mesh.dim == 3:
dt = 0.002
dt = actx.to_numpy(
maxwell_operator.estimate_rk4_timestep(actx, dcoll, fields=fields))
dt_stepper = set_up_rk4("w", dt, fields, rhs)
final_t = dt * 600
nsteps = int(final_t/dt)
target_steps = 60
final_t = dt * target_steps
nsteps = int(final_t/dt) + 1
print("dt=%g nsteps=%d" % (dt, nsteps))
logger.info("dt = %g nsteps = %d", dt, nsteps)
from grudge.shortcuts import make_visualizer
vis = make_visualizer(discr, vis_order=order)
vis = make_visualizer(dcoll)
step = 0
norm = bind(discr, sym.norm(2, sym.var("u")))
def norm(u):
return op.norm(dcoll, u, 2)
from time import time
t_last_step = time()
e, h = maxwell_operator.split_eh(fields)
e, h = op.split_eh(fields)
if 1:
vis.write_vtk_file("fld-%04d.vtu" % step,
[
("e", e),
("h", h),
])
if visualize:
vis.write_vtk_file(
f"fld-cavities-{step:04d}.vtu",
[
("e", e),
("h", h),
]
)
for event in dt_stepper.run(t_end=final_t):
if isinstance(event, dt_stepper.StateComputed):
assert event.component_id == "w"
step += 1
e, h = maxwell_operator.split_eh(event.state_component)
norm_e0 = actx.to_numpy(norm(u=e[0]))
norm_e1 = actx.to_numpy(norm(u=e[1]))
norm_h0 = actx.to_numpy(norm(u=h[0]))
norm_h1 = actx.to_numpy(norm(u=h[1]))
logger.info(
"[%04d] t = %.5f |e0| = %.5e, |e1| = %.5e, |h0| = %.5e, |h1| = %.5e",
step, event.t,
norm_e0, norm_e1, norm_h0, norm_h1
)
print(step, event.t, norm(queue, u=e[0]), norm(queue, u=e[1]),
norm(queue, u=h[0]), norm(queue, u=h[1]),
time()-t_last_step)
if step % 10 == 0:
e, h = op.split_eh(event.state_component)
vis.write_vtk_file("fld-%04d.vtu" % step,
if visualize:
vis.write_vtk_file(
f"fld-cavities-{step:04d}.vtu",
[
("e", e),
("h", h),
])
t_last_step = time()
]
)
# NOTE: These are here to ensure the solution is bounded for the
# time interval specified
assert norm_e0 < 0.5
assert norm_e1 < 0.5
assert norm_h0 < 0.5
assert norm_h1 < 0.5
if __name__ == "__main__":
main(3)
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--dim", default=3, type=int)
parser.add_argument("--order", default=4, type=int)
parser.add_argument("--visualize", action="store_true")
args = parser.parse_args()
logging.basicConfig(level=logging.INFO)
main(cl.create_some_context,
dim=args.dim,
order=args.order,
visualize=args.visualize)
# Copyright (C) 2007 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# FIXME: This example doesn't quite do what it should any more.
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
import numpy.linalg as la
from six.moves import range
from six.moves import zip
def main(write_output=True, allow_features=None):
from grudge.timestep import RK4TimeStepper
from grudge.mesh import make_ball_mesh, make_cylinder_mesh, make_box_mesh
from grudge.visualization import \
VtkVisualizer, \
SiloVisualizer, \
get_rank_partition
from math import sqrt, pi
from grudge.backends import guess_run_context
rcon = guess_run_context(allow_features)
epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
mu0 = 4*pi*1e-7 # N/A**2.
epsilon = 1*epsilon0
mu = 1*mu0
dims = 3
if rcon.is_head_rank:
if dims == 2:
from grudge.mesh import make_rect_mesh
mesh = make_rect_mesh(
a=(-10.5,-1.5),
b=(10.5,1.5),
max_area=0.1
)
elif dims == 3:
from grudge.mesh import make_box_mesh
mesh = make_box_mesh(
a=(-10.5,-1.5,-1.5),
b=(10.5,1.5,1.5),
max_volume=0.1)
if rcon.is_head_rank:
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
#for order in [1,2,3,4,5,6]:
discr = rcon.make_discretization(mesh_data, order=3)
if write_output:
vis = VtkVisualizer(discr, rcon, "dipole")
from analytic_solutions import DipoleFarField, SphericalFieldAdapter
from grudge.data import ITimeDependentGivenFunction
sph_dipole = DipoleFarField(
q=1, #C
d=1/39,
omega=2*pi*1e8,
epsilon=epsilon0,
mu=mu0,
)
cart_dipole = SphericalFieldAdapter(sph_dipole)
class PointDipoleSource(ITimeDependentGivenFunction):
def __init__(self):
from pyrticle.tools import CInfinityShapeFunction
sf = CInfinityShapeFunction(
0.1*sph_dipole.wavelength,
discr.dimensions)
self.num_sf = discr.interpolate_volume_function(
lambda x, el: sf(x))
self.vol_0 = discr.volume_zeros()
def volume_interpolant(self, t, discr):
from grudge.tools import make_obj_array
return make_obj_array([
self.vol_0,
self.vol_0,
sph_dipole.source_modulation(t)*self.num_sf
])
from grudge.mesh import BTAG_ALL, BTAG_NONE
if dims == 2:
from grudge.models.em import TMMaxwellOperator as MaxwellOperator
else:
from grudge.models.em import MaxwellOperator
op = MaxwellOperator(
epsilon, mu,
flux_type=1,
pec_tag=BTAG_NONE,
absorb_tag=BTAG_ALL,
current=PointDipoleSource(),
)
fields = op.assemble_eh(discr=discr)
if rcon.is_head_rank:
print("#elements=", len(mesh.elements))
stepper = RK4TimeStepper()
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
if write_output:
log_file_name = "dipole.dat"
else:
log_file_name = None
logmgr = LogManager(log_file_name, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
from pytools.log import IntervalTimer
vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
logmgr.add_quantity(vis_timer)
from grudge.log import EMFieldGetter, add_em_quantities
field_getter = EMFieldGetter(discr, op, lambda: fields)
add_em_quantities(logmgr, op, field_getter)
from pytools.log import PushLogQuantity
relerr_e_q = PushLogQuantity("relerr_e", "1", "Relative error in masked E-field")
relerr_h_q = PushLogQuantity("relerr_h", "1", "Relative error in masked H-field")
logmgr.add_quantity(relerr_e_q)
logmgr.add_quantity(relerr_h_q)
logmgr.add_watches(["step.max", "t_sim.max",
("W_field", "W_el+W_mag"), "t_step.max",
"relerr_e", "relerr_h"])
if write_output:
point_timeseries = [
(open("b-x%d-vs-time.dat" % i, "w"),
open("b-x%d-vs-time-true.dat" % i, "w"),
discr.get_point_evaluator(numpy.array([i,0,0][:dims],
dtype=discr.default_scalar_type)))
for i in range(1,5)
]
# timestep loop -------------------------------------------------------
mask = discr.interpolate_volume_function(sph_dipole.far_field_mask)
def apply_mask(field):
from grudge.tools import log_shape
ls = log_shape(field)
result = discr.volume_empty(ls)
from pytools import indices_in_shape
for i in indices_in_shape(ls):
result[i] = mask * field[i]
return result
rhs = op.bind(discr)
t = 0
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=1e-8, logmgr=logmgr,
max_dt_getter=lambda t: op.estimate_timestep(discr,
stepper=stepper, t=t, fields=fields))
for step, t, dt in step_it:
if write_output and step % 10 == 0:
sub_timer = vis_timer.start_sub_timer()
e, h = op.split_eh(fields)
sph_dipole.set_time(t)
true_e, true_h = op.split_eh(
discr.interpolate_volume_function(cart_dipole))
visf = vis.make_file("dipole-%04d" % step)
mask_e = apply_mask(e)
mask_h = apply_mask(h)
mask_true_e = apply_mask(true_e)
mask_true_h = apply_mask(true_h)
from pyvisfile.silo import DB_VARTYPE_VECTOR
vis.add_data(visf,
[
("e", e),
("h", h),
("true_e", true_e),
("true_h", true_h),
("mask_e", mask_e),
("mask_h", mask_h),
("mask_true_e", mask_true_e),
("mask_true_h", mask_true_h)],
time=t, step=step)
visf.close()
sub_timer.stop().submit()
from grudge.tools import relative_error
relerr_e_q.push_value(
relative_error(
discr.norm(mask_e-mask_true_e),
discr.norm(mask_true_e)))
relerr_h_q.push_value(
relative_error(
discr.norm(mask_h-mask_true_h),
discr.norm(mask_true_h)))
if write_output:
for outf_num, outf_true, evaluator in point_timeseries:
for outf, ev_h in zip([outf_num, outf_true],
[h, true_h]):
outf.write("%g\t%g\n" % (t, op.mu*evaluator(ev_h[1])))
outf.flush()
fields = stepper(fields, t, dt, rhs)
finally:
if write_output:
vis.close()
logmgr.save()
discr.close()
if __name__ == "__main__":
main()
from pytools.test import mark_test
@mark_test.long
def test_run():
main(write_output=False)
from __future__ import absolute_import
from six.moves import range
def main():
import scipy.special
maxnu = 10
n_zeros = 20
zeros = []
for n in range(0,maxnu+1):
zeros.append(list(scipy.special.jn_zeros(n, n_zeros)))
outf = open("bessel_zeros.py", "w").write("bessel_zeros = %s" % zeros)
if __name__ == "__main__":
main()
# Copyright (C) 2007 Andreas Kloeckner
# Adapted 2010 by David Powell
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This example is for maxwell's equations in a 2D rectangular cavity
with inhomogeneous dielectric filling"""
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy
CAVITY_GEOMETRY = """
// a rectangular cavity with a dielectric in one region
lc = 10e-3;
height = 50e-3;
air_width = 100e-3;
dielectric_width = 50e-3;
Point(1) = {0, 0, 0, lc};
Point(2) = {air_width, 0, 0, lc};
Point(3) = {air_width+dielectric_width, 0, 0, lc};
Point(4) = {air_width+dielectric_width, height, 0, lc};
Point(5) = {air_width, height, 0, lc};
Point(6) = {0, height, 0, lc};
Line(1) = {1, 2};
Line(2) = {2, 3};
Line(3) = {3, 4};
Line(4) = {4, 5};
Line(5) = {5, 6};
Line(6) = {6, 1};
Line(7) = {2, 5};
Line Loop(1) = {1, 7, 5, 6};
Line Loop(2) = {2, 3, 4, -7};
Plane Surface(1) = {1};
Plane Surface(2) = {2};
Physical Surface("vacuum") = {1};
Physical Surface("dielectric") = {2};
"""
def main(write_output=True, allow_features=None, flux_type_arg=1,
bdry_flux_type_arg=None, extra_discr_args={}):
from math import sqrt, pi
from grudge.models.em import TEMaxwellOperator
from grudge.backends import guess_run_context
rcon = guess_run_context(allow_features)
epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
mu0 = 4*pi*1e-7 # N/A**2.
c = 1/sqrt(mu0*epsilon0)
materials = {"vacuum" : (epsilon0, mu0),
"dielectric" : (2*epsilon0, mu0)}
output_dir = "2d_cavity"
import os
if not os.access(output_dir, os.F_OK):
os.makedirs(output_dir)
# should no tag raise an error or default to free space?
def eps_val(x, el):
for key in list(materials.keys()):
if el in material_elements[key]:
return materials[key][0]
raise ValueError("Element does not belong to any material")
def mu_val(x, el):
for key in list(materials.keys()):
if el in material_elements[key]:
return materials[key][1]
raise ValueError("Element does not belong to any material")
# geometry of cavity
d = 100e-3
a = 150e-3
# analytical frequency and transverse wavenumbers of resonance
f0 = 9.0335649907522321e8
h = 2*pi*f0/c
l = -h*sqrt(2)
# substitute the following and change materials for a homogeneous cavity
#h = pi/a
#l =-h
def initial_val(discr):
# the initial solution for the TE_10-like mode
def initial_Hz(x, el):
from math import cos, sin
if el in material_elements["vacuum"]:
return h*cos(h*x[0])
else:
return -l*sin(h*d)/sin(l*(a-d))*cos(l*(a-x[0]))
from grudge.tools import make_obj_array
result_zero = discr.volume_zeros(kind="numpy", dtype=numpy.float64)
H_z = make_tdep_given(initial_Hz).volume_interpolant(0, discr)
return make_obj_array([result_zero, result_zero, H_z])
if rcon.is_head_rank:
from grudge.mesh.reader.gmsh import generate_gmsh
mesh = generate_gmsh(CAVITY_GEOMETRY, 2, force_dimension=2)
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
# Work out which elements belong to each material
material_elements = {}
for key in list(materials.keys()):
material_elements[key] = set(mesh_data.tag_to_elements[key])
order = 3
#extra_discr_args.setdefault("debug", []).append("cuda_no_plan")
#extra_discr_args.setdefault("debug", []).append("dump_optemplate_stages")
from grudge.data import make_tdep_given
from grudge.mesh import BTAG_ALL
op = TEMaxwellOperator(epsilon=make_tdep_given(eps_val), mu=make_tdep_given(mu_val), \
flux_type=flux_type_arg, \
bdry_flux_type=bdry_flux_type_arg, dimensions=2, pec_tag=BTAG_ALL)
# op = TEMaxwellOperator(epsilon=epsilon0, mu=mu0,
# flux_type=flux_type_arg, \
# bdry_flux_type=bdry_flux_type_arg, dimensions=2, pec_tag=BTAG_ALL)
discr = rcon.make_discretization(mesh_data, order=order,
tune_for=op.sym_operator(),
**extra_discr_args)
# create the initial solution
fields = initial_val(discr)
from grudge.visualization import VtkVisualizer
if write_output:
from os.path import join
vis = VtkVisualizer(discr, rcon, join(output_dir, "cav-%d" % order))
# monitor the solution at a point to find the resonant frequency
try:
point_getter = discr.get_point_evaluator(numpy.array([75e-3, 25e-3, 0])) #[0.25, 0.25, 0.25]))
except RuntimeError:
point_getter = None
if rcon.is_head_rank:
print("---------------------------------------------")
print("order %d" % order)
print("---------------------------------------------")
print("#elements=", len(mesh.elements))
from grudge.timestep.runge_kutta import LSRK4TimeStepper
stepper = LSRK4TimeStepper(dtype=discr.default_scalar_type, rcon=rcon)
#from grudge.timestep.dumka3 import Dumka3TimeStepper
#stepper = Dumka3TimeStepper(3, dtype=discr.default_scalar_type, rcon=rcon)
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
if write_output:
from os.path import join
log_file_name = join(output_dir, "cavity-%d.dat" % order)
else:
log_file_name = None
logmgr = LogManager(log_file_name, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
from pytools.log import IntervalTimer
vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
logmgr.add_quantity(vis_timer)
#from grudge.log import EMFieldGetter, add_em_quantities
#field_getter = EMFieldGetter(discr, op, lambda: fields)
#add_em_quantities(logmgr, op, field_getter)
logmgr.add_watches(
["step.max", "t_sim.max",
#("W_field", "W_el+W_mag"),
"t_step.max"]
)
# timestep loop -------------------------------------------------------
rhs = op.bind(discr)
final_time = 10e-9
if point_getter is not None:
from os.path import join
pointfile = open(join(output_dir, "point.txt"), "wt")
done_dt = False
try:
from grudge.timestep import times_and_steps
from os.path import join
step_it = times_and_steps(
final_time=final_time, logmgr=logmgr,
max_dt_getter=lambda t: op.estimate_timestep(discr,
stepper=stepper, t=t, fields=fields))
for step, t, dt in step_it:
if step % 10 == 0 and write_output:
sub_timer = vis_timer.start_sub_timer()
e, h = op.split_eh(fields)
visf = vis.make_file(join(output_dir, "cav-%d-%04d") % (order, step))
vis.add_data(visf,
[
("e",
discr.convert_volume(e, kind="numpy")),
("h",
discr.convert_volume(h, kind="numpy")),],
time=t, step=step
)
visf.close()
sub_timer.stop().submit()
fields = stepper(fields, t, dt, rhs)
if point_getter is not None:
val = point_getter(fields)
#print val
if not done_dt:
pointfile.write("#%g\n" % dt)
done_dt = True
pointfile.write("%g\n" %val[0])
finally:
if write_output:
vis.close()
logmgr.close()
discr.close()
if point_getter is not None:
pointfile.close()
# entry point -----------------------------------------------------------------
if __name__ == "__main__":
main(write_output=True)
/*
From Robert E. Collin, Field Theory of Guided Waves, p. 413, chaper 6
Find the cut-off frequency of a rectangular waveguide partially filled with a dielectric slab,
in order to find the resonant frequency of an inhomogeneous 2D cavity.
Take (5a), the transcendental equation for h and l, and substitute for their definitions in terms of gamma
Then solve for the condition that gamma is 0, for the mode with m=0.
t - width of dielectric section
d - width of air section
kappa - relative permittivity
k_0 - free space wavenumber
gamma - waveguide wavenumber
l - transverse wavenumber in dielectric
h - transverse wavenumber in air
*/
trans_eq : h*tan(l*t) + l*tan(h*d);
l_gamma : sqrt(gamma^2 - (m*pi/b)^2 + kappa*k_0^2);
h_gamma : sqrt(gamma^2 - (m*pi/b)^2 + k_0^2);
l_simp : l_gamma, gamma=0, m=0;
h_simp : h_gamma, gamma=0, m=0;
subst(h_gamma, h, trans_eq)$
subst(l_gamma, l, %)$
subst(0, m, %)$
trans_eq2 : subst(0, gamma, %);
c : 2.99792458e8$
plot2d([trans_eq2], [f,0.1e9,1.4e9], [y, -1000, 1000]), t = 50e-3, d=100e-3, kappa=2, k_0 = 2*%pi*f/c$
f_sol : find_root(trans_eq2, f, 0.8e9, 1e9), t = 50e-3, d = 100e-3, kappa = 2, k_0 = 2*%pi*f/c;
h_simp: float(2*%pi*f_sol/c);
sqrt(kappa)*2*%pi*f_sol/c, kappa=2$
l_simp: float(%);
%pi*a/(a-d-sqrt(kappa)), a=150e-3, d=100e-3, kappa=2;
float(%);
# Copyright (C) 2007 Andreas Kloeckner
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"Maxwell's equation example with fixed material coefficients"
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import numpy.linalg as la
def main(write_output=True):
from math import sqrt, pi, exp
from os.path import join
from grudge.backends import guess_run_context
rcon = guess_run_context()
epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
mu0 = 4*pi*1e-7 # N/A**2.
epsilon = 1*epsilon0
mu = 1*mu0
output_dir = "maxwell-2d"
import os
if not os.access(output_dir, os.F_OK):
os.makedirs(output_dir)
from grudge.mesh.generator import make_disk_mesh
mesh = make_disk_mesh(r=0.5, max_area=1e-3)
if rcon.is_head_rank:
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
class CurrentSource:
shape = (3,)
def __call__(self, x, el):
return [0,0,exp(-80*la.norm(x))]
order = 3
final_time = 1e-8
discr = rcon.make_discretization(mesh_data, order=order,
debug=["cuda_no_plan"])
from grudge.visualization import VtkVisualizer
if write_output:
vis = VtkVisualizer(discr, rcon, join(output_dir, "em-%d" % order))
if rcon.is_head_rank:
print("order %d" % order)
print("#elements=", len(mesh.elements))
from grudge.mesh import BTAG_ALL, BTAG_NONE
from grudge.models.em import TMMaxwellOperator
from grudge.data import make_tdep_given, TimeIntervalGivenFunction
op = TMMaxwellOperator(epsilon, mu, flux_type=1,
current=TimeIntervalGivenFunction(
make_tdep_given(CurrentSource()), off_time=final_time/10),
absorb_tag=BTAG_ALL, pec_tag=BTAG_NONE)
fields = op.assemble_eh(discr=discr)
from grudge.timestep import LSRK4TimeStepper
stepper = LSRK4TimeStepper()
from time import time
last_tstep = time()
t = 0
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
if write_output:
log_file_name = join(output_dir, "maxwell-%d.dat" % order)
else:
log_file_name = None
logmgr = LogManager(log_file_name, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
from pytools.log import IntervalTimer
vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
logmgr.add_quantity(vis_timer)
from grudge.log import EMFieldGetter, add_em_quantities
field_getter = EMFieldGetter(discr, op, lambda: fields)
add_em_quantities(logmgr, op, field_getter)
logmgr.add_watches(["step.max", "t_sim.max",
("W_field", "W_el+W_mag"), "t_step.max"])
# timestep loop -------------------------------------------------------
rhs = op.bind(discr)
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=final_time, logmgr=logmgr,
max_dt_getter=lambda t: op.estimate_timestep(discr,
stepper=stepper, t=t, fields=fields))
for step, t, dt in step_it:
if step % 10 == 0 and write_output:
e, h = op.split_eh(fields)
visf = vis.make_file(join(output_dir, "em-%d-%04d" % (order, step)))
vis.add_data(visf,
[
("e", discr.convert_volume(e, "numpy")),
("h", discr.convert_volume(h, "numpy")),
],
time=t, step=step
)
visf.close()
fields = stepper(fields, t, dt, rhs)
assert discr.norm(fields) < 0.03
finally:
if write_output:
vis.close()
logmgr.close()
discr.close()
if __name__ == "__main__":
import cProfile as profile
#profile.run("main()", "wave2d.prof")
main()
# entry points for py.test ----------------------------------------------------
from pytools.test import mark_test
@mark_test.long
def test_maxwell_2d():
main(write_output=False)
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
__copyright__ = "Copyright (C) 2007 Andreas Kloeckner"
__license__ = """
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 conditions:
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.
"""
import numpy as np
def make_mesh(a, b, pml_width=0.25, **kwargs):
from meshpy.geometry import GeometryBuilder, make_circle
geob = GeometryBuilder()
circle_centers = [(-1.5, 0), (1.5, 0)]
for cent in circle_centers:
geob.add_geometry(*make_circle(1, cent))
geob.wrap_in_box(1)
geob.wrap_in_box(pml_width)
mesh_mod = geob.mesher_module()
mi = mesh_mod.MeshInfo()
geob.set(mi)
mi.set_holes(circle_centers)
built_mi = mesh_mod.build(mi, **kwargs)
def boundary_tagger(fvi, el, fn, points):
return []
from grudge.mesh import make_conformal_mesh_ext
from grudge.mesh.element import Triangle
pts = np.asarray(built_mi.points, dtype=np.float64)
return make_conformal_mesh_ext(
pts,
[Triangle(i, el, pts)
for i, el in enumerate(built_mi.elements)],
boundary_tagger)
def main(write_output=True):
from grudge.timestep.runge_kutta import LSRK4TimeStepper
from math import sqrt, pi, exp
from grudge.backends import guess_run_context
rcon = guess_run_context()
epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
mu0 = 4*pi*1e-7 # N/A**2.
epsilon = 1*epsilon0
mu = 1*mu0
c = 1/sqrt(mu*epsilon)
pml_width = 0.5
#mesh = make_mesh(a=np.array((-1,-1,-1)), b=np.array((1,1,1)),
#mesh = make_mesh(a=np.array((-3,-3)), b=np.array((3,3)),
mesh = make_mesh(a=np.array((-1,-1)), b=np.array((1,1)),
#mesh = make_mesh(a=np.array((-2,-2)), b=np.array((2,2)),
pml_width=pml_width, max_volume=0.01)
if rcon.is_head_rank:
mesh_data = rcon.distribute_mesh(mesh)
else:
mesh_data = rcon.receive_mesh()
class Current:
def volume_interpolant(self, t, discr):
from grudge.tools import make_obj_array
result = discr.volume_zeros(kind="numpy", dtype=np.float64)
omega = 6*c
if omega*t > 2*pi:
return make_obj_array([result, result, result])
x = make_obj_array(discr.nodes.T)
r = np.sqrt(np.dot(x, x))
idx = r<0.3
result[idx] = (1+np.cos(pi*r/0.3))[idx] \
*np.sin(omega*t)**3
result = discr.convert_volume(result, kind=discr.compute_kind,
dtype=discr.default_scalar_type)
return make_obj_array([-result, result, result])
order = 3
discr = rcon.make_discretization(mesh_data, order=order,
debug=["cuda_no_plan"])
from grudge.visualization import VtkVisualizer
if write_output:
vis = VtkVisualizer(discr, rcon, "em-%d" % order)
from grudge.mesh import BTAG_ALL, BTAG_NONE
from grudge.data import GivenFunction, TimeHarmonicGivenFunction, TimeIntervalGivenFunction
from grudge.models.em import MaxwellOperator
from grudge.models.pml import \
AbarbanelGottliebPMLMaxwellOperator, \
AbarbanelGottliebPMLTMMaxwellOperator, \
AbarbanelGottliebPMLTEMaxwellOperator
op = AbarbanelGottliebPMLTEMaxwellOperator(epsilon, mu, flux_type=1,
current=Current(),
pec_tag=BTAG_ALL,
absorb_tag=BTAG_NONE,
add_decay=True
)
fields = op.assemble_ehpq(discr=discr)
stepper = LSRK4TimeStepper()
if rcon.is_head_rank:
print("order %d" % order)
print("#elements=", len(mesh.elements))
# diagnostics setup ---------------------------------------------------
from pytools.log import LogManager, add_general_quantities, \
add_simulation_quantities, add_run_info
if write_output:
log_file_name = "maxwell-%d.dat" % order
else:
log_file_name = None
logmgr = LogManager(log_file_name, "w", rcon.communicator)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
from pytools.log import IntervalTimer
vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
logmgr.add_quantity(vis_timer)
from grudge.log import EMFieldGetter, add_em_quantities
field_getter = EMFieldGetter(discr, op, lambda: fields)
add_em_quantities(logmgr, op, field_getter)
logmgr.add_watches(["step.max", "t_sim.max", ("W_field", "W_el+W_mag"), "t_step.max"])
from grudge.log import LpNorm
class FieldIdxGetter:
def __init__(self, whole_getter, idx):
self.whole_getter = whole_getter
self.idx = idx
def __call__(self):
return self.whole_getter()[self.idx]
# timestep loop -------------------------------------------------------
t = 0
pml_coeff = op.coefficients_from_width(discr, width=pml_width)
rhs = op.bind(discr, pml_coeff)
try:
from grudge.timestep import times_and_steps
step_it = times_and_steps(
final_time=4/c, logmgr=logmgr,
max_dt_getter=lambda t: op.estimate_timestep(discr,
stepper=stepper, t=t, fields=fields))
for step, t, dt in step_it:
if step % 10 == 0 and write_output:
e, h, p, q = op.split_ehpq(fields)
visf = vis.make_file("em-%d-%04d" % (order, step))
#pml_rhs_e, pml_rhs_h, pml_rhs_p, pml_rhs_q = \
#op.split_ehpq(rhs(t, fields))
j = Current().volume_interpolant(t, discr)
vis.add_data(visf, [
("e", discr.convert_volume(e, "numpy")),
("h", discr.convert_volume(h, "numpy")),
("p", discr.convert_volume(p, "numpy")),
("q", discr.convert_volume(q, "numpy")),
("j", discr.convert_volume(j, "numpy")),
#("pml_rhs_e", pml_rhs_e),
#("pml_rhs_h", pml_rhs_h),
#("pml_rhs_p", pml_rhs_p),
#("pml_rhs_q", pml_rhs_q),
#("max_rhs_e", max_rhs_e),
#("max_rhs_h", max_rhs_h),
#("max_rhs_p", max_rhs_p),
#("max_rhs_q", max_rhs_q),
],
time=t, step=step)
visf.close()
fields = stepper(fields, t, dt, rhs)
_, _, energies_data = logmgr.get_expr_dataset("W_el+W_mag")
energies = [value for tick_nbr, value in energies_data]
assert energies[-1] < max(energies) * 1e-2
finally:
logmgr.close()
if write_output:
vis.close()
if __name__ == "__main__":
main()
# entry points for py.test ----------------------------------------------------
from pytools.test import mark_test
@mark_test.long
def test_maxwell_pml():
main(write_output=False)