In this tutorial the implementation of the so called exact Dirichlet-to-Neumann map for scattering problems on a disc is shown.
The equation solved is the 2D Helmholtz equation
on the disc
of radius
, where the coefficient functions
and
depend on whether the TE or TM mode is considered. In the TE-case, i.e.
corresponds to the third component of the magnetic field,
and
are given by
with the permittivity
, the permeability
and the frequency
. On the other hand, in the TM-case, i.e.
corresponds to the third component of the electric field,
and
are given by
In the exterior domain
the total field
can be split into a known incoming field
and an unknown scattered field
, i.e.
The coefficient functions
and
are assumed to be constant in
. We will denote the constant values of the coefficient functions by
and
and they are defined according to the definitions above
and
where
denotes the the vacuum permittivity and
denotes the vacuum permeability.
Finally we assume the total field
and its co-normal derivative
to be continuous over
.
We will suppose for now that there exists an appropriate Dirichlet-to-Neumann (DtN) mapping
for the scattered field
on the boundary
, i.e.
In the tutorial BGT_0.cc this mapping was given by the BGT-0 boundary condition
. In the section Dirichlet-to-Neumann Mapping we will see how to construct such a linear DtN mapping that can be regarded as "exact".
Now we derive the corresponding variational formulation of the introduced problem: find
such that
Considering its continuity over
the co-normal of the total field
can be written in the form
Using the fact that the total field in the exterior domain can be split into an incoming field and a scattered field, and incorporating the DtN mapping
gives
Now we use the linearity of the DtN mapping
and the continuity of the total field
over
to obtain
Incorporating this into the variational formulation yields
In this section we want derive the exact DtN mapping for scattering problems on a disc. Since the total field
and the incoming field
are supposed to satisfy the Helmholtz equation in the exterior domain
, the scattered field
also satisfies the Helmholtz equation in
, i.e. we have
with the wave number
for both, the TE-case and the TM-case.
Rewriting this equation in polar coordinates
gives
Using the approach
, that is based on the Fourier expansion in
, we get
i.e.
Since
,
, are mutually linear independent, we have
and thus
By substituting
we arrive at the Bessel differential equation
The solutions to this equation define the Bessel functions
of the first kind and the Bessel functions
of the second kind.
As the Bessel differential equation is linear, any linear combination of the Bessel functions
and
solve the equation. In particular, we can choose the Hankel functions
. In the limit
the Hankel functions behave like
[1] and thus, they satisfy the Sommerfeld radiation condition
c.f. [2].
Taking the Hankel functions
as basis for
we can write the scattered field in the form
and its derivative
In particular, we have
and by multiplying with
,
and integrating over
we arrive at
where we used the orthogonality of
,
. Thus, we obtain
and
can written as
This is our sought DtN mapping which we can incorporate into the boundary integral
First, system files
#include <iostream>
#include "basics.hh" #include "formula.hh" #include "function.hh" #include "geometry.hh" #include "graphics.hh" #include "hp1D.hh" #include "hp2D.hh" #include "operator.hh" #include "space.hh" #include "toolbox.hh"
using concepts::Real; using concepts::Cmplx;
concepts:: to Real and Cmplx everytime.
Before starting with the main program we define a class integrandF derived from concepts::Formula in order to evaluate
at
for different values of
. Since
is usually very small (in the implementation below we have
) we evaluate the value of
by simply computing
class integrandF : public concepts::Formula<Cmplx> { public: // ** Constructor ** integrandF(const int n = 0) : n_(n) {} // ** () opterator ** virtual Cmplx operator() (const Real p, const Real t = 0.0) const; virtual Cmplx operator() (const concepts::Real2d& p, const Real t = 0.0) const; virtual Cmplx operator() (const concepts::Real3d& p, const Real t = 0.0) const; // ** clone ** virtual integrandF* clone() const { return new integrandF(n_); } protected: virtual std::ostream& info(std::ostream& os) const; private: // ** exponent of the function ** const int n_; }; Cmplx integrandF::operator() (const Real p, const Real t) const { std::stringstream error; error << "integrandF::operator() not defined for input parameter of type Real"; throw concepts::MissingFeature(error.str()); return 0.0; } Cmplx integrandF::operator() (const concepts::Real2d& p, const Real t) const { Real x = p[0]; Real y = p[1]; Real r = sqrt(x*x+y*y); Cmplx c(x/r,y/r); return pow(c,n_); } Cmplx integrandF::operator() (const concepts::Real3d& p, const Real t) const { const concepts::Real2d p2(p[0], p[1]); return (*this)(p2,t); } std::ostream& integrandF::info(std::ostream& os) const { return os << "integrandF(" << n_ << ")"; }
We start the main program
int main(int argc, char** argv) { try {
const Real R = 15.0; // outer radius const Real innerR = 1.0; // inner radius (i.e. radius of scatterer) const Real omega = 1.0; // frequency const Real eps0 = 1.0; // permittivity outside the scatterer const Real mu0 = 1.0; // permeability outside the scatterer const Cmplx epsSc (4.0, 0.0); // permittivity inside the scatterer const Real muSc = 1.0; // permeability inside the scatterer const int DtNmaxN = 5; // number DtN summands
const uint attrBoundary = 11; // boundary const uint attrSc = 21; // scatterer const uint attr0 = 22; // computational domain without scatterer
We specify whether the TE-case or the TM-case is computed.
enum TETM {TE, TM};
TETM mode = TM;
Then we declare
and
as PiecewiseConstFormula and
as ConstFormula.
concepts::PiecewiseConstFormula<Cmplx> alpha; concepts::PiecewiseConstFormula<Cmplx> beta; concepts::ConstFormula<Cmplx> alpha0;
We write the values of
,
and
according to the defined mode. If the TE-case was chosen we write
if (mode == TE) { alpha[concepts::Attribute(attrSc)] = Cmplx(pow(epsSc,-1.0)); alpha[concepts::Attribute(attr0)] = Cmplx(pow(eps0,-1.0),0.0); beta[concepts::Attribute(attrSc)] = Cmplx(omega*omega*muSc,0.0); beta[concepts::Attribute(attr0)] = Cmplx(omega*omega*mu0,0.0); alpha0 = Cmplx(pow(eps0,-1.0),0.0); }
if (mode == TM) { alpha[concepts::Attribute(attrSc)] = Cmplx(1.0, 0.0); alpha[concepts::Attribute(attr0)] = Cmplx(1.0, 0.0); beta[concepts::Attribute(attrSc)] = Cmplx(omega*omega*epsSc*muSc); beta[concepts::Attribute(attr0)] = Cmplx(omega*omega*eps0*mu0,0.0); alpha0 = Cmplx(1.0, 0.0); }
Now we compute the wave number
const Real k0 = omega*sqrt(mu0*eps0);
and its normal gradient
as ParsedFormula. std::stringstream uIncReal, uIncImag, uIncGradReal, uIncGradImag;
uIncReal << "(cos((" << k0 << ")*x))";
uIncImag << "(sin((" << k0 << ")*x))";
uIncGradReal << "(-(x/(sqrt(x*x+y*y)))*" << k0 << "*sin((" << k0 << ")*x))";
uIncGradImag << "( (x/(sqrt(x*x+y*y)))*" << k0 << "*cos((" << k0 << ")*x))";
concepts::ParsedFormula<Cmplx> uInc( uIncReal.str(), uIncImag.str());
concepts::ParsedFormula<Cmplx> uIncGrad(uIncGradReal.str(), uIncGradImag.str());
The mesh is created as a circle surrounded by a ring.
Real ringData[3] = {innerR,(innerR+R)/2.0,R}; uint quadAttrData[3] = {attrSc,attr0,attr0}; uint edgeAttrData[3] = {0,0,attrBoundary}; concepts::Array<Real> ring(ringData,3); concepts::Array<uint> ringQuadAttr(quadAttrData,3); concepts::Array<uint> ringEdgeAttr(edgeAttrData,3); concepts::Circle msh(ring,ringQuadAttr,ringEdgeAttr); std::cout << std::endl << "Mesh: " << msh << std::endl;
graphics::drawMeshEPS(msh,"exactDTN_mesh.eps",100,1.0,20);
The space is built by refining the mesh once and setting the polynomial degree to 10. Then the elements of the space are built and the space is plotted.
hp2D::hpAdaptiveSpaceH1 spc(msh, 1, 10); spc.rebuild(); std::cout << std::endl << "Space: " << spc << std::endl; graphics::drawMeshEPS(spc,"exactDTN_space.eps",100,1.0,20);
Now the trace space of the boundary
can be built.
hp2D::TraceSpace tspc(spc,concepts::makeSet<uint>(1,attrBoundary),hp2D::TraceSpace::FIRST); std::cout << std::endl << "Trace Space: " << tspc << std::endl;
The right hand side
hp1D::Riesz<Cmplx> lform(alpha0*uIncGrad); concepts::Vector<Cmplx> rhs(tspc, lform);
concepts::SparseMatrix<Cmplx> S(spc.dim(), spc.dim()); hp2D::Laplace<Cmplx> la(alpha); concepts::SparseMatrix<Cmplx> A(spc, la); A.addInto(S, 1.0); hp2D::Identity<Cmplx> id(beta); concepts::SparseMatrix<Cmplx> M(spc, id); M.addInto(S, -1.0);
Now we perform the DtN sum. First we define the DtN factor
const Real DtNfactor = omega/(2*M_PI*R);
Cmplx besselCoeff;
-DtNmaxN to DtNmaxN. for (int n = -abs(DtNmaxN); n <= abs(DtNmaxN); ++n) { // v-integral integrandF exp_p_inphi(n); hp1D::Riesz<Cmplx> vLform(alpha0*exp_p_inphi); concepts::Vector<Cmplx> vVector(tspc, vLform); // u-integral integrandF exp_m_inphi(-n); hp1D::Riesz<Cmplx> uLform(exp_m_inphi); concepts::Vector<Cmplx> uVector(tspc, uLform); // rank-1-product of v- and u-integral concepts::SparseMatrix<Cmplx> DtNmatrix(tspc,vVector,uVector); // ratio of derivative of Hankel function and Hankel function besselCoeff = Cmplx(concepts::besselJn(omega*R,n-1),concepts::besselYn(omega*R,n-1)) / Cmplx(concepts::besselJn(omega*R,n),concepts::besselYn(omega*R,n)) - n/(omega*R); DtNmatrix.addInto(S, -DtNfactor*besselCoeff); // integral of uInc * exp_m_inphi Cmplx integral = concepts::integrate(tspc,uInc*exp_m_inphi); rhs = rhs - vVector*(DtNfactor*besselCoeff*integral); }
The system matrix including the DtN part is displayed
std::cout << std::endl << "System Matrix: " << S << std::endl;
concepts::SuperLU<Cmplx> solver(S); std::cout << std::endl << "Solver: " << solver << std::endl; concepts::Vector<Cmplx> sol(spc); solver(rhs, sol);
In order to plot the solution the shape functions are computed on equidistant points using the trapezoidal quadrature rule.
hp2D::IntegrableQuad::rule().set(concepts::TRAPEZE, true, 10); spc.recomputeShapefunctions(); graphics::MatlabGraphics(spc, "exactDtN", sol);
Finally, exceptions are caught and a sane return value is given back.
} catch (concepts::ExceptionBase& e) { std::cout << e << std::endl; return 1; } return 0; }
Output of the program:
Mesh: Circle(ncell = 13, cells: Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(0), (Vertex(Key(0)), Vertex(Key(1)), Vertex(Key(2)), Vertex(Key(3))), Attribute(21)), vtx = [<2>(0.5, 0), <2>(0, 0.5), <2>(-0.5, 0), <2>(0, -0.5)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(1), (Vertex(Key(1)), Vertex(Key(0)), Vertex(Key(4)), Vertex(Key(5))), Attribute(21)), vtx = [<2>(0, 0.5), <2>(0.5, 0), <2>(1, 0), <2>(0, 1)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(2), (Vertex(Key(2)), Vertex(Key(1)), Vertex(Key(5)), Vertex(Key(6))), Attribute(21)), vtx = [<2>(-0.5, 0), <2>(0, 0.5), <2>(0, 1), <2>(-1, 0)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(3), (Vertex(Key(3)), Vertex(Key(2)), Vertex(Key(6)), Vertex(Key(7))), Attribute(21)), vtx = [<2>(0, -0.5), <2>(-0.5, 0), <2>(-1, 0), <2>(0, -1)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(4), (Vertex(Key(0)), Vertex(Key(3)), Vertex(Key(7)), Vertex(Key(4))), Attribute(21)), vtx = [<2>(0.5, 0), <2>(0, -0.5), <2>(0, -1), <2>(1, 0)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(5), (Vertex(Key(5)), Vertex(Key(4)), Vertex(Key(8)), Vertex(Key(9))), Attribute(22)), vtx = [<2>(0, 1), <2>(1, 0), <2>(8, 0), <2>(0, 8)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(6), (Vertex(Key(6)), Vertex(Key(5)), Vertex(Key(9)), Vertex(Key(10))), Attribute(22)), vtx = [<2>(-1, 0), <2>(0, 1), <2>(0, 8), <2>(-8, 0)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(7), (Vertex(Key(7)), Vertex(Key(6)), Vertex(Key(10)), Vertex(Key(11))), Attribute(22)), vtx = [<2>(0, -1), <2>(-1, 0), <2>(-8, 0), <2>(0, -8)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(8), (Vertex(Key(4)), Vertex(Key(7)), Vertex(Key(11)), Vertex(Key(8))), Attribute(22)), vtx = [<2>(1, 0), <2>(0, -1), <2>(0, -8), <2>(8, 0)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(9), (Vertex(Key(9)), Vertex(Key(8)), Vertex(Key(12)), Vertex(Key(13))), Attribute(22)), vtx = [<2>(0, 8), <2>(8, 0), <2>(15, 0), <2>(0, 15)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(10), (Vertex(Key(10)), Vertex(Key(9)), Vertex(Key(13)), Vertex(Key(14))), Attribute(22)), vtx = [<2>(-8, 0), <2>(0, 8), <2>(0, 15), <2>(-15, 0)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(11), (Vertex(Key(11)), Vertex(Key(10)), Vertex(Key(14)), Vertex(Key(15))), Attribute(22)), vtx = [<2>(0, -8), <2>(-8, 0), <2>(-15, 0), <2>(0, -15)]), Quad2d(idx = Level<2>(0, 0); 0, 0), cntr = Quad(Key(12), (Vertex(Key(8)), Vertex(Key(11)), Vertex(Key(15)), Vertex(Key(12))), Attribute(22)), vtx = [<2>(8, 0), <2>(0, -8), <2>(0, -15), <2>(15, 0)]))
Space: hpAdaptiveSpaceH1(dim = 5241 (V:57, E:972, I:4212), nelm = 52)
Trace Space: TraceSpace(QuadEdgeFirst(), dim = 5241, nelm = 8)
System Matrix: SparseMatrix(5241x5241, HashedSparseMatrix: 754721 (2.74763%) entries bound.)
Solver: SuperLU(n = 5241)
Plot of the refined mesh:
Matlab plots of the total field
:
[1] M. Abramowitz, and I.A. Stegun (ed.), "Handbook of mathematical functions with formulas, graphs, and mathematical tables", National Bureau of Standards, Applied Mathematics Series 55, tenth printing, 1972.
[2] A. Sommerfeld, "Partial Differential Equations in Physics - Lectures on Theoretical Physics Volume VI", Academic Press, New York, 1964.
#include <iostream> #include "basics.hh" #include "formula.hh" #include "function.hh" #include "geometry.hh" #include "graphics.hh" #include "hp1D.hh" #include "hp2D.hh" #include "operator.hh" #include "space.hh" #include "toolbox.hh" using concepts::Real; using concepts::Cmplx; // ************************************************************** integrandF ** /* * Class for evaluating the integrand function of the exact DtN map The order \c n has to be given to evaluate \f[(x+iy)^n\f], where \f$x\f$ and \f$y\f$ are cartesian coordinates. */ class integrandF : public concepts::Formula<Cmplx> { public: // ** Constructor ** integrandF(const int n = 0) : n_(n) {} // ** () opterator ** virtual Cmplx operator() (const Real p, const Real t = 0.0) const; virtual Cmplx operator() (const concepts::Real2d& p, const Real t = 0.0) const; virtual Cmplx operator() (const concepts::Real3d& p, const Real t = 0.0) const; // ** clone ** virtual integrandF* clone() const { return new integrandF(n_); } protected: virtual std::ostream& info(std::ostream& os) const; private: // ** exponent of the function ** const int n_; }; Cmplx integrandF::operator() (const Real p, const Real t) const { std::stringstream error; error << "integrandF::operator() not defined for input parameter of type Real"; throw concepts::MissingFeature(error.str()); return 0.0; } Cmplx integrandF::operator() (const concepts::Real2d& p, const Real t) const { Real x = p[0]; Real y = p[1]; Real r = sqrt(x*x+y*y); Cmplx c(x/r,y/r); return pow(c,n_); } Cmplx integrandF::operator() (const concepts::Real3d& p, const Real t) const { const concepts::Real2d p2(p[0], p[1]); return (*this)(p2,t); } std::ostream& integrandF::info(std::ostream& os) const { return os << "integrandF(" << n_ << ")"; } // ******************************************************************** main ** int main(int argc, char** argv) { try { // ** parameters ** const Real R = 15.0; // outer radius const Real innerR = 1.0; // inner radius (i.e. radius of scatterer) const Real omega = 1.0; // frequency const Real eps0 = 1.0; // permittivity outside the scatterer const Real mu0 = 1.0; // permeability outside the scatterer const Cmplx epsSc (4.0, 0.0); // permittivity inside the scatterer const Real muSc = 1.0; // permeability inside the scatterer const int DtNmaxN = 5; // number DtN summands // ** attributes of the computational domain ** const uint attrBoundary = 11; // boundary const uint attrSc = 21; // scatterer const uint attr0 = 22; // computational domain without scatterer // ** set mode ** enum TETM {TE, TM}; TETM mode = TM; // ** declare piecewise constant formulas for alpha and beta ** concepts::PiecewiseConstFormula<Cmplx> alpha; concepts::PiecewiseConstFormula<Cmplx> beta; concepts::ConstFormula<Cmplx> alpha0; // ** Piecewise constant alpha and beta (TE-case) ** if (mode == TE) { alpha[concepts::Attribute(attrSc)] = Cmplx(pow(epsSc,-1.0)); alpha[concepts::Attribute(attr0)] = Cmplx(pow(eps0,-1.0),0.0); beta[concepts::Attribute(attrSc)] = Cmplx(omega*omega*muSc,0.0); beta[concepts::Attribute(attr0)] = Cmplx(omega*omega*mu0,0.0); alpha0 = Cmplx(pow(eps0,-1.0),0.0); } // ** Piecewise constant alpha and beta (TM-case) ** if (mode == TM) { alpha[concepts::Attribute(attrSc)] = Cmplx(1.0, 0.0); alpha[concepts::Attribute(attr0)] = Cmplx(1.0, 0.0); beta[concepts::Attribute(attrSc)] = Cmplx(omega*omega*epsSc*muSc); beta[concepts::Attribute(attr0)] = Cmplx(omega*omega*eps0*mu0,0.0); alpha0 = Cmplx(1.0, 0.0); } // ** wave vector in the exterior ** const Real k0 = omega*sqrt(mu0*eps0); // ** incoming wave and its normal gradient ** std::stringstream uIncReal, uIncImag, uIncGradReal, uIncGradImag; uIncReal << "(cos((" << k0 << ")*x))"; uIncImag << "(sin((" << k0 << ")*x))"; uIncGradReal << "(-(x/(sqrt(x*x+y*y)))*" << k0 << "*sin((" << k0 << ")*x))"; uIncGradImag << "( (x/(sqrt(x*x+y*y)))*" << k0 << "*cos((" << k0 << ")*x))"; concepts::ParsedFormula<Cmplx> uInc( uIncReal.str(), uIncImag.str()); concepts::ParsedFormula<Cmplx> uIncGrad(uIncGradReal.str(), uIncGradImag.str()); // ** create mesh ** Real ringData[3] = {innerR,(innerR+R)/2.0,R}; uint quadAttrData[3] = {attrSc,attr0,attr0}; uint edgeAttrData[3] = {0,0,attrBoundary}; concepts::Array<Real> ring(ringData,3); concepts::Array<uint> ringQuadAttr(quadAttrData,3); concepts::Array<uint> ringEdgeAttr(edgeAttrData,3); concepts::Circle msh(ring,ringQuadAttr,ringEdgeAttr); std::cout << std::endl << "Mesh: " << msh << std::endl; graphics::drawMeshEPS(msh,"exactDTN_mesh.eps",100,1.0,20); // ** space ** hp2D::hpAdaptiveSpaceH1 spc(msh, 1, 10); spc.rebuild(); std::cout << std::endl << "Space: " << spc << std::endl; graphics::drawMeshEPS(spc,"exactDTN_space.eps",100,1.0,20); // ** build trace space of boundary ** hp2D::TraceSpace tspc(spc,concepts::makeSet<uint>(1,attrBoundary),hp2D::TraceSpace::FIRST); std::cout << std::endl << "Trace Space: " << tspc << std::endl; // ** right hand side without DtN part ** hp1D::Riesz<Cmplx> lform(alpha0*uIncGrad); concepts::Vector<Cmplx> rhs(tspc, lform); // ** system matrix without DtN part ** concepts::SparseMatrix<Cmplx> S(spc.dim(), spc.dim()); hp2D::Laplace<Cmplx> la(alpha); concepts::SparseMatrix<Cmplx> A(spc, la); A.addInto(S, 1.0); hp2D::Identity<Cmplx> id(beta); concepts::SparseMatrix<Cmplx> M(spc, id); M.addInto(S, -1.0); // ** DtN part of system matrix and rhs /* * implementation of the DtN map * * \int (Bu)v ds = \omega/(2*\pi*R) * \sum H'_n(\omega*R)/H_n(\omega*R) * * \int v exp(i*n*\phi(x)) ds * \int u exp(-i*n*\phi(x)) ds * */ const Real DtNfactor = omega/(2*M_PI*R); Cmplx besselCoeff; for (int n = -abs(DtNmaxN); n <= abs(DtNmaxN); ++n) { // v-integral integrandF exp_p_inphi(n); hp1D::Riesz<Cmplx> vLform(alpha0*exp_p_inphi); concepts::Vector<Cmplx> vVector(tspc, vLform); // u-integral integrandF exp_m_inphi(-n); hp1D::Riesz<Cmplx> uLform(exp_m_inphi); concepts::Vector<Cmplx> uVector(tspc, uLform); // rank-1-product of v- and u-integral concepts::SparseMatrix<Cmplx> DtNmatrix(tspc,vVector,uVector); // ratio of derivative of Hankel function and Hankel function besselCoeff = Cmplx(concepts::besselJn(omega*R,n-1),concepts::besselYn(omega*R,n-1)) / Cmplx(concepts::besselJn(omega*R,n),concepts::besselYn(omega*R,n)) - n/(omega*R); DtNmatrix.addInto(S, -DtNfactor*besselCoeff); // integral of uInc * exp_m_inphi Cmplx integral = concepts::integrate(tspc,uInc*exp_m_inphi); rhs = rhs - vVector*(DtNfactor*besselCoeff*integral); } // ** print system matrix and rhs vector ** std::cout << std::endl << "System Matrix: " << S << std::endl; // ** solve ** concepts::SuperLU<Cmplx> solver(S); std::cout << std::endl << "Solver: " << solver << std::endl; concepts::Vector<Cmplx> sol(spc); solver(rhs, sol); // ** plot solution ** hp2D::IntegrableQuad::rule().set(concepts::TRAPEZE, true, 10); spc.recomputeShapefunctions(); graphics::MatlabGraphics(spc, "exactDtN", sol); } catch (concepts::ExceptionBase& e) { std::cout << e << std::endl; return 1; } return 0; }