In this third mini post, I will tackle the weak formulation of the linear mild-slope equation and introduce the boundary conditions. The linear version of the equation takes into account the following physical phenomena:

Here is the equatiom:

where

  • \(\eta : \Omega(\mathbb{R} \times \mathbb{R}) \to \mathbb{C}\) is the unknown complex amplitude of the water free-surface elevation within the domain \(\Omega\);
  • \(k \in \mathbb{R}^{+}\) is the wave number;
  • \(c_p, c_g : \Omega(\mathbb{R} \times \mathbb{R}) \to \mathbb{R}^{+}\) are respectively the phase and group velocities of the prescribed wave.

The boundary conditions will be introduced too.

I won’t discuss the derivation of the governing equation in this post.

If everything goes smoothly, at the end of that post, we should see how a monochromatic wave behaves in the Lyttelton port.

wave

Bathymetry

First thing we need to do is to gather all the data required: we already retrieved the coastline from the OSM database but we’re missing the bathymetry as both the phase speed \(c_p\) and the group speed \(c_g\) depend on it. Luckily Land Information New Zealand has a wonderful platform where you can access a lot of information, including nautical charts:

The second chart is a set of depth polygons and can be downloaded from LINZ as an Esri shapefile.

Then we need to lookup the depth at either each vertex or each triangle of the mesh we generated in the first part. I will consider that the depth is constant over a triangle in order to make the maths easier later on (in particular integration).

To lookup efficiently, I’m first building a spatial index of the depth polygons (I’m using a R-tree) and then query every mesh vertices. Here is how to do that in Perl:

 1 #! /usr/bin/perl
 2 use warnings;
 3 use strict;
 4 
 5 use Geo::ShapeFile;
 6 use Math::Trig 'pi';
 7 use List::Util 'min' ,'max';
 8 
 9 my $shapefile = new Geo::ShapeFile("depth-area-polygons-hydro-14k-122k");
