]> https://gitweb.dealii.org/ - dealii.git/commitdiff
add cartoon
authorManaswinee Bezbaruah <bezba004@tamu.edu>
Fri, 18 Mar 2022 16:34:39 +0000 (11:34 -0500)
committerMatthias Maier <tamiko@43-1.org>
Thu, 26 May 2022 05:16:02 +0000 (00:16 -0500)
examples/step-81/doc/intro.dox
examples/step-81/doc/results.dox
examples/step-81/step-81.cc

index b9e5d19196f7c5446928775262839db0656d6b5a..15bc1ba658536549bb578fec623294092b2ea6e8 100644 (file)
@@ -24,13 +24,13 @@ how to implement simple first-order absorbing boundary conditions and a
 more sophisticated "perfectly matched layer" for electromagnetic waves.
 
 
-<h2>Time-Harmonic Maxwell's Equations with interface conditions</h2>
+<h3>Time-Harmonic Maxwell's Equations with interface conditions</h3>
 
 We start the discussion with a short derivation of the governing equations
 and some pointers to literature.
 
 
-<h3>Derivation of time-harmonic Maxwell's equations</h3>
+<h4>Derivation of time-harmonic Maxwell's equations</h4>
 
 In two ($d=2$) or three ($d=3$) spatial dimensions,
 the time evolution of an electromagnetic
@@ -105,7 +105,7 @@ write $\mathbf{E}(\mathbf{x})$, $\mathbf{H}(\mathbf{x})$, etc., when
 referring to the time-harmonic fields.
 
 
-<h3>Jump conditions on lower dimensional interfaces</h3>
+<h4>Jump conditions on lower dimensional interfaces</h4>
 
 Graphene is a two-dimensional carbon allotrope with a <i>single</i> atom
 layer that is arranged in a honeycomb lattice @cite Geim2004. Due to its
@@ -133,7 +133,11 @@ field. This is best seen by visualizing <a
 href="https://en.wikipedia.org/wiki/Amp%C3%A8re%27s_circuital_law">Ampère's
 law</a>,
 
-@f[\text{TODO}@f]
+@htmlonly
+<p align="center">
+  <img src="https://www.dealii.org/images/steps/developer/step-81-cartoon.png" alt="Visualization of Ohm's law and Ampère's law leading to a jump condition over the interface" height="200">
+</p>
+@endhtmlonly
 
 and then taking the limit of the upper and lower part of the line integral
 approaching the sheet. In contrast, the tangential part of the electric
@@ -151,7 +155,7 @@ when approaching the interface from above or below the interface:
 $\mathbf{F}^\pm(\mathbf{x})=\lim_{\delta\to0,\delta>0}\mathbf{F}(\mathbf{x}\pm\delta\mathbf{\nu})$.
 
 
-<h3> Rescaling </h3>
+<h4> Rescaling </h4>
 
 We will be using a rescaled version of the Maxwell's equations described above.
 The rescaling has the following key differences:<br />
@@ -210,7 +214,7 @@ i\varepsilon_r\hat{\mathbf{E}} + \nabla\times(\mu^{-1}\mathbf{H})
 
 We will omit the hat in further discussion for ease of notation.
 
-<h3> Variational Statement</h3>
+<h4> Variational Statement</h4>
 
 Let $\Omega \subset \mathbb{R}^n$, $(n = 2,3)$ be a simply connected and bounded
 domain with Lipschitz-continuous and piecewise smooth boundary, $\partial\Omega$.
@@ -310,7 +314,7 @@ A(\mathbf{E},\varphi) = F(\varphi)
 @f]
 
 
-<h3>Absorbing boundary conditions and perfectly matched layer</h3>
+<h4>Absorbing boundary conditions and perfectly matched layer</h4>
 
 Moreover, the above equations are supplemented by the Silver-Müller radiation
 condition, if the ambient (unbounded) medium is isotropic. This amounts to the
@@ -326,7 +330,7 @@ x \not\in \Sigma
 In our case, we eliminate reflection from infinity by implementing a PML and
 avoid the explicit use of the last condition.
 
-<h2> Discretization Scheme</h2>
+<h3> Discretization Scheme</h3>
 
 The variational form is discretized on a non-uniform quadrilateral mesh with
 higher-order, curl-conforming Nédélec elements. This way the interface with a
@@ -361,7 +365,7 @@ A_{ij} = F_i
 Using a skeleton similar to step-4, we have constructed a Maxwell class and we
 have used complex-valued FENedelec elements to solve our equations. <br />
 
-<h2> Perfectly Matched Layer </h2>
+<h3> Perfectly Matched Layer </h3>
 The SPP amplitude is negatively effected by the absorbing boundary condition and
 this causes the solution image to be distorted. In order to reduce the resonance
 and distortion in our solutions, we are implementing a Perfectly Matched Layer
@@ -381,7 +385,7 @@ be a function of the radial distance $\rho$ from the origin $e_r$. The normal fi
 $\nu$ of $\Sigma$ is orthogonal to the radial direction $e_r$, which makes
 $\mathbf{J}_a \equiv 0$ and $\mathbf{M}_a \equiv 0$ within the PML. <br />
 
