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 285 additions and 3809 deletions
# grudge - the Hybrid'n'Easy DG Environment
# 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
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()
# a second mesh to regrid to
if rcon.is_head_rank:
from grudge.mesh.generator import \
make_rect_mesh, \
make_centered_regular_rect_mesh
refine = 4
mesh2 = make_centered_regular_rect_mesh((0,-5), (10,5), n=(8,8),
post_refine_factor=refine)
mesh_data2 = rcon.distribute_mesh(mesh2)
else:
mesh_data2 = rcon.receive_mesh()
for order in [3,4]:
discr = rcon.make_discretization(mesh_data, order=order,
default_scalar_type=numpy.float64,
quad_min_degrees={
"gasdyn_vol": 3*order,
"gasdyn_face": 3*order,
})
discr2 = rcon.make_discretization(mesh_data2, order=order,
default_scalar_type=numpy.float64,
quad_min_degrees={
"gasdyn_vol": 3*order,
"gasdyn_face": 3*order,
})
from grudge.visualization import SiloVisualizer, VtkVisualizer
vis = VtkVisualizer(discr, rcon, "vortex-%d" % order)
#vis = SiloVisualizer(discr, rcon)
from gas_dynamics_initials import Vortex
vortex = Vortex()
fields = vortex.volume_interpolant(0, discr)
from grudge.models.gas_dynamics import GasDynamicsOperator
from grudge.mesh import BTAG_ALL
op = GasDynamicsOperator(dimensions=2, gamma=vortex.gamma, mu=vortex.mu,
prandtl=vortex.prandtl, spec_gas_const=vortex.spec_gas_const,
bc_inflow=vortex, bc_outflow=vortex, bc_noslip=vortex,
inflow_tag=BTAG_ALL, source=None)
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 for mesh 1 =", len(mesh.elements))
print("#elements for mesh 2 =", len(mesh2.elements))
# limiter ------------------------------------------------------------
from grudge.models.gas_dynamics import SlopeLimiter1NEuler
limiter = SlopeLimiter1NEuler(discr, vortex.gamma, 2, op)
from grudge.timestep import SSPRK3TimeStepper
#stepper = SSPRK3TimeStepper(limiter=limiter)
stepper = SSPRK3TimeStepper()
#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 = 0.2
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]))
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)
#regrid to discr2 at some arbitrary time
if step == 21:
#get interpolated fields
fields = discr.get_regrid_values(fields, discr2, dtype=None, use_btree=True, thresh=1e-8)
#get new stepper (old one has reference to discr
stepper = SSPRK3TimeStepper()
#new bind
euler_ex = op.bind(discr2)
#new rhs
max_eigval = [0]
def rhs(t, q):
ode_rhs, speed = euler_ex(t, q)
max_eigval[0] = speed
return ode_rhs
rhs(t+dt, fields)
#add logmanager
#discr2.add_instrumentation(logmgr)
#new step_it
step_it = times_and_steps(
final_time=final_time, logmgr=logmgr,
max_dt_getter=lambda t: op.estimate_timestep(discr2,
stepper=stepper, t=t, max_eigenvalue=max_eigval[0]))
#new visualization
vis.close()
vis = VtkVisualizer(discr2, rcon, "vortexNewGrid-%d" % order)
discr=discr2
assert not numpy.isnan(numpy.sum(fields[0]))
true_fields = vortex.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()
# grudge - the Hybrid'n'Easy DG Environment
# 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)
# grudge - the Hybrid'n'Easy DG Environment
# 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)
# grudge - the Hybrid'n'Easy DG Environment
# 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)
# grudge - the Hybrid'n'Easy DG Environment
# 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)
# grudge - the Hybrid'n'Easy DG Environment
# 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()
# grudge - the Hybrid'n'Easy DG Environment
# 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()
# grudge - the Hybrid'n'Easy DG Environment
# 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()
# grudge - the Hybrid'n'Easy DG Environment
# 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
# grudge - the Hybrid'n'Easy DG Environment
# 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
# grudge - the Hybrid'n'Easy DG Environment
# 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
from grudge.tools import \
cyl_bessel_j, \
cyl_bessel_j_prime
from math import sqrt, pi, sin, cos, atan2, acos
import numpy
import numpy.linalg as la
import cmath
from six.moves import range
from six.moves import zip
# solution adapters -----------------------------------------------------------
class RealPartAdapter:
def __init__(self, adaptee):
self.adaptee = adaptee
@property
def shape(self):
return self.adaptee.shape
def __call__(self, x, el):
return [xi.real for xi in self.adaptee(x, el)]
class SplitComplexAdapter:
def __init__(self, adaptee):
self.adaptee = adaptee
@property
def shape(self):
(n,) = self.adaptee.shape
return (n*2,)
def __call__(self, x, el):
ad_x = self.adaptee(x, el)
return [xi.real for xi in ad_x] + [xi.imag for xi in ad_x]
class CylindricalFieldAdapter:
"""Turns a field returned as (r, phi, z) components into
(x,y,z) components.
"""
def __init__(self, adaptee):
self.adaptee = adaptee
@property
def shape(self):
return self.adaptee.shape
def __call__(self, x, el):
xy = x[:2]
r = la.norm(xy)
phi = atan2(x[1], x[0])
prev_result = self.adaptee(x, el)
result = []
i = 0
while i < len(prev_result):
fr, fphi, fz = prev_result[i:i+3]
result.extend([
cos(phi)*fr - sin(phi)*fphi, # ex
sin(phi)*fr + cos(phi)*fphi, # ey
fz,
])
i += 3
return result
class SphericalFieldAdapter:
"""Turns the adaptee's field C{(Er, Etheta, Ephi)} components into
C{(Ex,Ey,Ez)} components.
Conventions are those of
http://mathworld.wolfram.com/SphericalCoordinates.html
"""
def __init__(self, adaptee):
self.adaptee = adaptee
@property
def shape(self):
return self.adaptee.shape
@staticmethod
def r_theta_phi_from_xyz(x):
r = la.norm(x)
return r, atan2(x[1], x[0]), acos(x[2]/r)
def __call__(self, x, el):
r, theta, phi = self.r_theta_phi_from_xyz(x)
er = numpy.array([
cos(theta)*sin(phi),
sin(theta)*sin(phi),
cos(phi)])
etheta = numpy.array([
-sin(theta),
cos(theta),
0])
ephi = numpy.array([
cos(theta)*cos(phi),
sin(theta)*cos(phi),
-sin(phi)])
adaptee_result = self.adaptee(x, el)
result = numpy.empty_like(adaptee_result)
i = 0
while i < len(adaptee_result):
fr, ftheta, fphi = adaptee_result[i:i+3]
result[i:i+3] = fr*er + ftheta*etheta + fphi*ephi
i += 3
return result
# actual solutions ------------------------------------------------------------
class CylindricalCavityMode:
"""A cylindrical TM cavity mode.
Taken from:
J.D. Jackson, Classical Electrodynamics, Wiley.
3rd edition, 2001.
ch 8.7, p. 368f.
"""
def __init__(self, m, n, p, radius, height, epsilon, mu):
try:
from bessel_zeros import bessel_zeros
except ImportError:
print("*** You need to generate the bessel root data file.")
print("*** Execute generate-bessel-zeros.py at the command line.")
raise
assert m >= 0 and m == int(m)
assert n >= 1 and n == int(n)
assert p >= 0 and p == int(p)
self.m = m
self.n = n
self.p = p
self.phi_sign = 1
R = self.radius = radius
d = self.height = height
self.epsilon = epsilon
self.mu = mu
self.t = 0
x_mn = bessel_zeros[m][n-1]
self.omega = 1 / sqrt(mu*epsilon) * sqrt(
x_mn**2 / R**2
+ p**2 * pi**2 / d**2)
self.gamma_mn = x_mn/R
def set_time(self, t):
self.t = t
shape = (6,)
def __call__(self, x, el):
# coordinates -----------------------------------------------------
xy = x[:2]
r = sqrt(xy*xy)
phi = atan2(x[1], x[0])
z = x[2]
# copy instance variables for easier access -----------------------
m = self.m
p = self.p
gamma = self.gamma_mn
phi_sign = self.phi_sign
omega = self.omega
d = self.height
epsilon = self.epsilon
# common subexpressions -------------------------------------------
tdep = cmath.exp(-1j * omega * self.t)
phi_factor = cmath.exp(phi_sign * 1j * m * phi)
# psi and derivatives ---------------------------------------------
psi = cyl_bessel_j(m, gamma * r) * phi_factor
psi_dr = gamma*cyl_bessel_j_prime(m, gamma*r) * phi_factor
psi_dphi = (cyl_bessel_j(m, gamma * r)
* 1/r * phi_sign*1j*m * phi_factor)
# field components in polar coordinates ---------------------------
ez = tdep * cos(p * pi * z / d) * psi
e_transverse_factor = (tdep
* (-p*pi/(d*gamma**2))
* sin(p * pi * z / d))
er = e_transverse_factor * psi_dr
ephi = e_transverse_factor * psi_dphi
hz = 0j
# z x grad psi = z x (psi_x, psi_y) = (-psi_y, psi_x)
# z x grad psi = z x (psi_r, psi_phi) = (-psi_phi, psi_r)
h_transverse_factor = (tdep
* 1j*epsilon*omega/gamma**2
* cos(p * pi * z / d))
hr = h_transverse_factor * (-psi_dphi)
hphi = h_transverse_factor * psi_dr
return [er, ephi, ez, hr, hphi, hz]
class RectangularWaveguideMode:
"""A rectangular TM cavity mode."""
def __init__(self, epsilon, mu, mode_indices,
dimensions=(1,1,1), coefficients=(1,0,0),
forward_coeff=1, backward_coeff=0):
for n in mode_indices:
assert n >= 0 and n == int(n)
self.mode_indices = mode_indices
self.dimensions = dimensions
self.coefficients = coefficients
self.forward_coeff = forward_coeff
self.backward_coeff = backward_coeff
self.epsilon = epsilon
self.mu = mu
self.t = 0
self.factors = [n*pi/a for n, a in zip(self.mode_indices, self.dimensions)]
c = 1/sqrt(mu*epsilon)
self.k = sqrt(sum(f**2 for f in self.factors))
self.omega = self.k*c
def set_time(self, t):
self.t = t
def __call__(self, discr):
f,g,h = self.factors
omega = self.omega
k = self.k
x = discr.nodes[:,0]
y = discr.nodes[:,1]
if discr.dimensions > 2:
z = discr.nodes[:,2]
else:
z = discr.volume_zeros()
sx = numpy.sin(f*x)
sy = numpy.sin(g*y)
cx = numpy.cos(f*x)
cy = numpy.cos(g*y)
zdep_add = numpy.exp(1j*h*z)+numpy.exp(-1j*h*z)
zdep_sub = numpy.exp(1j*h*z)-numpy.exp(-1j*h*z)
tdep = numpy.exp(-1j * omega * self.t)
C = 1j/(f**2+g**2)
result = numpy.zeros((6,len(x)), dtype=zdep_add.dtype)
result[0] = C*f*h*cx*sy*zdep_sub*tdep # ex
result[1] = C*g*h*sx*cy*zdep_sub*tdep # ey
result[2] = sx*sy*zdep_add*tdep # ez
result[3] = -C*g*self.epsilon*omega*sx*cy*zdep_add*tdep # hx
result[4] = C*f*self.epsilon*omega*cx*sy*zdep_add*tdep
return result
class RectangularCavityMode(RectangularWaveguideMode):
"""A rectangular TM cavity mode."""
def __init__(self, *args, **kwargs):
if "scale" in kwargs:
kwargs["forward_coeff"] = scale
kwargs["backward_coeff"] = scale
else:
kwargs["forward_coeff"] = 1
kwargs["backward_coeff"] = 1
RectangularWaveguideMode.__init__(self, *args, **kwargs)
# dipole solution -------------------------------------------------------------
class DipoleFarField:
"""Dipole EM field.
See U{http://piki.tiker.net/wiki/Dipole}.
"""
def __init__(self, q, d, omega, epsilon, mu):
self.q = q
self.d = d
self.epsilon = epsilon
self.mu = mu
self.c = c = 1/sqrt(mu*epsilon)
self.omega = omega
freq = omega/(2*pi)
self.wavelength = c/freq
def far_field_mask(self, x, el):
if la.norm(x) > 0.5*self.wavelength:
return 1
else:
return 0
def set_time(self, t):
self.t = t
shape = (6,)
def __call__(self, x, el):
# coordinates ---------------------------------------------------------
r, theta, phi = SphericalFieldAdapter.r_theta_phi_from_xyz(x)
# copy instance variables for easier access ---------------------------
q = self.q
d = self.d
epsilon = self.epsilon
mu = self.mu
c = self.c
omega = self.omega
t = self.t
# field components ----------------------------------------------------
tdep = omega*t-omega*r/c
e_r = (2*cos(phi)*q*d) / (4*pi*epsilon) * (
1/r**3 * sin(tdep)
+omega/(c*r**2) * cos(tdep))
e_phi = (sin(phi)*q*d) / (4*pi*epsilon) * (
(1/r**3 - omega**2/(c**2*r)*sin(tdep))
+ omega/(c*r**2)*cos(tdep))
h_theta = (omega*sin(phi)*q*d)/(4*pi) * (
- omega/(c*r)*sin(tdep)
+ 1/r**2*cos(tdep))
return [e_r, 0, e_phi, 0, h_theta, 0]
def source_modulation(self, t):
j0 = self.q*self.d*self.omega/self.epsilon
return j0*cos(self.omega*t)
# analytic solution tools -----------------------------------------------------
def check_time_harmonic_solution(discr, mode, c_sol):
from grudge.discretization import bind_nabla, bind_mass_matrix
from grudge.visualization import SiloVisualizer
from grudge.silo import SiloFile
from grudge.tools import dot, cross
from grudge.silo import DB_VARTYPE_VECTOR
def curl(field):
return cross(nabla, field)
vis = SiloVisualizer(discr)
nabla = bind_nabla(discr)
mass = bind_mass_matrix(discr)
def rel_l2_error(err, base):
def l2_norm(field):
return sqrt(dot(field, mass*field))
base_l2 = l2_norm(base)
err_l2 = l2_norm(err)
if base_l2 == 0:
if err_l2 == 0:
return 0.
else:
return float("inf")
else:
return err_l2/base_l2
dt = 0.1
for step in range(10):
t = step*dt
mode.set_time(t)
fields = discr.interpolate_volume_function(c_sol)
er = fields[0:3]
hr = fields[3:6]
ei = fields[6:9]
hi = fields[9:12]
silo = SiloFile("em-complex-%04d.silo" % step)
vis.add_to_silo(silo,
vectors=[
("curl_er", curl(er)),
("om_hi", -mode.mu*mode.omega*hi),
("curl_hr", curl(hr)),
("om_ei", mode.epsilon*mode.omega*hi),
],
expressions=[
("diff_er", "curl_er-om_hi", DB_VARTYPE_VECTOR),
("diff_hr", "curl_hr-om_ei", DB_VARTYPE_VECTOR),
],
write_coarse_mesh=True,
time=t, step=step
)
er_res = curl(er) + mode.mu *mode.omega*hi
ei_res = curl(ei) - mode.mu *mode.omega*hr
hr_res = curl(hr) - mode.epsilon*mode.omega*ei
hi_res = curl(hi) + mode.epsilon*mode.omega*er
print("time=%f, rel l2 residual in Re[E]=%g\tIm[E]=%g\tRe[H]=%g\tIm[H]=%g" % (
t,
rel_l2_error(er_res, er),
rel_l2_error(ei_res, ei),
rel_l2_error(hr_res, hr),
rel_l2_error(hi_res, hi),
))
# grudge - the Hybrid'n'Easy DG Environment
# 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 as np
__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
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 logging
logger = logging.getLogger(__name__)
import numpy as np
def main(write_output=True, allow_features=None, flux_type_arg=1,
bdry_flux_type_arg=None, extra_discr_args={}):
from grudge.mesh.generator import make_cylinder_mesh, make_box_mesh
from grudge.tools import EOCRecorder, to_obj_array
from math import sqrt, pi # noqa
from analytic_solutions import ( # noqa
RealPartAdapter,
SplitComplexAdapter,
CylindricalFieldAdapter,
CylindricalCavityMode,
RectangularWaveguideMode,
RectangularCavityMode)
from grudge.models.em import MaxwellOperator
import pyopencl as cl
import pyopencl.tools as cl_tools
logging.basicConfig(level=logging.DEBUG)
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
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
logger = logging.getLogger(__name__)
eoc_rec = EOCRecorder()
cylindrical = False
periodic = False
def main(ctx_factory, dim=3, order=4, visualize=False):
cl_ctx = ctx_factory()
queue = cl.CommandQueue(cl_ctx)
if cylindrical:
R = 1
d = 2
mode = CylindricalCavityMode(m=1, n=1, p=1,
radius=R, height=d,
epsilon=epsilon, mu=mu)
# r_sol = CylindricalFieldAdapter(RealPartAdapter(mode))
# c_sol = SplitComplexAdapter(CylindricalFieldAdapter(mode))
allocator = cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue))
actx = PyOpenCLArrayContext(queue, allocator=allocator)
if rcon.is_head_rank:
mesh = make_cylinder_mesh(radius=R, height=d, max_volume=0.01)
else:
if periodic:
mode = RectangularWaveguideMode(epsilon, mu, (3, 2, 1))
periodicity = (False, False, True)
else:
periodicity = None
mode = RectangularCavityMode(epsilon, mu, (1, 2, 2))
from meshmode.mesh.generation import generate_regular_rect_mesh
mesh = generate_regular_rect_mesh(
a=(0.0,)*dim,
b=(1.0,)*dim,
nelements_per_axis=(4,)*dim)
if rcon.is_head_rank:
mesh = make_box_mesh(max_volume=0.001, periodicity=periodicity)
dcoll = make_discretization_collection(actx, mesh, order=order)
if rcon.is_head_rank:
mesh_data = rcon.distribute_mesh(mesh)
if 0:
epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
mu0 = 4*np.pi*1e-7 # N/A**2.
epsilon = 1*epsilon0
mu = 1*mu0
else:
mesh_data = rcon.receive_mesh()
for order in [4, 5, 6]:
#for order in [1,2,3,4,5,6]:
extra_discr_args.setdefault("debug", []).extend([
"cuda_no_plan", "cuda_dump_kernels"])
op = MaxwellOperator(epsilon, mu,
flux_type=flux_type_arg,
bdry_flux_type=bdry_flux_type_arg)
epsilon = 1
mu = 1
discr = rcon.make_discretization(mesh_data, order=order,
tune_for=op.sym_operator(),
**extra_discr_args)
from grudge.visualization import VtkVisualizer
if write_output:
vis = VtkVisualizer(discr, rcon, "em-%d" % order)
mode.set_time(0)
def get_true_field():
return discr.convert_volume(
to_obj_array(mode(discr)
.real.astype(discr.default_scalar_type).copy()),
kind=discr.compute_kind)
fields = get_true_field()
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
from grudge.models.em import MaxwellOperator
if write_output:
log_file_name = "maxwell-%d.dat" % order
maxwell_operator = MaxwellOperator(
dcoll,
epsilon,
mu,
flux_type=0.5,
dimensions=dim
)
def cavity_mode(x, t=0):
if dim == 3:
return get_rectangular_cavity_mode(actx, x, t, 1, (1, 2, 2))
else:
log_file_name = None
return get_rectangular_cavity_mode(actx, x, t, 1, (2, 3))
logmgr = LogManager(log_file_name, "w", rcon.communicator)
fields = cavity_mode(actx.thaw(dcoll.nodes()), t=0)
add_run_info(logmgr)
add_general_quantities(logmgr)
add_simulation_quantities(logmgr)
discr.add_instrumentation(logmgr)
stepper.add_instrumentation(logmgr)
maxwell_operator.check_bc_coverage(mesh)
from pytools.log import IntervalTimer
vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
logmgr.add_quantity(vis_timer)
def rhs(t, w):
return maxwell_operator.operator(t, w)
from grudge.log import EMFieldGetter, add_em_quantities
field_getter = EMFieldGetter(discr, op, lambda: fields)
add_em_quantities(logmgr, op, field_getter)
dt = actx.to_numpy(
maxwell_operator.estimate_rk4_timestep(actx, dcoll, fields=fields))
logmgr.add_watches(
["step.max", "t_sim.max",
("W_field", "W_el+W_mag"),
"t_step.max"]
)
dt_stepper = set_up_rk4("w", dt, fields, rhs)
# }}}
target_steps = 60
final_t = dt * target_steps
nsteps = int(final_t/dt) + 1
# {{{ timestep loop
logger.info("dt = %g nsteps = %d", dt, nsteps)
rhs = op.bind(discr)
final_time = 0.5e-9
from grudge.shortcuts import make_visualizer
vis = make_visualizer(dcoll)
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))
step = 0
for step, t, dt in step_it:
if step % 50 == 0 and write_output:
sub_timer = vis_timer.start_sub_timer()
e, h = op.split_eh(fields)
visf = vis.make_file("em-%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()
def norm(u):
return op.norm(dcoll, u, 2)
fields = stepper(fields, t, dt, rhs)
e, h = maxwell_operator.split_eh(fields)
mode.set_time(final_time)
if visualize:
vis.write_vtk_file(
f"fld-cavities-{step:04d}.vtu",
[
("e", e),
("h", h),
]
)
eoc_rec.add_data_point(order,
discr.norm(fields-get_true_field()))
for event in dt_stepper.run(t_end=final_t):
if isinstance(event, dt_stepper.StateComputed):
assert event.component_id == "w"
finally:
if write_output:
vis.close()
step += 1
e, h = maxwell_operator.split_eh(event.state_component)
logmgr.close()
discr.close()
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]))
if rcon.is_head_rank:
print()
print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
logger.info(
"[%04d] t = %.5f |e0| = %.5e, |e1| = %.5e, |h0| = %.5e, |h1| = %.5e",
step, event.t,
norm_e0, norm_e1, norm_h0, norm_h1
)
# }}}
if step % 10 == 0:
if visualize:
vis.write_vtk_file(
f"fld-cavities-{step:04d}.vtu",
[
("e", e),
("h", h),
]
)
assert eoc_rec.estimate_order_of_convergence()[0, 1] > 6
# 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
# {{{ entry points for py.test
from pytools.test import mark_test
@mark_test.long
def test_maxwell_cavities():
main(write_output=False)
@mark_test.long
def test_maxwell_cavities_lf():
main(write_output=False, flux_type_arg="lf", bdry_flux_type_arg=1)
@mark_test.mpi
@mark_test.long
def test_maxwell_cavities_mpi():
from pytools.mpi import run_with_mpi_ranks
run_with_mpi_ranks(__file__, 2, main,
write_output=False, allow_features=["mpi"])
def test_cuda():
try:
from pycuda.tools import mark_cuda_test
except ImportError:
pass
yield "SP CUDA Maxwell", mark_cuda_test(
do_test_maxwell_cavities_cuda), np.float32
yield "DP CUDA Maxwell", mark_cuda_test(
do_test_maxwell_cavities_cuda), np.float64
def do_test_maxwell_cavities_cuda(dtype):
import pytest # noqa
main(write_output=False, allow_features=["cuda"],
extra_discr_args=dict(
debug=["cuda_no_plan"],
default_scalar_type=dtype,
))
# }}}
# entry point -----------------------------------------------------------------
if __name__ == "__main__":
from pytools.mpi import in_mpi_relaunch
if in_mpi_relaunch():
test_maxwell_cavities_mpi()
else:
do_test_maxwell_cavities_cuda(np.float32)
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)
# grudge - the Hybrid'n'Easy DG Environment
# 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()
# grudge - the Hybrid'n'Easy DG Environment
# 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(%);
# grudge - the Hybrid'n'Easy DG Environment
# 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)