10 $shapefile->build_spatial_index();
11 
12 my $rtree = $shapefile->get_spatial_index();
13 
14 my $d = 0.00955042966330666;
15 my $xmin = 250.816739768889;
16 my $ymin = 162.523014276134;
17 my $earthRadius = 6378137;
18 
19 my @vertices;
20 
21 open my $mesh, "< ../lyttelton.mesh";
22 while (<$mesh>) {
23 	last if /Vertices/;
24 }
25 my $nbVertex = <$mesh>;
26 foreach my $vid (1..$nbVertex) {
27 	<$mesh> =~ /^([-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)\s+([-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)\s+\d+$/;
28 
29 	my $x = $d * $1 + $xmin;
30 	my $y = $d * $2 + $ymin;
31  		
32 	# From Google mercator convertion
33     $x = 2 * pi * $earthRadius * ($x / 256 - 0.5);
34     $y = -2 * pi * $earthRadius * ($y / 256 - 0.5);
35 
36     my @shapes;
37     $rtree->query_point(($x, $y),\@shapes);
38 
39 	my $z = 12;
40 	foreach my $shape (@shapes) {
41 		if ($shape->contains_point(Geo::ShapeFile::Point->new(X => $x, Y => $y))) {
42 			my %db = $shapefile->get_dbf_record($shape->shape_id());
43 			$z = $db{'DRVAL2'};
44 			last;
45 		}
46 	}
47 	push @vertices, [$x, $y, $z];
48 }
49 
50 while (<$mesh>) {
51 	last if /Triangles/;
52 }
53 my $nbTriangle = <$mesh>;
54 foreach my $vid (1..$nbTriangle) {
55 	<$mesh> =~ /^(\d+)\s+(\d+)\s+(\d+)\s+\d+$/;
56 
57 	my $v1 = $1 - 1;
58 	my $v2 = $2 - 1;
59 	my $v3 = $3 - 1;
60 
61 	print min($vertices[$v1]->[2], $vertices[$v2]->[2], $vertices[$v3]->[2]) ."\n";
62 }

Ultimately we can display the depth (red is ~12m deep):


View Larger Map

As you can see there are some artifacts close to the border due to differences in the port geometry between the OSM and LINZ datasets. I will write a tool to fix that but it will be later.

Boundary conditions

Weak formulation

We follow the exact same steps as in the “FEM for dummies” section of the second Schur complement method post, that is, start from the governing equation, write it in the sense of distribution and apply the Green-Ostrogradsky theorem when needed. Again, I will skip all the functional analysis details regarding functional spaces. If you’re interested there is this wonderful course.

Let \((e_{i})_{1 \leq i \leq N}\) a basis of the finite dimensional \(\mathbb{C}\)-vector space of the \(\Omega(\mathbb{R} \times \mathbb{R}) \to \mathbb{C}\) piecewise linear functions over our mesh \((T_{i})_{1 \leq i \leq N}\). Let’s remark that we can choose \((e_{i})\) to be real valued tent functions so that we’re not going to compute integrals over complex valued functions.

Interior

The contribution of the first (interior) integral to the discrete operator being built is easy to compute as it has already been encountered in the second Schur complement method post.

Given \(T_{i} = (q_{i_{1}},q_{i_{2}},q_{i_{3}})\), it goes:

The contribution of the third (interior) integral goes:

(We used a different 2D quadrature formula, exact for second degree polynomials.)

Border

In order to cope with the second (boundary) integral, we need to describe what’s happening near the boundary and give an expression of \(\partial \eta / \partial n\restriction_{\Omega}\). Let \(\Gamma_{C}\) and \(\Gamma_{O}\) respectively the closed and open boundaries. Then:

Closed boundary

Expression of the complex amplitude normal derivative along the closed boundary

Close to \(\Gamma_{C}\), we decompose the unknown complex amplitude \(\eta\) as the sum of two complex amplitudes:

  • the amplitude of an unknown incident wave;
  • the amplitude of its reflection

Here is a sketch of what’s happening:

closed boundary sketch

Ultimately we end up with the following expression:

with \(\rho\) a phase shift and \(0 \leq R \leq 1\) a reflection coefficient.

We’re facing a technical difficulty as we don’t know \(\theta\), the incoming wave angle. The only unknown we had so far was \(\eta\), the complex amplitude of the free-surface elevation. We need to find a linear relationship between \(\cos(\theta)\) and \(\eta\). Here is how to find one:

  • Start with \(\cos(\theta) = \sqrt{1 - \sin^2(\theta)}\)
  • Use the first order Maclaurin serie of \(\sqrt{1 - x}\):
  • Remark that
  • So that

and … voila:

Since I’m implementing a baby simulator, I say that the phase shift \(\rho\) is zero.

Computing the closed boundary line integral

Now that we have an expression of \(\partial \eta / \partial n\restriction_{\Gamma_{C}}\) we can evaluate

Using Simpson’s rule we get

Remembering that \(\nabla e_{i_{j} }, 1 \leq j \leq 2\) is constant along \(\Gamma_{C_{i}}\)

So that we can calculate

We only get a contribution from \( \left[ e_{i_{j}} \frac{\partial e_{i_{k}}}{\partial s} \right]_{\Gamma_{C_{i}}}\) at each end of the closed boundary so that I’m going to neglect it.

Open boundary

Expression of the complex amplitude normal derivative along the open boundary

Close to \(\Gamma_{O}\), we decompose the unknown complex amplitude \(\eta\) as the sum of two complex amplitudes:

  • the amplitude \(\eta_{in}\) of a wave crossing \(\Gamma_{O}\) towards the shore;
  • the amplitude \(\eta_{out}\) of a wave crossing \(\Gamma_{O}\) away from the shore

Here is the sketch:

open boundary sketch

\(\eta_{in}\) is given, so that computing it’s normal derivative along the open boundary is direct:

We don’t know \(\theta\), the outgoing wave angle but we can reuse the approximation crafted in the previous section with \(R = 0\) (no reflection) and then write back \(\eta_{out}\) as the difference between \(\eta\) and \(\eta_{in}\):

We end up with:

The first term contributes to the left hand side of the linear system. The second term, involving the incoming prescribed wave, contributes to the right hand side of the linear system.

Computing the open boundary line integral

Since the first term of the normal derivative expression along the open boundary differs from the “closed” one by a \(\frac{1-R}{1 + R}\) prefactor, I will only cover the computation of the second term, the contribution to the right hand side of the linear system:

Again, using Simpson’s rule we get

And finally,

Again, we neglect the \(\left[ e_{i_{j}} \frac{\partial \eta_{in}}{\partial s} \right]_{\Gamma_{O_{i}}}\) term : there only is a contribution at both ends of the open boundary.

Conclusion

We’ve been through the tedious process of computing by hand all we needed to built a linear system and solve the PDE using the distributed hybrid CPU/GPU solver introduced in the previous post. Note that this time we cope with complex numbers so that the code had to be modified a little bit.

The assembly code for the mild-slope equation can be found in:

Group and phase speed, dispersion relation.

Ultimately, we will give an expression of \(c_p\) the phase speed,\(c_g\) the group speed as well as the product \(c_p c_g\) which appears everywhere in the previously calculated expressions.

These expressions depend on the depth \(h\) and we will finaly use the bathymetry information we gathered in the first part.

The Wikipedia article tells us:

The phase and group speed depend on the dispersion relation, and are derived from Airy wave theory as:

where \(g\) is Earth’s gravity and \(\tanh\) is the hyperbolic tangent.

It comes

Conclusion and references

Here is the solution to the equation in the Lyttleton harbor for the following input:

  • 3m wave length
  • 45 degrees
  • harbor reflection coeff 0.9


View Larger Map

My main references were:

The next time I will modify Blender to use the solutions of my wave simulator in an attempt to render realistic coastal scenes. The workflow should be similar to the Ocean Modifier one.



blog comments powered by Disqus

Published

12 April 2014

Category

Numerical methods

Tags