-<img src = "PML.png"></img>
+<img src = "PML.png">
 
 Introduce a change of coordinates
 @f[
index dee312300603a529b20dcbe4bba7ee737e838991..ab14f3f31d3c702a3ed6c0159e6d795e1006bf03 100644 (file)
@@ -14,7 +14,7 @@ The following images are the outputs for the imaginary $E_x$ without the interfa
 
 @code
 # use absorbing boundary conditions?
-  set absorbing boundary condition boolean = false
+  set absorbing boundary condition = false
 
 # position of the dipole
   set dipole position    = 0, 0
@@ -25,21 +25,24 @@ The following images are the outputs for the imaginary $E_x$ without the interfa
 # surface conductivity between material 1 and material 2
   set sigma              = 0, 0; 0, 0| 0, 0; 0, 0
 @endcode
+
 Following are the output images:
 
 <table width="80%" align="center">
   <tr>
+      <td align="center">
+      <img src="https://www.dealii.org/images/steps/developer/step-81-nointerface_abs_PML0.png" alt="Visualization of the solution of step-81 with no interface, Dirichlet boundary conditions and PML strength 0" height="210"/>
+      <p> Solution with no interface, Dirichlet boundary conditions and PML strength 0.</p>
+      </td>
+      <td></td>
        <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-nointerface_abs_PML0.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 0" height="300">
-       </td>
-       <td></td>
-       <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-nointerface_noabs_PML0.png" alt="Visualization of the solution of step-81 with no interface, no absorbing boundary conditions and PML strength 0" height="300">
+      <img src="https://www.dealii.org/images/steps/developer/step-81-nointerface_noabs_PML0.png" alt="Visualization of the solution of step-81 with no interface, no absorbing boundary conditions and PML strength 0" height="210">
+       <p> Solution with no interface, absorbing boundary conditions and PML strength 0.</p>
        </td>
        <td></td>
        <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-nointerface_abs_PML4.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 4" height="300">
+      <img src="https://www.dealii.org/images/steps/developer/step-81-nointerface_abs_PML4.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 4" height="210">
+      <p> Solution with no interface, absorbing boundary conditions and PML strength 4.</p>
     </td>
   </tr>
 </table>
@@ -62,16 +65,19 @@ Once again, we will visualize the output with absorbing boundary conditions and
 <table width="80%" align="center">
   <tr>
        <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-imagEx_noabs_PML0.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 0" height="300">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-imagEx_noabs_PML0.png" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+        <p> Solution with an interface, Dirichlet boundary conditions and PML strength 0.</p>
        </td>
        <td></td>
        <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-imagEx_abs_PML0.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 0" height="300">
-    </td>
-    <td></td>
-    <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-imagEx_abs_PML4.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 4" height="300">
-    </td>
+        <img src="https://www.dealii.org/images/steps/developer/step-81-imagEx_abs_PML0.png" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 0.</p>
+        </td>
+        <td></td>
+        <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-imagEx_abs_PML4.png" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 4" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 4.</p>
+        </td>
   </tr>
 </table>
 
@@ -79,26 +85,69 @@ Once again, we will visualize the output with absorbing boundary conditions and
 <table width="80%" align="center">
   <tr>
        <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-realEx_noabs_PML0.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 0" height="300">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-realEx_noabs_PML0.png" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+       <p> Solution with an interface, Dirichlet boundary conditions and PML strength 0.</p>
        </td>
        <td></td>
        <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-realEx_abs_PML0.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 0" height="300">
-    </td>
-    <td></td>
-    <td align="center">
-      <img src="https://www.dealii.org/images/steps/developer/step-81-realEx_abs_PML4.png" alt="Visualization of the solution of step-81 with no interface, absorbing boundary conditions and PML strength 4" height="300">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-realEx_abs_PML0.png" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 0.</p>
+        </td>
+        <td></td>
+        <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-realEx_abs_PML4.png" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 4" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 4.</p>
     </td>
   </tr>
 </table>
 
 The SPP is confined near the interface that we created, however without absorbing boundary conditions, we don't observe a dissipation effect. On adding the absorbing boundary conditions, we observe distortion and resonance and we still don't notice any dissipation. As expected, the PML removes the distortion and resonance. The standing wave is also dissipating and getting absorbed within the PML, and as we increase the PML strength, the standing wave will dissipate more within the PML ring.
 
+Here are some animations to demonstrate the effect of the PML
+<table width="80%" align="center">
+  <tr>
+       <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-dirichlet_Ex.gif" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+        <p> Solution with an interface, Dirichlet boundary conditions and PML strength 0.</p>
+       </td>
+       <td></td>
+       <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-absorbing_Ex.gif" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 0.</p>
+        </td>
+        <td></td>
+        <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-perfectly_matched_layer_Ex.gif" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 4" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 4.</p>
+        </td>
+  </tr>
+</table>
+
+
+<table width="80%" align="center">
+  <tr>
+       <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-dirichlet_Ey.gif" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+        <p> Solution with an interface, Dirichlet boundary conditions and PML strength 0.</p>
+       </td>
+       <td></td>
+       <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-absorbing_Ey.gif" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 0" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 0.</p>
+        </td>
+        <td></td>
+        <td align="center">
+        <img src="https://www.dealii.org/images/steps/developer/step-81-perfectly_matched_layer_Ey.gif" alt="Visualization of the solution of step-81 with an interface, absorbing boundary conditions and PML strength 4" height="210">
+       <p> Solution with an interface, absorbing boundary conditions and PML strength 4.</p>
+        </td>
+  </tr>
+</table>
+
 <h3> Notes </h3>
 
 <h4> Real and Complex Matrices </h4>
-As is evident from the results, we are splitting our solution matrices into the real and the imaginary components. We started off using the $H^{curl}$ conforming Nédélec Elements, and we made two copies of the Finite Elements in order 
-to represent the real and the imaginary components of our input (FE_NedelecSZ was used instead of FE_Nedelec to avoid the sign conflicts issues present in traditional Nédélec elements). In the assembly, we create two vectors of dimension $dim$ that assist us in extracting the real and the imaginary components of our finite elements.  
+As is evident from the results, we are splitting our solution matrices into the real and the imaginary components. We started off using the $H^{curl}$ conforming Nédélec Elements, and we made two copies of the Finite Elements in order
+to represent the real and the imaginary components of our input (FE_NedelecSZ was used instead of FE_Nedelec to avoid the sign conflicts issues present in traditional Nédélec elements). In the assembly, we create two vectors of dimension $dim$ that assist us in extracting the real and the imaginary components of our finite elements.
 
 
 <h4> Rotations and Scaling </h4>
@@ -134,7 +183,7 @@ We will create a video demonstrating the wave in motion, which is essentially an
 template <int dim>
 void Maxwell<dim>::output_results(unsigned int t)
 {
-       std::cout << "Running step:" << alpha << std::endl;
+    std::cout << "Running step:" << t << std::endl;
     DataOut<2> data_out;
     data_out.attach_dof_handler(dof_handler);
     Vector<double> postprocessed;
@@ -151,10 +200,10 @@ void Maxwell<dim>::output_results(unsigned int t)
     data_out.add_data_vector(postprocessed, {"E_x","E_y","null0","null1"});
     data_out.build_patches();
     const std::string filename =
-               "solution-" + Utilities::int_to_string(alpha) + ".vtk";
+               "solution-" + Utilities::int_to_string(t) + ".vtk";
     std::ofstream output(filename);
     data_out.write_vtk(output);
-    std::cout << "Done running step:" << alpha << std::endl;
+    std::cout << "Done running step:" << t << std::endl;
 
 @endcode
 
@@ -168,8 +217,8 @@ for(int t = 0; t<=100; t++){
 This would generate 100 solution .vtk files, which can be opened in a group on Paraview and then can be saved as an animation. We used FFMPEG to generate gifs.
 
 <h4> Resulting videos </h4>
-Following are the resulting videos of our experiments. As we see, TODO 
+Following are the resulting videos of our experiments. As we see, TODO
 
 <h3> Possibilities for Extension </h3>
-The current program doesn't allow for iterative solvers as the solutions will not converge with an iterative solver. One possible direction for future work is to implement an iterative solver and involve more preconditioners. An advantage of iterative solvers is the more efficient memory usage, and our current memory usage does not allow for a large number of DOFs. 
-Another possible direction would be to perform Local Mesh Refinement (instead of Global Mesh Refinement). This will also help us visualize more DOFs in a more memory and time efficient way. 
+The current program doesn't allow for iterative solvers as the solutions will not converge with an iterative solver. One possible direction for future work is to implement an iterative solver and involve more preconditioners. An advantage of iterative solvers is the more efficient memory usage, and our current memory usage does not allow for a large number of DOFs.
+Another possible direction would be to perform Local Mesh Refinement (instead of Global Mesh Refinement). This will also help us visualize more DOFs in a more memory and time efficient way.
index e318b4d5f18b8ea7fda1ba56dd04a08c977f0640..0fbf4fd6d907e0928d074fc22c051ce3b46558ab 100644 (file)
 
 // @sect3{Include files}
 
- // The set of include files is quite standard. The most notable incluse is
- // the fe/fe_nedelec_sz.h file that allows us to use the FE_NedelecSZ elements.
- // This is an implementation of the $H^{curl}$ conforming Nédélec Elements
- // that resolves the sign conflict issues that arise from parametrization.
+// The set of include files is quite standard. The most notable incluse is
+// the fe/fe_nedelec_sz.h file that allows us to use the FE_NedelecSZ elements.
+// This is an implementation of the $H^{curl}$ conforming Nédélec Elements
+// that resolves the sign conflict issues that arise from parametrization.
 
 #include <deal.II/base/function.h>
 #include <deal.II/base/parameter_acceptor.h>
@@ -75,29 +75,31 @@ namespace Step81
   using namespace dealii;
   using namespace std::complex_literals;
 
-   // @sect4{Parameters Class}
-
-   // The Parameters class inherits ParameterAcceptor, and instantiates all the
-   // coefficients in our variational equations.
-   // These coefficients are passed through ParameterAcceptor and are editable
-   // through a .prm file
-   // More explanation on the use and inheritance from the ParameterAcceptor
-   // can be found in step-60.
-  
-   // epsilon is the Electric Permitivitty coefficient and it is a rank 2 tensor. Depending on the material,
-   // we assign the i^th diagonal element of the tensor to the material epsilon value
-   // (one of the private epsilon_1_ or epsilon_2_ variables).
-   //
-   // mu_inv  is the inverese of the Magnetic Permiabillity coefficient and it is a complex number.
-   
-   // sigma is the Surface Conductivity coefficient between material left and material right
-   // and it is a rank 2 tensor. It is only changed if we are at the interface between two
-   // materials. If we are at an interface, we assign the i^th diagonal element of the
-   // tensor to the private sigma_ value.
-   
-   // J_a is the strength and orientation of the dipole. It is a rank 1 tensor that depends
-   // on the private dipole_position_, dipole_radius_, dipole_strength_, dipole_orientation_
-   // variables.
+  // @sect4{Parameters Class}
+
+  // The Parameters class inherits ParameterAcceptor, and instantiates all the
+  // coefficients in our variational equations.
+  // These coefficients are passed through ParameterAcceptor and are editable
+  // through a .prm file
+  // More explanation on the use and inheritance from the ParameterAcceptor
+  // can be found in step-60.
+
+  // epsilon is the Electric Permitivitty coefficient and it is a rank 2 tensor.
+  // Depending on the material, we assign the i^th diagonal element of the
+  // tensor to the material epsilon value (one of the private epsilon_1_ or
+  // epsilon_2_ variables).
+  //
+  // mu_inv  is the inverese of the Magnetic Permiabillity coefficient and it is
+  // a complex number.
+
+  // sigma is the Surface Conductivity coefficient between material left and
+  // material right and it is a rank 2 tensor. It is only changed if we are at
+  // the interface between two materials. If we are at an interface, we assign
+  // the i^th diagonal element of the tensor to the private sigma_ value.
+
+  // J_a is the strength and orientation of the dipole. It is a rank 1 tensor
+  // that depends on the private dipole_position_, dipole_radius_,
+  // dipole_strength_, dipole_orientation_ variables.
 
   template <int dim>
   class Parameters : public ParameterAcceptor
@@ -114,18 +116,16 @@ namespace Step81
     using curl_type = Tensor<1, dim == 2 ? 1 : dim, rank0_type>;
 
   public:
-    rank2_type epsilon(const Point<dim, double> &x,
-                       types::material_id        material);
+    rank2_type epsilon(const Point<dim> &x, types::material_id material);
 
-    std::complex<double> mu_inv(const Point<dim, double> &x,
-                                types::material_id        material);
+    std::complex<double> mu_inv(const Point<dim> & x,
+                                types::material_id material);
 
-    rank2_type sigma(const dealii::Point<dim, double> &x,
-                     types::material_id                left,
-                     types::material_id                right);
+    rank2_type sigma(const dealii::Point<dim> &x,
+                     types::material_id        left,
+                     types::material_id        right);
 
-    rank1_type J_a(const dealii::Point<dim, double> &point,
-                   types::material_id                id);
+    rank1_type J_a(const dealii::Point<dim> &point, types::material_id id);
 
   private:
     rank2_type           epsilon_1;
@@ -134,10 +134,10 @@ namespace Step81
     std::complex<double> mu_inv_2;
     rank2_type           sigma_tensor;
 
-    double                 dipole_radius;
-    Point<dim>             dipole_position;
-    Tensor<1, dim, double> dipole_orientation;
-    rank0_type             dipole_strength;
+    double         dipole_radius;
+    Point<dim>     dipole_position;
+    Tensor<1, dim> dipole_orientation;
+    rank0_type     dipole_strength;
   };
 
 
@@ -177,11 +177,9 @@ namespace Step81
     add_parameter("dipole radius", dipole_radius, "radius of the dipole");
 
     dipole_position = Point<dim>(0., 0.8);
-    add_parameter("dipole position",
-                  dipole_position,
-                  "position of the dipole");
+    add_parameter("dipole position", dipole_position, "position of the dipole");
 
-    dipole_orientation = Tensor<1, dim, double>{{0., 1.}};
+    dipole_orientation = Tensor<1, dim>{{0., 1.}};
     add_parameter("dipole orientation",
                   dipole_orientation,
                   "orientation of the dipole");
@@ -192,14 +190,14 @@ namespace Step81
 
   template <int dim>
   typename Parameters<dim>::rank2_type
-  Parameters<dim>::epsilon(const Point<dim, double> & /*x*/,
+  Parameters<dim>::epsilon(const Point<dim> & /*x*/,
                            types::material_id material)
   {
     return (material == 1 ? epsilon_1 : epsilon_2);
   }
 
   template <int dim>
-  std::complex<double> Parameters<dim>::mu_inv(const Point<dim, double> & /*x*/,
+  std::complex<double> Parameters<dim>::mu_inv(const Point<dim> & /*x*/,
                                                types::material_id material)
   {
     return (material == 1 ? mu_inv_1 : mu_inv_2);
@@ -207,7 +205,7 @@ namespace Step81
 
   template <int dim>
   typename Parameters<dim>::rank2_type
-  Parameters<dim>::sigma(const dealii::Point<dim, double> & /*x*/,
+  Parameters<dim>::sigma(const dealii::Point<dim> & /*x*/,
                          types::material_id left,
                          types::material_id right)
   {
@@ -216,7 +214,7 @@ namespace Step81
 
   template <int dim>
   typename Parameters<dim>::rank1_type
-  Parameters<dim>::J_a(const dealii::Point<dim, double> &point,
+  Parameters<dim>::J_a(const dealii::Point<dim> &point,
                        types::material_id /*id*/)
   {
     rank1_type J_a;
@@ -230,13 +228,23 @@ namespace Step81
     return J_a;
   }
 
-   // @sect4{PerfectlyMatchedLayer Class}
-   // The PerfectlyMatchedLayer class inherits ParameterAcceptor,
-   // and it modifies our coefficients from Parameters.
-   // The radii and the strength of the PML is specified, and the
-   // coefficients will be modified using transformation
-   // matrices within the PML region. The radii and strength of
-   // the PML are editable through a .prm file
+  // @sect4{PerfectlyMatchedLayer Class}
+  // The PerfectlyMatchedLayer class inherits ParameterAcceptor,
+  // and it modifies our coefficients from Parameters.
+  // The radii and the strength of the PML is specified, and the
+  // coefficients will be modified using transformation
+  // matrices within the PML region. The radii and strength of
+  // the PML are editable through a .prm file
+  // The rotation function is the $T_{exer}$ mentioned in the
+  // perfectly matched layer section of the introduction.
+  // Moreover, the matrices A, B and C are defined as mentioned
+  // @f[
+  // A = T_{e_xe_r}^{-1}
+  // \text{diag}\left(\frac{1}{\bar{d}^2},\frac{1}{d\bar{d}}\right)T_{e_xe_r},\qquad
+  // B = T_{e_xe_r}^{-1} \text{diag}\left(d,\bar{d}\right)T_{e_xe_r},\qquad
+  // C = T_{e_xe_r}^{-1} \text{diag}\left(\frac{1}{\bar{d}},\frac{1}{d}\right)
+  // T_{e_xe_r}.\qquad
+  // @f]
 
   template <int dim>
   class PerfectlyMatchedLayer : public ParameterAcceptor
@@ -256,20 +264,20 @@ namespace Step81
     double outer_radius;
     double strength;
 
-    std::complex<double> d_tensor(const Point<dim, double> point);
+    std::complex<double> d(const Point<dim> point);
 
-    std::complex<double> d_bar_tensor(const Point<dim, double> point);
+    std::complex<double> d_bar(const Point<dim> point);
 
 
-    rank2_type T_exer(std::complex<double> d_1,
-                      std::complex<double> d_2,
-                      Point<dim>           point);
+    rank2_type rotation(std::complex<double> d_1,
+                        std::complex<double> d_2,
+                        Point<dim>           point);
 
-    rank2_type a_matrix(const Point<dim, double> point);
+    rank2_type a_matrix(const Point<dim> point);
 
-    rank2_type b_matrix(const Point<dim, double> point);
+    rank2_type b_matrix(const Point<dim> point);
 
-    rank2_type c_matrix(const Point<dim, double> point);
+    rank2_type c_matrix(const Point<dim> point);
   };
 
 
@@ -281,30 +289,30 @@ namespace Step81
     add_parameter("inner radius",
                   inner_radius,
                   "inner radius of the PML shell");
-    outer_radius = 15.;
+    outer_radius = 20.;
     add_parameter("outer radius",
                   outer_radius,
                   "outer radius of the PML shell");
     strength = 8.;
     add_parameter("strength", strength, "strength of the PML");
-  };
+  }
 
 
   template <int dim>
   typename std::complex<double>
-  PerfectlyMatchedLayer<dim>::d_tensor(const Point<dim, double> point)
+  PerfectlyMatchedLayer<dim>::d(const Point<dim> point)
   {
     const auto   radius = point.norm();
     const double s =
       strength * ((radius - inner_radius) * (radius - inner_radius)) /
       ((outer_radius - inner_radius) * (outer_radius - inner_radius));
-    return 1 + 1.0i * s;
+    return 1.0 + 1.0i * s;
   }
 
 
   template <int dim>
   typename std::complex<double>
-  PerfectlyMatchedLayer<dim>::d_bar_tensor(const Point<dim, double> point)
+  PerfectlyMatchedLayer<dim>::d_bar(const Point<dim> point)
   {
     const auto   radius = point.norm();
     const double s_bar =
@@ -312,15 +320,15 @@ namespace Step81
       ((radius - inner_radius) * (radius - inner_radius) *
        (radius - inner_radius)) /
       (radius * (outer_radius - inner_radius) * (outer_radius - inner_radius));
-    return 1 + 1.0i * s_bar;
+    return 1.0 + 1.0i * s_bar;
   }
 
 
   template <int dim>
   typename PerfectlyMatchedLayer<dim>::rank2_type
-  PerfectlyMatchedLayer<dim>::T_exer(std::complex<double> d_1,
-                                     std::complex<double> d_2,
-                                     Point<dim>           point)
+  PerfectlyMatchedLayer<dim>::rotation(std::complex<double> d_1,
+                                       std::complex<double> d_2,
+                                       Point<dim>           point)
   {
     rank2_type result;
     result[0][0] = point[0] * point[0] * d_1 + point[1] * point[1] * d_2;
@@ -333,39 +341,43 @@ namespace Step81
 
   template <int dim>
   typename PerfectlyMatchedLayer<dim>::rank2_type
-  PerfectlyMatchedLayer<dim>::a_matrix(const Point<dim, double> point)
+  PerfectlyMatchedLayer<dim>::a_matrix(const Point<dim> point)
   {
-    const auto d     = d_tensor(point);
-    const auto d_bar = d_bar_tensor(point);
-    return invert(T_exer(d * d, d * d_bar, point)) *
-           T_exer(d * d, d * d_bar, point);
+    const auto d     = this->d(point);
+    const auto d_bar = this->d_bar(point);
+    return invert(rotation(d * d, d * d_bar, point)) *
+           rotation(d * d, d * d_bar, point);
   }
 
 
   template <int dim>
   typename PerfectlyMatchedLayer<dim>::rank2_type
-  PerfectlyMatchedLayer<dim>::b_matrix(const Point<dim, double> point)
+  PerfectlyMatchedLayer<dim>::b_matrix(const Point<dim> point)
   {
-    const auto d     = d_tensor(point);
-    const auto d_bar = d_bar_tensor(point);
-    return invert(T_exer(d, d_bar, point)) * T_exer(d, d_bar, point);
+    const auto d     = this->d(point);
+    const auto d_bar = this->d_bar(point);
+    return invert(rotation(d, d_bar, point)) * rotation(d, d_bar, point);
   }
 
 
   template <int dim>
   typename PerfectlyMatchedLayer<dim>::rank2_type
-  PerfectlyMatchedLayer<dim>::c_matrix(const Point<dim, double> point)
+  PerfectlyMatchedLayer<dim>::c_matrix(const Point<dim> point)
   {
-    const auto d     = d_tensor(point);
-    const auto d_bar = d_bar_tensor(point);
-    return invert(T_exer(1. / d_bar, 1. / d, point)) *
-           T_exer(1. / d_bar, 1. / d, point);
+    const auto d     = this->d(point);
+    const auto d_bar = this->d_bar(point);
+    return invert(rotation(1. / d_bar, 1. / d, point)) *
+           rotation(1. / d_bar, 1. / d, point);
   }
 
 
   // @sect4{Maxwell Class}
   // At this point we are ready to instantiate all the major functions of
-  // the finite element program and also a list of variables.
+  // the finite element program and also a list of variables. Most of these
+  // an exact copy of the functions in the tutorial programs. In addition,
+  // we instatiate the parameters and the perfectly matched layer. The
+  // default values of these parameters are set to show us a standing wave
+  // with absorbing boundary conditions and a PML.
 
   template <int dim>
   class Maxwell : public ParameterAcceptor
@@ -380,7 +392,7 @@ namespace Step81
     unsigned int refinements;
     unsigned int fe_order;
     unsigned int quadrature_order;
-    bool absorbing_boundary;
+    bool         absorbing_boundary;
 
     void parse_parameters_callback();
     void make_grid();
@@ -435,11 +447,11 @@ namespace Step81
     add_parameter("quadrature order",
                   quadrature_order,
                   "order of the quadrature");
-        
+
     absorbing_boundary = true;
     add_parameter("absorbing boundary condition",
-                    absorbing_boundary,
-                    "use absorbing boundary conditions?");
+                  absorbing_boundary,
+                  "use absorbing boundary conditions?");
   }
 
 
@@ -459,24 +471,25 @@ namespace Step81
   {
     GridGenerator::hyper_cube(triangulation, -scaling, scaling);
     triangulation.refine_global(refinements);
-    
-    if (!absorbing_boundary){
+
+    if (!absorbing_boundary)
+      {
         for (auto &face : triangulation.active_face_iterators())
-            if (face->at_boundary())
-                face->set_boundary_id(1);
-    };
-      
+          if (face->at_boundary())
+            face->set_boundary_id(1);
+      };
+
     for (auto &cell : triangulation.active_cell_iterators())
       if (cell->center()[1] > 0.)
         cell->set_material_id(1);
       else
         cell->set_material_id(2);
-    
+
 
     std::cout << "Number of active cells: " << triangulation.n_active_cells()
               << std::endl;
   }
+
   // Enumerate all the degrees of freedom and set up matrix and vector
   // objects to hold the system data. Enumerating is done by using
   // DoFHandler::distribute_dofs().
@@ -532,17 +545,20 @@ namespace Step81
   }
 
 
- // Assemble the stiffness matrix and the right-hand side:
- //\f{align*}{
- // A_{ij} = \int_\Omega (\mu_r^{-1}\nabla \times \varphi_i) \cdot (\nabla\times\bar{\varphi}_j)\text{d}x
- // - \int_\Omega \varepsilon_r\varphi_i \cdot \bar{\varphi}_j\text{d}x
- // - i\int_\Sigma (\sigma_r^{\Sigma}(\varphi_i)_T) \cdot (\bar{\varphi}_j)_T\text{do}x
- // - i\int_{\partial\Omega} (\sqrt{\mu_r^{-1}\varepsilon}(\varphi_i)_T) \cdot (\nabla\times(\bar{\varphi}_j)_T)\text{d}x,
- // \f}
- // \f{align}{
- //  F_i = i\int_\Omega J_a \cdot \bar{\varphi_i}\text{d}x - \int_\Omega \mu_r^{-1} \cdot (\nabla \times \bar{\varphi_i}) \text{d}x.
- // \f}
- // In addition, we will be modifying the coefficients if the position of the cell is within the PML region.
+  // Assemble the stiffness matrix and the right-hand side:
+  //\f{align*}{
+  // A_{ij} = \int_\Omega (\mu_r^{-1}\nabla \times \varphi_i) \cdot
+  // (\nabla\times\bar{\varphi}_j)\text{d}x
+  // - \int_\Omega \varepsilon_r\varphi_i \cdot \bar{\varphi}_j\text{d}x
+  // - i\int_\Sigma (\sigma_r^{\Sigma}(\varphi_i)_T) \cdot
+  // (\bar{\varphi}_j)_T\text{do}x
+  // - i\int_{\partial\Omega} (\sqrt{\mu_r^{-1}\varepsilon}(\varphi_i)_T) \cdot
+  // (\nabla\times(\bar{\varphi}_j)_T)\text{d}x, \f} \f{align}{
+  //  F_i = i\int_\Omega J_a \cdot \bar{\varphi_i}\text{d}x - \int_\Omega
+  //  \mu_r^{-1} \cdot (\nabla \times \bar{\varphi_i}) \text{d}x.
+  // \f}
+  // In addition, we will be modifying the coefficients if the position of the
+  // cell is within the PML region.
 
   template <int dim>
   void Maxwell<dim>::assemble_system()
@@ -561,7 +577,7 @@ namespace Step81
                                             update_quadrature_points |
                                             update_normal_vectors |
                                             update_JxW_values);
-    
+
     const unsigned int dofs_per_cell = fe->dofs_per_cell;
 
     const unsigned int n_q_points      = quadrature_formula.size();
@@ -571,6 +587,22 @@ namespace Step81
     Vector<double>     cell_rhs(dofs_per_cell);
     std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
 
+    // This is assembling the interior of the domain on the left hand side.
+    // So we are assembling
+    // //\f{align*}{
+    // \int_\Omega (\mu_r^{-1}\nabla \times \varphi_i) \cdot
+    // (\nabla\times\bar{\varphi}_j)\text{d}x
+    // - \int_\Omega \varepsilon_r\varphi_i \cdot \bar{\varphi}_j\text{d}x
+    // \f}
+    // and
+    // \f{align}{
+    // i\int_\Omega J_a \cdot \bar{\varphi_i}\text{d}x
+    // - \int_\Omega \mu_r^{-1} \cdot (\nabla \times \bar{\varphi_i}) \text{d}x.
+    // \f}
+    // In doing so, we need test functions $\phi_i$ and $\phi_j$, and the curl
+    // of these test variables. We must be careful with the signs of the
+    // imaginary parts of these comples test variables. Moreover, we have a
+    // conditional that changes the parameters if the cell is in the PML region.
     for (const auto &cell : dof_handler.active_cell_iterators())
       {
         fe_values.reinit(cell);
@@ -587,9 +619,9 @@ namespace Step81
 
         for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
           {
-            const Point<dim, double> &position = quadrature_points[q_point];
-            const auto                radius   = position.norm();
-            const auto inner_radius = perfectly_matched_layer.inner_radius;
+            const Point<dim> &position = quadrature_points[q_point];
+            const auto        radius   = position.norm();
+            const auto inner_radius    = perfectly_matched_layer.inner_radius;
 
             auto       mu_inv  = parameters.mu_inv(position, id);
             auto       epsilon = parameters.epsilon(position, id);
@@ -599,7 +631,7 @@ namespace Step81
               {
                 auto A = perfectly_matched_layer.a_matrix(position);
                 auto B = perfectly_matched_layer.b_matrix(position);
-                auto d = perfectly_matched_layer.d_tensor(position);
+                auto d = perfectly_matched_layer.d(position);
 
                 mu_inv  = mu_inv / d;
                 epsilon = invert(A) * epsilon * invert(B);
@@ -632,13 +664,23 @@ namespace Step81
               }
           }
 
+        // Now we assemble the face and the boundary. The following loops will
+        // assemble
+        // //\f{align*}{
+        // - i\int_\Sigma (\sigma_r^{\Sigma}(\varphi_i)_T) \cdot
+        // (\bar{\varphi}_j)_T\text{do}x \f} and \f{align}{
+        //  - i\int_{\partial\Omega} (\sqrt{\mu_r^{-1}\varepsilon}(\varphi_i)_T)
+        //  \cdot (\nabla\times(\bar{\varphi}_j)_T)\text{d}x,
+        // \f}
+        // respectively. The test variables and the PML are implemented
+        // similarly as the domain.
         for (const auto &face : cell->face_iterators())
           {
             if (face->at_boundary())
               {
                 const auto id = face->boundary_id();
-                if (id !=0)
-                {
+                if (id != 0)
+                  {
                     fe_face_values.reinit(cell, face);
                     FEValuesViews::Vector<dim> real_part(fe_face_values, 0);
                     FEValuesViews::Vector<dim> imag_part(fe_face_values, dim);
@@ -646,10 +688,9 @@ namespace Step81
                     for (unsigned int q_point = 0; q_point < n_face_q_points;
                          ++q_point)
                       {
-                        const Point<dim, double> position =
-                          quadrature_points[q_point];
-                        const auto radius = position.norm();
-                        const auto inner_radius =
+                        const auto &position = quadrature_points[q_point];
+                        const auto  radius   = position.norm();
+                        const auto  inner_radius =
                           perfectly_matched_layer.inner_radius;
 
                         auto mu_inv  = parameters.mu_inv(position, id);
@@ -659,18 +700,20 @@ namespace Step81
                           {
                             auto A = perfectly_matched_layer.a_matrix(position);
                             auto B = perfectly_matched_layer.b_matrix(position);
-                            auto d = perfectly_matched_layer.d_tensor(position);
+                            auto d = perfectly_matched_layer.d(position);
 
                             mu_inv  = mu_inv / d;
                             epsilon = invert(A) * epsilon * invert(B);
                           };
 
-                        const auto normal = fe_face_values.normal_vector(q_point);
+                        const auto normal =
+                          fe_face_values.normal_vector(q_point);
 
                         for (unsigned int i = 0; i < dofs_per_cell; ++i)
                           {
-                            const auto phi_i = real_part.value(i, q_point) -
-                                               1.0i * imag_part.value(i, q_point);
+                            const auto phi_i =
+                              real_part.value(i, q_point) -
+                              1.0i * imag_part.value(i, q_point);
                             const auto phi_i_T = tangential_part(phi_i, normal);
 
                             for (unsigned int j = 0; j < dofs_per_cell; ++j)
@@ -686,13 +729,13 @@ namespace Step81
                                 const auto sqrt_prod = prod;
 
                                 const auto temp =
-                                  -1.0i *
-                                  scalar_product((sqrt_prod * phi_j_T), phi_i_T);
+                                  -1.0i * scalar_product((sqrt_prod * phi_j_T),
+                                                         phi_i_T);
                                 cell_matrix(i, j) += temp.real();
                               } /* j */
-                          }   /* i */
-                        }  /* q_point */
-                    }
+                          }     /* i */
+                      }         /* q_point */
+                  }
               }
             else
               {
@@ -712,10 +755,9 @@ namespace Step81
                 for (unsigned int q_point = 0; q_point < n_face_q_points;
                      ++q_point)
                   {
-                    const Point<dim, double> position =
-                      quadrature_points[q_point];
-                    const auto radius = position.norm();
-                    const auto inner_radius =
+                    const auto &position = quadrature_points[q_point];
+                    const auto  radius   = position.norm();
+                    const auto  inner_radius =
                       perfectly_matched_layer.inner_radius;
 
                     auto sigma = parameters.sigma(position, id1, id2);
@@ -757,7 +799,7 @@ namespace Step81
         constraints.distribute_local_to_global(
           cell_matrix, cell_rhs, local_dof_indices, system_matrix, system_rhs);
       }
-  };
+  }
 
   // We use a direct solver from the SparseDirectUMFPACK to solve the system
   template <int dim>
@@ -768,17 +810,18 @@ namespace Step81
     A_direct.vmult(solution, system_rhs);
   }
 
-// The output is written into a vtk file with 4 components
-template <int dim>
-void Maxwell<dim>::output_results()
-{
+  // The output is written into a vtk file with 4 components
+  template <int dim>
+  void Maxwell<dim>::output_results()
+  {
     DataOut<2> data_out;
     data_out.attach_dof_handler(dof_handler);
-    data_out.add_data_vector(solution, {"real_Ex", "real_Ey", "imag_Ex", "imag_Ey"});
+    data_out.add_data_vector(solution,
+                             {"real_Ex", "real_Ey", "imag_Ex", "imag_Ey"});
     data_out.build_patches();
     std::ofstream output("solution.vtk");
     data_out.write_vtk(output);
-}
+  }
 
 
   template <int dim>
@@ -793,8 +836,8 @@ void Maxwell<dim>::output_results()
 
 } // namespace Step81
 
-// The following main function calls the class step-81(), initializes the ParameterAcceptor,
-// and calls the run() function.
+// The following main function calls the class step-81(), initializes the
+// ParameterAcceptor, and calls the run() function.
 
 int main()
 {

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.