<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://flow-physics.github.io//feed.xml" rel="self" type="application/atom+xml" /><link href="https://flow-physics.github.io//" rel="alternate" type="text/html" /><updated>2024-04-07T18:01:20+00:00</updated><id>https://flow-physics.github.io//feed.xml</id><title type="html">Flow Physics</title><subtitle>An engineer’s workbench for scientific computing, fluid mechanics and mathematical physics</subtitle><entry><title type="html">Numpy arrays in Pydantic</title><link href="https://flow-physics.github.io//2024/02/12/numpy-arrays-in-pydantic.html" rel="alternate" type="text/html" title="Numpy arrays in Pydantic" /><published>2024-02-12T00:00:00+00:00</published><updated>2024-02-12T00:00:00+00:00</updated><id>https://flow-physics.github.io//2024/02/12/numpy-arrays-in-pydantic</id><content type="html" xml:base="https://flow-physics.github.io//2024/02/12/numpy-arrays-in-pydantic.html"><![CDATA[<p>Recently, I have been developing a <a href="https://fastapi.tiangolo.com/" target="_blank" rel="noopener">FastAPI</a> application which relies on the excellent <a href="https://docs.pydantic.dev/latest/" target="_blank" rel="noopener">Pydantic</a> package for data validation and serialization.</p>

<p>I will give a very short intro, in case you are not familiar with Pydantic. If we develop our Python class objects as derived from Pydantic’s BaseModel class, then we can have very helpful things like type validation, type hinting, JSON data serialization and <a href="https://docs.pydantic.dev/latest/#why-use-pydantic" target="_blank" rel="noopener">so on</a> for our class.  e.g.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>

<span class="k">class</span> <span class="nc">Book</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
    <span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>  
    <span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">author</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">subject</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>

<span class="n">book_data</span> <span class="o">=</span> <span class="p">{</span><span class="s">'id'</span><span class="p">:</span> <span class="mi">12</span><span class="p">,</span>
             <span class="s">'name'</span><span class="p">:</span> <span class="s">"What is Life?"</span><span class="p">,</span>
             <span class="s">'author'</span><span class="p">:</span> <span class="s">"Schrodinger, Erwin"</span><span class="p">,</span>
             <span class="s">'subject'</span><span class="p">:</span> <span class="p">[</span><span class="s">"physics"</span><span class="p">,</span> <span class="s">"biology"</span><span class="p">]}</span>
<span class="n">my_book</span> <span class="o">=</span> <span class="n">Book</span><span class="p">(</span><span class="o">**</span><span class="n">book_data</span><span class="p">)</span>  

<span class="k">print</span><span class="p">(</span><span class="n">my_book</span><span class="p">.</span><span class="nb">id</span><span class="p">)</span>  
<span class="c1">#&gt; 12
</span><span class="k">print</span><span class="p">(</span><span class="n">my_book</span><span class="p">.</span><span class="n">model_dump</span><span class="p">())</span>  
<span class="s">"""
{
    'id': 12,
    'name': "What is Life?",
    'author': "Schrodinger, Erwin",
    'subject': ["physics", "biology"]
}
"""</span></code></pre></figure>

<p>And this plays nicely with <a href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping" target="_blank" rel="noopener">ORM</a> as well.</p>

<p>If you are dealing with scientific or numerical data in Python, naturally you will use Numpy arrays. But how to handle Numpy arrays within Pydantic BaseModel?</p>

<h3 id="numpy-array-as-an-annotated-type">Numpy array as an ‘Annotated’ type</h3>

<p>We can define a custom type for our Numpy arrays using the <code class="language-plaintext highlighter-rouge">Annotated</code> type. This will wrap around Numpy’s original <a href="https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html" target="_blank" rel="noopener"><code class="language-plaintext highlighter-rouge">ndarray</code></a> class. But we need to provide two additional things:</p>

<ol>
  <li>A function to convert a provided string input to Numpy array - a before validation method</li>
  <li>A function to serialize a provided Numpy array into List/string - a custom serialization method</li>
</ol>

<p>Sample code for this custom datatype <code class="language-plaintext highlighter-rouge">MyNumPyArray</code> creation is given below:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</span><span class="p">,</span> <span class="n">BeforeValidator</span><span class="p">,</span> <span class="n">PlainSerializer</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Annotated</span>
<span class="kn">import</span> <span class="nn">ast</span>

<span class="k">def</span> <span class="nf">nd_array_before_validator</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="c1"># custom before validation logic
</span>    <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
        <span class="n">x_list</span> <span class="o">=</span> <span class="n">ast</span><span class="p">.</span><span class="n">literal_eval</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">x_list</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">List</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">x</span>

<span class="k">def</span> <span class="nf">nd_array_serializer</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="c1"># custom serialization logic
</span>    <span class="k">return</span> <span class="n">x</span><span class="p">.</span><span class="n">tolist</span><span class="p">()</span>
    <span class="c1"># return np.array2string(x,separator=',', threshold=sys.maxsize)
</span>
<span class="n">MyNumPyArray</span> <span class="o">=</span> <span class="n">Annotated</span><span class="p">[</span>   <span class="n">np</span><span class="p">.</span><span class="n">ndarray</span><span class="p">,</span>
                            <span class="n">BeforeValidator</span><span class="p">(</span><span class="n">nd_array_before_validator</span><span class="p">),</span>
                            <span class="n">PlainSerializer</span><span class="p">(</span><span class="n">nd_array_serializer</span><span class="p">,</span> <span class="n">return_type</span><span class="o">=</span><span class="n">List</span><span class="p">),</span>
                        <span class="p">]</span>

<span class="c1"># Remember to add 'model_config = ConfigDict(arbitrary_types_allowed=True)' to the model class when using MyNumPyArray</span></code></pre></figure>

<p>Now, you can include <code class="language-plaintext highlighter-rouge">numpy</code> arrays in Pydantic classes as given below:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">ConfigDict</span>

<span class="k">class</span> <span class="nc">SomeClass</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
    <span class="s">"""
        Sample class that has a Numpy array field
    """</span>
    <span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">data</span><span class="p">:</span> <span class="n">MyNumPyArray</span>

    <span class="n">model_config</span> <span class="o">=</span> <span class="n">ConfigDict</span><span class="p">(</span><span class="n">arbitrary_types_allowed</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>


<span class="c1"># Testing
</span><span class="n">sample_data</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">],[</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">]])</span>
<span class="n">test_instance</span> <span class="o">=</span> <span class="n">SomeClass</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"Test"</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">sample_data</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">test_instance</span><span class="p">.</span><span class="n">model_dump</span><span class="p">())</span>
<span class="c1">#&gt; {'name': 'Test', 'data': [[1,2], [3,4]]}</span></code></pre></figure>

<h3 id="references-and-notes">References and notes:</h3>

<ol>
  <li><a href="https://github.com/pydantic/pydantic/issues/7017#issuecomment-1670142686" target="_blank" rel="noopener">https://github.com/pydantic/pydantic/issues/7017#issuecomment-1670142686</a></li>
  <li>You can use <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" target="_blank" rel="noopener"><code class="language-plaintext highlighter-rouge">pydantic.dataclasses</code></a> if you only require data validation from Pydantic for Numpy arrays. See, for example, <a href="https://stackoverflow.com/questions/70306311/pydantic-initialize-numpy-ndarray" target="_blank" rel="noopener">https://stackoverflow.com/questions/70306311/pydantic-initialize-numpy-ndarray</a></li>
</ol>]]></content><author><name>Rajesh Venkatesan</name></author><category term="numpy" /><category term="arrays" /><category term="pydantic" /><category term="fastapi" /><summary type="html"><![CDATA[Recently, I have been developing a FastAPI application which relies on the excellent Pydantic package for data validation and serialization.]]></summary></entry><entry><title type="html">Reading data in parallel using MPI I/O</title><link href="https://flow-physics.github.io//hpc/2022/10/17/reading-data-in-parallel-using-mpi-i-o.html" rel="alternate" type="text/html" title="Reading data in parallel using MPI I/O" /><published>2022-10-17T10:16:20+00:00</published><updated>2022-10-17T10:16:20+00:00</updated><id>https://flow-physics.github.io//hpc/2022/10/17/reading-data-in-parallel-using-mpi-i-o</id><content type="html" xml:base="https://flow-physics.github.io//hpc/2022/10/17/reading-data-in-parallel-using-mpi-i-o.html"><![CDATA[<p>In the previous post “<a href="http://flowphysics.com/first-steps-in-parallel-file-access-using-mpi-i-o/">First steps in parallel file access using MPI I/O</a>”, we discussed how to write some simple data in parallel using MPI I/O. Here we shall read back that data in parallel. The entire code is given in my GitHub repo <a href="https://github.com/rajesh-ae/MPI_Notes/blob/main/MPI_IO/read_char_parallel.c">MPI_Notes</a>. Essentially, we have the following text data in a file:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">abcdefghijklmnopqrstuvwxyz</code></pre></figure>

<p>We will try to read this using 6 MPI processes. And we want each process to read a part of this data as shown below: (this is the same partitioning used when we wrote the data)</p>

<p><img src="/assets/img/2022/10/read_data-1.png" alt="" /></p>

<p>This partitioning is arbitrary and we could have chosen different ways to divide up the data among processes to read. Similar to the writing process, there are three approaches in MPI I/O which can be used to read this data:</p>

<ol>
  <li>Using explicit offsets</li>
  <li>Using individual file pointers</li>
  <li>Using shared file pointers</li>
</ol>

<h2 id="using-explicit-offsets">Using explicit offsets</h2>

<p>As the name suggests, we need to calculate where each process should start reading its data (‘<em>offset</em>’) and the length of the data to be read in by that process (‘<em>count</em>’). So, <em>proc 0</em> should read at the beginning of the file (3 characters to be read), <em>proc 1</em> should start writing after 3 characters (5 characters to be read), <em>proc 2</em> should start reading after 8 characters (3 characters to be read) and so on. This can be done by the following piece of code:</p>

<figure class="highlight"><pre><code class="language-fortran" data-lang="fortran"><span class="n">MPI_File_open</span><span class="p">(</span><span class="n">MPI_COMM_WORLD</span><span class="p">,</span><span class="w"> </span><span class="s2">"file_exp_offset.dat"</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_MODE_RDONLY</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_INFO_NULL</span><span class="p">,</span><span class="w"> </span><span class="p">&amp;</span><span class="n">file_handle</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_File_read_at_all</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span><span class="w"> </span><span class="n">disp</span><span class="p">,</span><span class="w"> </span><span class="n">test_txt</span><span class="p">,</span><span class="n">arr_len_local</span><span class="p">,</span><span class="n">MPI_CHAR</span><span class="p">,</span><span class="n">MPI_STATUS_IGNORE</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_File_close</span><span class="p">(&amp;</span><span class="n">file_handle</span><span class="p">);</span></code></pre></figure>

<p>‘disp’ denotes the offset calculated and ‘arr_len_local’ is the count (length) of the data to read in. Once the data is read in, the MPI processes will have the following data in their ‘<em>test_txt</em>’ array:</p>

<p><img src="/assets/img/2022/10/data-2.png" alt="" /></p>

<p>While the explicit offset approach seems quite straightforward, it is preferable to use the individual file pointer method for more complex datasets and partitioning. This is discussed next.</p>

<h2 id="using-individual-file-pointers">Using individual file pointers</h2>

<p>Since we have explained the individual file pointer method in the previous <a href="http://flowphysics.com/first-steps-in-parallel-file-access-using-mpi-i-o/">post</a>, we shall only outline the approach here. Instead of calculating individual file pointer locations manually, we define a new global datatype to represent the data partitioning. This is done by:</p>

<figure class="highlight"><pre><code class="language-fortran" data-lang="fortran"><span class="n">MPI_Type_create_subarray</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="p">&amp;</span><span class="n">total_len</span><span class="p">,</span><span class="w"> </span><span class="p">&amp;</span><span class="n">arr_len_local</span><span class="p">,</span><span class="w"> </span><span class="p">&amp;</span><span class="n">disp</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_ORDER_C</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_CHAR</span><span class="p">,</span><span class="w"> </span><span class="p">&amp;</span><span class="n">char_array_mpi</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_Type_commit</span><span class="p">(&amp;</span><span class="n">char_array_mpi</span><span class="p">);</span></code></pre></figure>

<p>Once the global datatype is created like this, reading the data in parallel is very simple as shown below:</p>

<figure class="highlight"><pre><code class="language-fortran" data-lang="fortran"><span class="n">MPI_File_open</span><span class="p">(</span><span class="n">MPI_COMM_WORLD</span><span class="p">,</span><span class="w"> </span><span class="s2">"file_ind_ptr.dat"</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_MODE_RDONLY</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_INFO_NULL</span><span class="p">,</span><span class="w"> </span><span class="p">&amp;</span><span class="n">file_handle</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_File_set_view</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_CHAR</span><span class="p">,</span><span class="w"> </span><span class="n">char_array_mpi</span><span class="p">,</span><span class="s2">"native"</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_INFO_NULL</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_File_read_all</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span><span class="w"> </span><span class="n">test_txt</span><span class="p">,</span><span class="w"> </span><span class="n">arr_len_local</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_CHAR</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_STATUS_IGNORE</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_File_close</span><span class="p">(&amp;</span><span class="n">file_handle</span><span class="p">);</span></code></pre></figure>

<h2 id="using-shared-file-pointers">Using shared file pointers</h2>

<p>In the shared file pointer approach, we specify simply the ‘count’ of the data to be read by each process and let MPI calculate the offsets for us. This can be achieved by the following code snippet:</p>

<figure class="highlight"><pre><code class="language-fortran" data-lang="fortran"><span class="n">MPI_File_open</span><span class="p">(</span><span class="n">MPI_COMM_WORLD</span><span class="p">,</span><span class="w"> </span><span class="s2">"file_shr_ptr.dat"</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_MODE_RDONLY</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_INFO_NULL</span><span class="p">,</span><span class="w"> </span><span class="p">&amp;</span><span class="n">file_handle</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_File_read_ordered</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span><span class="w"> </span><span class="n">test_txt</span><span class="p">,</span><span class="w"> </span><span class="n">arr_len_local</span><span class="p">,</span><span class="w"> </span><span class="n">MPI_CHAR</span><span class="p">,</span><span class="n">MPI_STATUS_IGNORE</span><span class="p">);</span><span class="w">
</span><span class="n">MPI_File_close</span><span class="p">(&amp;</span><span class="n">file_handle</span><span class="p">);</span></code></pre></figure>

<p>Of course, this will come with a performance penalty as the shared file pointer is synchronized internally by MPI. The approach to use for reading/writing should be tested and its performance evaluated before deploying for production use in a software.</p>

<p>For demonstration purposes, we have chosen to read/write some character array in the examples here. We shall look into writing/reading general numerical data in some complex data partitioning in the next articles in this series on MPI I/O.</p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="HPC" /><category term="mpi" /><category term="MPI I/O" /><category term="Parallel Computing" /><category term="parallel file access" /><category term="parallel I/O" /><summary type="html"><![CDATA[In the previous post “First steps in parallel file access using MPI I/O”, we discussed how to write some simple data in parallel using MPI I/O. Here we shall read back that data in parallel. The entire code is given in my GitHub repo MPI_Notes. Essentially, we have the following text data in a file:]]></summary></entry><entry><title type="html">First steps in parallel file access using MPI I/O</title><link href="https://flow-physics.github.io//hpc/2022/10/13/first-steps-in-parallel-file-access-using-mpi-i-o.html" rel="alternate" type="text/html" title="First steps in parallel file access using MPI I/O" /><published>2022-10-13T15:08:53+00:00</published><updated>2022-10-13T15:08:53+00:00</updated><id>https://flow-physics.github.io//hpc/2022/10/13/first-steps-in-parallel-file-access-using-mpi-i-o</id><content type="html" xml:base="https://flow-physics.github.io//hpc/2022/10/13/first-steps-in-parallel-file-access-using-mpi-i-o.html"><![CDATA[<p>If your code uses MPI for parallelization, then you may want to use MPI I/O for writing and reading data from files in parallel. It is common to see the use of POSIX parallel approach where each MPI process reads/writes a separate file. This approach has the following drawbacks:</p>

<ul>
  <li>Not efficiently scalable for very high number of processes</li>
  <li>Creates a large clutter in the file system and almost unmanageable when thousands of processes are used</li>
</ul>

<p>MPI I/O fixes these problems and provides an elegant solution for MPI applications. It is very easy to read/write data in parallel using MPI I/O if you are familiar with the basics of MPI communications (<em>viz.</em> point-to-point, collective). In this article, I will show you how to do this with a simple example. The entire code (written in C) can be found in full in my Github repo <a href="https://github.com/rajesh-ae/MPI_Notes/tree/main/MPI_IO">MPI_Notes/MPI_IO</a>.</p>

<p>Parallel file writing can be thought of in the following way. Say, we have 10 people trying to write something to the same notebook. Instead of passing the notebook to them one by one, we want to tell everyone where they should start writing (e.g. the page number) their part so they can all write at the same time. Obviously, we don’t want any of them to overwrite what others have written. So we need to have the page numbers for each person calculated correctly based on:</p>

<ul>
  <li>How much would be written by others before the current person writes their part? This is called ‘displacement’</li>
  <li>How much we expect this person to write? This is the ‘length/count of the data’</li>
</ul>

<p>In the case of POSIX parallel I/O, we would simply be giving separate notebooks to each person and let them write whatever they want. So, we don’t have to calculate the above things. But then, it is a lot of notebooks to carry around! It may not be possible to distribute and collect back these many notebooks in a short time as well.</p>

<p>Let us assume that we have 6 MPI processes in our code. And each of them have some data in a character array as shown below:</p>

<p><img src="/assets/img/2022/10/data-2.png" alt="Data in individual MPI processes" /></p>

<p>We shall ask this data to be written to a single file. And we expect the final data to be in order of the ranks, thus listing all the alphabets in sequence. There are three ways to do this in MPI I/O:</p>

<ol>
  <li>Explicit offset</li>
  <li>Individual file pointers</li>
  <li>Shared file pointers</li>
</ol>

<h2 id="using-explicit-offsets">Using explicit offsets</h2>

<p>As the name implies, we simply calculate the location where each process needs to write. This is usually specified as an ‘offset’/’displacement’ from the beginning of the file. In our example, <em>proc 0</em> should write at the beginning of the file (after 0 characters), <em>proc 1</em> should start writing after 3 characters (that would be written by <em>proc 0</em>), <em>proc 2</em> should start writing after 8 characters (to take into account data from <em>proc 0</em> and <em>proc 1</em>) and so on. Clearly, every process needs to know only where it should start writing. Additionally, we also need to inform MPI about how much data would be written.</p>

<p>Both this information should be supplied in the units of the datatype we are writing. In this example, the data is made of characters (these are MPI_CHAR type). The following code snippet opens/creates a file for parallel access by MPI I/O and the writes the data using the explicit offset approach collectively: (Full code <a href="https://github.com/rajesh-ae/MPI_Notes/blob/main/MPI_IO/write_char_parallel.c">here</a>)</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">MPI_File_open</span><span class="p">(</span><span class="n">MPI_COMM_WORLD</span><span class="p">,</span> <span class="s">"file_exp_offset.dat"</span><span class="p">,</span> <span class="n">MPI_MODE_CREATE</span><span class="o">|</span><span class="n">MPI_MODE_WRONLY</span><span class="p">,</span> <span class="n">MPI_INFO_NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">file_handle</span><span class="p">);</span>
<span class="n">MPI_File_write_at_all</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span> <span class="n">displacement</span><span class="p">,</span> <span class="n">test_txt</span><span class="p">,</span> <span class="n">arr_len_local</span><span class="p">,</span> <span class="n">MPI_CHAR</span><span class="p">,</span> <span class="n">MPI_STATUS_IGNORE</span><span class="p">);</span>
<span class="n">MPI_File_close</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file_handle</span><span class="p">);</span></code></pre></figure>

<p>Once we run the program with 6 MPI processes, we will have the file “file_exp_offset.dat” written to disk. As we have written character data, we can view the contents of this file in any text editor. The file contains the following:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">abcdefghijklmnopqrstuvwxyz</code></pre></figure>

<h2 id="using-individual-file-pointers">Using individual file pointers</h2>

<p>When a file is opened using MPI_File_open() command, MPI creates an individual file pointer for each MPI process so that it tracks the position in file of that process. It is very similar to the file pointer in C, for example, but maintained for each MPI process separately. Note that this pointer could be in different position in the file for different processes. Using this individual file pointer, it is possible to read/write data conveniently to file in parallel. While it is possible to set the individual pointers manually per process, the most common way is to inform MPI about the global view of the data we are planning to write. Let me explain.</p>

<p>In our example, this can be achieved by:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// Create a MPI datatype for the global array</span>
<span class="n">MPI_Type_create_subarray</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">total_len</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">arr_len_local</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">disp</span><span class="p">,</span> <span class="n">MPI_ORDER_C</span><span class="p">,</span> <span class="n">MPI_CHAR</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">char_array_mpi</span><span class="p">);</span>
<span class="n">MPI_Type_commit</span><span class="p">(</span><span class="o">&amp;</span><span class="n">char_array_mpi</span><span class="p">);</span></code></pre></figure>

<p>The above snippet creates a MPI datatype called ‘char_array_mpi’ which describes the global view of the data. ‘total_len’ is the total length of global data, ‘arr_len_local’ is the length of the data in the current process and ‘disp’ gives the displacement of the local data in the global array that we described earlier.</p>

<p>Once the global datatype is created, we inform MPI I/O by setting a ‘File View’ as follows:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">MPI_File_set_view</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MPI_CHAR</span><span class="p">,</span> <span class="n">char_array_mpi</span><span class="p">,</span><span class="s">"native"</span><span class="p">,</span> <span class="n">MPI_INFO_NULL</span><span class="p">);</span></code></pre></figure>

<p>‘File View’ essentially changes the way the file is accessed. MPI now knows that our read/write operations are towards reading/writing data of type ‘char_array_mpi’ and each individual process will contain only the local data for this datatype. It also additionally knows that every element we shall write is of type MPI_CHAR. This command also moves the individual (and shared) file pointer positions to the correct locations as per the global datatype (and redefines them as zero position of those pointers).</p>

<p>After setting the ‘File View’, we can write our character arrays to file using:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">MPI_File_write_all</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span> <span class="n">test_txt</span><span class="p">,</span> <span class="n">arr_len_local</span><span class="p">,</span> <span class="n">MPI_CHAR</span><span class="p">,</span> <span class="n">MPI_STATUS_IGNORE</span><span class="p">);</span></code></pre></figure>

<p>The complete code snippet is:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">MPI_File_open</span><span class="p">(</span><span class="n">MPI_COMM_WORLD</span><span class="p">,</span> <span class="s">"file_ind_ptr.dat"</span><span class="p">,</span> <span class="n">MPI_MODE_CREATE</span><span class="o">|</span><span class="n">MPI_MODE_WRONLY</span><span class="p">,</span> <span class="n">MPI_INFO_NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">file_handle</span><span class="p">);</span>
<span class="n">MPI_File_set_view</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MPI_CHAR</span><span class="p">,</span> <span class="n">char_array_mpi</span><span class="p">,</span><span class="s">"native"</span><span class="p">,</span> <span class="n">MPI_INFO_NULL</span><span class="p">);</span>
<span class="n">MPI_File_write_all</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span> <span class="n">test_txt</span><span class="p">,</span> <span class="n">arr_len_local</span><span class="p">,</span> <span class="n">MPI_CHAR</span><span class="p">,</span> <span class="n">MPI_STATUS_IGNORE</span><span class="p">);</span>
<span class="n">MPI_File_close</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file_handle</span><span class="p">);</span></code></pre></figure>

<p>While this may seem a bit tedious, this approach can be used to write more complex datatypes and partitioning commonly used in MPI applications. Also, note that we can repeatedly write the same datatype again if needed without much additional effort. If we want to read/write a different type of data, we need to change the ‘File View’.</p>

<h2 id="using-shared-file-pointers">Using shared file pointers</h2>

<p>One important point to note is that individual file pointers are what they claim to be, only ‘<em>individual</em>’. The processes do not know about how individual pointers of other processes move about. There are some situations where every MPI process needs to have a synchronized file pointer. For this purpose, MPI maintains a single shared file pointer for every MPI I/O file opened. When a read/write is done using this shared file pointer, every MPI process knows about a change in the shared file pointer position. For the sake of completeness, we shall demonstrate the collective writing of data using this approach in our example. The following code snippet writes using the shared file pointer:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">MPI_File_open</span><span class="p">(</span><span class="n">MPI_COMM_WORLD</span><span class="p">,</span> <span class="s">"file_shr_ptr.dat"</span><span class="p">,</span> <span class="n">MPI_MODE_CREATE</span><span class="o">|</span><span class="n">MPI_MODE_WRONLY</span><span class="p">,</span> <span class="n">MPI_INFO_NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">file_handle</span><span class="p">);</span>
<span class="n">MPI_File_write_ordered</span><span class="p">(</span><span class="n">file_handle</span><span class="p">,</span> <span class="n">test_txt</span><span class="p">,</span> <span class="n">arr_len_local</span><span class="p">,</span> <span class="n">MPI_CHAR</span><span class="p">,</span><span class="n">MPI_STATUS_IGNORE</span><span class="p">);</span>
<span class="n">MPI_File_close</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file_handle</span><span class="p">);</span></code></pre></figure>

<p>Note that the displacements are calculated on the fly by MPI in this case as we are using the shared file pointer (because of the call to ‘MPI_File_write_ordered’). But this will come with a performance penalty as well.</p>

<p>Please go through the entire code <a href="https://github.com/rajesh-ae/MPI_Notes/blob/main/MPI_IO/write_char_parallel.c">here</a> as it provides all the details. I have simplified a lot of things in this article but this is enough to get you started with MPI I/O. In the next article, we shall see how to read back the data in parallel.</p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="HPC" /><category term="mpi" /><category term="MPI I/O" /><category term="Parallel Computing" /><category term="parallel file access" /><category term="parallel I/O" /><summary type="html"><![CDATA[If your code uses MPI for parallelization, then you may want to use MPI I/O for writing and reading data from files in parallel. It is common to see the use of POSIX parallel approach where each MPI process reads/writes a separate file. This approach has the following drawbacks:]]></summary></entry><entry><title type="html">Parallel computing approaches for CFD</title><link href="https://flow-physics.github.io//hpc/2021/05/05/parallel-computing-approaches-for-cfd.html" rel="alternate" type="text/html" title="Parallel computing approaches for CFD" /><published>2021-05-05T18:09:52+00:00</published><updated>2021-05-05T18:09:52+00:00</updated><id>https://flow-physics.github.io//hpc/2021/05/05/parallel-computing-approaches-for-cfd</id><content type="html" xml:base="https://flow-physics.github.io//hpc/2021/05/05/parallel-computing-approaches-for-cfd.html"><![CDATA[<p>When it comes to parallel computing, there are many ways to solve any complex scientific computing problem. Some are more suitable than others. The method and approach should be determined on a case by case basis with an understanding of the computational algorithm and data structures involved. In this series of articles, I will describe the different parallel computing approaches I have used for computational fluid dynamics (CFD) simulations. These provide examples of widely used approaches and I hope that they will help the reader to parallelize their scientific computing programs. I assume that the reader is familiar with the basics of MPI and OpenMP models of parallel computing.</p>

<p>We shall look at the following approaches:</p>

<ol>
  <li>Slab decomposition with MPI
    * e.g. Direct Numerical Simulation (DNS), Poisson equation solver</li>
  <li>Pencil decomposition with MPI
    * e.g. DNS, Poisson equation solver</li>
  <li>Solving a large ODE system in parallel - using MPI, OpenMP, Hybrid MPI+OpenMP
    * e.g. vortex sheet evolution</li>
  <li>Graph partitioning with MPI
    * e.g. unstructured mesh CFD</li>
  <li>Coupling two MPI parallel applications</li>
</ol>

<p>The first case I will discuss is the ‘Slab decomposition’ strategy.</p>

<h2 id="slab-decomposition-with-mpi">Slab decomposition with MPI</h2>

<p>I used this approach in my Direct Numerical Simulation (DNS) code to simulate turbulent boundary layers. Essentially, we need to simulate the flow in a box as shown below.</p>

<p><img src="/assets/img/2021/05/domain.png" alt="" /></p>

<p>And the aim is to simulate this flow using as many processors as possible. I chose the MPI (the standard for distributed parallel computing) approach. The processors work independently in this model on their data and communicate with each other when they need to exchange data/information.</p>

<p>In this problem, the computation needs to be performed for every point (discretized) in the rectangular box. Fortunately, all the parts of the box have almost the <em>same amount of computation/workload</em>. There are special treatments required for the bounding surfaces for boundary conditions. But this is very minor compared to the majority of computational workload at individual points. So, it makes sense to divide the flow domain into equal parts and distribute the parts among processors. This is critical since if one of the processors is overworked, it would create a bottleneck for other processors continuing on with their calculation. Note that the computation is inter-dependent and exchange of data is necessary (to be discussed later). The act of making sure each process handles an equal share of the computation and no bottlenecks arise due to this division of workload is called “<em>load balancing</em>”. In this particular case of flow in a box, it so happens that dividing the flow domain equally would lead to equal workload for each process.</p>

<p>So, how does one go about dividing the box domain so that the processors work efficiently together? A well known approach for such simulations is the ‘slab decomposition’ shown below.</p>

<p><img src="/assets/img/2021/05/parallel.png" alt="" /></p>

<p>We split the entire domain into ‘<em>n</em>’ slices in the x-direction, just like cutting a loaf of bread. Each slice will be given to a processor/core. The grid and variable values associated with a given part is available only in the processor owning that part. Throughout the simulation, we expect to maintain this association.</p>

<p>If the slices are totally independent of each other and can proceed with their part of the calculation without depending on data from neighbouring slices, then the job is literally done. But this is not usually the case. The calculations in a slice will depend on neighbouring slices which reside in another processor’s control. Particularly, it would be necessary to access at least the data from points on the neighbouring slice’s surface. The idea of ‘<em>halo cells</em>’ is introduced for this purpose which stores the neighbour’s data (only the required part).  How this data communication is handled is very important as it will play a crucial role in overall speed of the simulation. It is possible in MPI to introduce what is known as “<em>Cartesian topology</em>”. This will help considerably in the ‘halo cell exchange’ of data from neighbouring processors.</p>

<p>Additionally, there may be parts of the simulation where it might be necessary to have the slab decomposition done in another direction instead of the original x-direction. For example, the slabs created in the z-direction is shown below.</p>

<p><img src="/assets/img/2021/05/z_slabs-2.png" alt="" /></p>

<p>To achieve this configuration, we have to ‘transpose’ the necessary data from the x-wise slabs to z-wise slabs. Lots of data exchange/communication is involved in this process. So, it would be wise to do this only when absolutely necessary. ‘<em>Transposition</em>’ should be done efficiently as well.</p>

<p><strong><em>(to be continued…)</em></strong></p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="HPC" /><category term="mpi" /><category term="OpenMP" /><category term="Parallel Computing" /><summary type="html"><![CDATA[When it comes to parallel computing, there are many ways to solve any complex scientific computing problem. Some are more suitable than others. The method and approach should be determined on a case by case basis with an understanding of the computational algorithm and data structures involved. In this series of articles, I will describe the different parallel computing approaches I have used for computational fluid dynamics (CFD) simulations. These provide examples of widely used approaches and I hope that they will help the reader to parallelize their scientific computing programs. I assume that the reader is familiar with the basics of MPI and OpenMP models of parallel computing.]]></summary></entry><entry><title type="html">An invitation to fluid dynamics from the oceans</title><link href="https://flow-physics.github.io//fluid%20dynamics/movies/2020/06/27/an-invitation-to-fluid-dynamics-from-the-oceans.html" rel="alternate" type="text/html" title="An invitation to fluid dynamics from the oceans" /><published>2020-06-27T11:01:44+00:00</published><updated>2020-06-27T11:01:44+00:00</updated><id>https://flow-physics.github.io//fluid%20dynamics/movies/2020/06/27/an-invitation-to-fluid-dynamics-from-the-oceans</id><content type="html" xml:base="https://flow-physics.github.io//fluid%20dynamics/movies/2020/06/27/an-invitation-to-fluid-dynamics-from-the-oceans.html"><![CDATA[<p>The last few weeks, I have been engrossed with the oceans and navigation in their vast expanse. It all started with a couple of movies I watched. The first movie “Kon-Tiki” beautifully portrays the 1947 expedition by Norwegian explorer and ethnographer <a href="https://en.wikipedia.org/wiki/Thor_Heyerdahl">Thor Heyerdahl</a> across the south pacific, from Peru to polynesia. The remarkable thing was that he achieved this (along with some of his friends) in a balsawood raft! He was trying to establish that Incan people were the first to populate the polynesian islands. This claim is still contested today. Nevertheless, this is an extraordinary feat. They were trying to catch the south equatorial current in their raft. This made me wonder how they were relying on its presence and persistence.</p>

<p>Another movie I watched was “Master and Commander: The Far Side of the World” by Peter Weir. It is about a british captain (played by Russell Crowe) of a small warship trying to maniacally battle a French warship all around South America in in the atlantic and pacific oceans at the turn of the 19th century (fictional story). The thing that most impressed me was that these were both <a href="https://en.wikipedia.org/wiki/Sailing_ship">sailing ships</a>.  Once again, I started wandering the internet about how the sailors were relying on <a href="https://en.wikipedia.org/wiki/Trade_winds">trade winds</a> to navigate the oceans.</p>

<p>Being a student of fluid mechanics, I started digging deeper and found a treasure trove of books on physical oceanography. The first thing a person must appreciate when trying to study the oceans is the Earth’s rotation. This leads to the <a href="https://en.wikipedia.org/wiki/Coriolis_force">Coriolis force</a> as we are observing Earth while moving along with its rotation. The Coriolis force plays a major role in creating the peristent ocean currents and the trade winds. Though I have had an acquaintance with geophysical fluid dynamics, it always takes a spark to make one go mad about a subject. This spark came from Henry Stommel’s great little book “Science of the Seven Seas” (freely available <a href="https://archive.org/details/scienceofsevense00stom/page/n7/mode/2up">here</a>). The way Stommel has introduced the oceans and its mystery to the reader is wonderful.  Any high school student  or undergrad picking up this elementary book would really feel what Stommel describes as “the call of the sea”. The fluid mechanics of the ocean and the atmosphere is facinating. While the fluid mechanics as a subject is quite mature with a long illustrated history, the scale of the oceans and the atmosphere leaves our computations and understanding falling short of satisfaction. Of course, this only gives impetus to the thousands of scientists working in this area to search deeper into nature. Henry Stommel was one of the great scientists in this field and his popular introduction definitely leaves a lasting impression about the subject and will inspire many to take up the oceans for their study and lifelong pursuit. I also highly recommend his more technical works (see <a href="https://en.wikipedia.org/wiki/Henry_Stommel#Bibliography">here</a>).</p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="Fluid Dynamics" /><category term="Movies" /><category term="henry stommel" /><category term="kon-tiki" /><category term="oceanography" /><category term="oceans" /><category term="seas" /><summary type="html"><![CDATA[The last few weeks, I have been engrossed with the oceans and navigation in their vast expanse. It all started with a couple of movies I watched. The first movie “Kon-Tiki” beautifully portrays the 1947 expedition by Norwegian explorer and ethnographer Thor Heyerdahl across the south pacific, from Peru to polynesia. The remarkable thing was that he achieved this (along with some of his friends) in a balsawood raft! He was trying to establish that Incan people were the first to populate the polynesian islands. This claim is still contested today. Nevertheless, this is an extraordinary feat. They were trying to catch the south equatorial current in their raft. This made me wonder how they were relying on its presence and persistence.]]></summary></entry><entry><title type="html">What is the Fourier transform?</title><link href="https://flow-physics.github.io//mathematical%20physics/2020/05/24/what-is-the-fourier-transform.html" rel="alternate" type="text/html" title="What is the Fourier transform?" /><published>2020-05-24T10:28:50+00:00</published><updated>2020-05-24T10:28:50+00:00</updated><id>https://flow-physics.github.io//mathematical%20physics/2020/05/24/what-is-the-fourier-transform</id><content type="html" xml:base="https://flow-physics.github.io//mathematical%20physics/2020/05/24/what-is-the-fourier-transform.html"><![CDATA[<p><em>Previous article:</em> <a href="http://flowphysics.com/fourier-transform-for-confused-engineers/">1. Fourier transform for confused engineers</a></p>

<p>What is the Fourier transform, really?</p>

<p>For some, it is the magic of seeing everything as waves. For others, it is like holding a prism in a beam of sunlight and seeing what it contains, a rainbow of colors. For some others, it is a tool to visualize what a piece of sound recording contains and adjusting it to make it sound better.</p>

<p>One can even take the romanticism out of the concept and say, it converts a bunch of given numbers into another bunch. It is through doing this that many important uses of Fourier transform can be realized. And this is precisely where many miss the woods for the trees. I hope to bridge both sides of this amazing idea. Let us begin!</p>

<p>When we have a clearly defined function, as shown in the plot below, everything is straightforward.</p>

<p><img src="/assets/img/2020/03/ftest.png" alt="" /></p>

<p>This means that we have a known expression for how the function depends on the variable <code class="language-plaintext highlighter-rouge">x</code>:</p>

\[f = f(x)\]

<p>All the nice definitions of the Fourier transform become useful and if the integral is not monstrous, we would have a good looking expression for the Fourier transform of the function. Life is tricky and usually we only have some values of the function at hand. Like:</p>

<p><img src="/assets/img/2020/03/ftest_1.png" alt="" />
<img src="/assets/img/2020/03/ftest_2.png" alt="" /></p>

<p>Most of the time, we just have the data points as shown by the red dots. You can imagine a function passing through the data like the blue line. But it does not matter. Now, it is just us and the red dots. The entire business of discrete Fourier transform (DFT) is to take the Fourier transform of such a bunch of data points. Before I throw some formulas at you, there are some ground rules to cover.</p>

<ol>
  <li>
    <p>The universe plays the game strict and fair.  If you have $N$ values in the data set, you will only get back $N$ number of values from the DFT. If you are getting anything more than $N$ values, then some of them are definitely redundant.</p>
  </li>
  <li>
    <p>The most common data points are spaced (sampled) uniformly as equi-distant points in $x$. This is usually the case since most of the digital systems sample signals at a given rate. We shall always assume this to be true in our discussions. If your samples are non-uniformly spaced, you might want to look <a href="https://en.wikipedia.org/wiki/Non-uniform_discrete_Fourier_transform">elsewhere</a>.</p>
  </li>
  <li>
    <p>The DFT models the data (and the underlying function - the blue line) using sines and cosines. Not just any sines and cosines. Some that are chosen particularly for the current dataset.</p>
  </li>
  <li>
    <p>Euler’s identity is the key to everything and never lose sight of it:</p>
  </li>
</ol>

\[e^{i\theta} = \cos \theta + i \sin \theta\]

<p>With all this preamble aside, let us look at how DFT is defined for a given set of $N$ data points (like the red dots in the plot above). We will list them as:</p>

\[f_0,f_1,f_2 . . . , f_{N-1}\]

<p>And it is also given that these points are spaced at intervals $ \Delta x$.</p>

<p>Now, we shall use a trick to tell the DFT that our set of points is periodic, even though they are actually not.  Imagine that the given set of points are repeated infinitely on both sides, like:</p>

<p>$ …f_0,f_1,f_2 . . . , f_{N-1}, f_0,f_1,f_2 . . . , f_{N-1},\ f_0,f_1,f_2 . . . , f_{N-1}, f_0,f_1,f_2 . . . , f_{N-1},f_0,f_1,f_2 . . . , f_{N-1}…$</p>

<p>This would make the graph look like:</p>

<p><img src="/assets/img/2020/05/ftest_periodic.png" alt="" /></p>

<p>We have got the function looking like a periodic function. But what is the period of our function (the repeated pattern in the plot above)? How long does a single period span in $x$?  This is easy. Take a look at the blue part of the curve above which corresponds to one period. We see that this part contains all the points $f_0,f_1,f_2 . . . , f_{N-1}$, but we also need to connect to the adjacent point (from the start of green curve). So, we actually have to include another point in our dataset (to make it periodic) which is given by $f_N = f_0$. This is important to calculate the period. Now, there are in total $N+1$ points with the number of intervals among them as $N$. The interval spacing is $\Delta x$. So, the period is,</p>

\[L = N \Delta x \Delta s=1\]

<p>But we usually do not include the last point $f_N$ in the dataset to avoid redundancy (since it simply repeats the $f_0$ value again). We just call our function periodic (in the sense given above) and this is enough.</p>

<p>Now that we got that clarified, the DFT for our data points is given by the strange looking formula:</p>

\[F_k= \frac{1}{N}\sum\limits_{n = 0}^{N-1} f_n e^{-i 2\pi n k/N}\qquad k = -(N/2)+1,..,-2,-1,0,1,2,...,N/2\]

<p>We need to understand what this formula says in all its glory. $F_k$’s are the Fourier coefficients and there are in total $N$ of them corresponding to as many Fourier modes. The above formula is actually $N$ similar looking formulas abbreviated into one. The symbol $k$ stands for the wavenumber/frequency associated with the Fourier mode. We will see what this all means in the next post.</p>

<p>P.S.: Have you noticed that the period $L$ does not even appear in this equation?! Have we wasted time in understanding the period of our function (in a strange made-up sense explained above)? NO!! You will later see that it plays a crucial role in understanding the DFT and appears explicitly in the derivative of our function if we are interested in that.</p>

<p><em>Next article: (In preparation)</em></p>

<p><em>Previous article:</em> <a href="http://flowphysics.com/fourier-transform-for-confused-engineers/">1. Fourier transform for confused engineers</a></p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="Mathematical Physics" /><category term="DFT" /><category term="FFT" /><category term="Fourier transform" /><summary type="html"><![CDATA[Previous article: 1. Fourier transform for confused engineers]]></summary></entry><entry><title type="html">Fourier transform for confused engineers</title><link href="https://flow-physics.github.io//mathematical%20physics/2020/02/22/fourier-transform-for-confused-engineers.html" rel="alternate" type="text/html" title="Fourier transform for confused engineers" /><published>2020-02-22T20:19:54+00:00</published><updated>2020-02-22T20:19:54+00:00</updated><id>https://flow-physics.github.io//mathematical%20physics/2020/02/22/fourier-transform-for-confused-engineers</id><content type="html" xml:base="https://flow-physics.github.io//mathematical%20physics/2020/02/22/fourier-transform-for-confused-engineers.html"><![CDATA[<p>If you are an engineer, you most likely encountered one of the following situations:</p>

<ul>
  <li>
    <p>Plot the power spectral density of a signal you have</p>
  </li>
  <li>
    <p>Find the dominant frequency/mode in a signal</p>
  </li>
  <li>
    <p>A journal paper stating, “It is obvious(?!) that this operation is straightforward to carry out in wave space”.</p>
  </li>
</ul>

<p>I have been in many such instances myself and referred several texts on Fourier analysis. There are some wonderful ones, don’t mistake me, but I have always felt that it is not straightforward to see the implications of the theory in the output of a Fourier transform function spit by a program/library in Python/Matlab. There are many equivalent ways in which the Fourier transform can be formulated, computed and interpreted. Throw the dreaded complex numbers in the mix , it is quite normal to feel lost. The feeling of not fully comprehending the idea, and yet repeatedly using it like a black box is frustrating. Having once been lost myself and now with a good grip on the means and ends of this wonderful mathematical tool, I attempt to write about this often-explained but rarely-understood method. This is mainly for my satisfaction, and if it helps someone understand this tool with a bit more of clarity, I will take that too.</p>

<p>This is the start of a series of posts on how to numerically compute and interpret the Fourier transform of signals/images/fields. Let’s dive right in!</p>

<p><em>Next article:</em> <a href="http://flowphysics.com/what-is-the-fourier-transform/">2. What is the Fourier transform?</a></p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="Mathematical Physics" /><category term="DFT" /><category term="FFT" /><category term="Fourier transform" /><summary type="html"><![CDATA[If you are an engineer, you most likely encountered one of the following situations:]]></summary></entry><entry><title type="html">Installing HDF5 and CGNS in Ubuntu 18.04 LTS</title><link href="https://flow-physics.github.io//installing%20software/2019/07/21/installing-hdf5-and-cgns-in-ubuntu-18-04-lts.html" rel="alternate" type="text/html" title="Installing HDF5 and CGNS in Ubuntu 18.04 LTS" /><published>2019-07-21T10:29:56+00:00</published><updated>2019-07-21T10:29:56+00:00</updated><id>https://flow-physics.github.io//installing%20software/2019/07/21/installing-hdf5-and-cgns-in-ubuntu-18-04-lts</id><content type="html" xml:base="https://flow-physics.github.io//installing%20software/2019/07/21/installing-hdf5-and-cgns-in-ubuntu-18-04-lts.html"><![CDATA[<p>If you are a computational fluid dynamics (CFD) practitioner, at some point, you would have to use Hierarchical Data Format 5 (<a href="https://www.hdfgroup.org/">HDF5</a>) and CFD General Notation System (<a href="http://cgns.github.io/">CGNS</a>) libraries in your programs. HDF5 format provides an advanced way of organizing any data and allows efficient access to the data. CGNS prescribes specific methods and practices to use when writing CFD simulation data into HDF5 format. You can find more info on everything about these libraries in their website. But here, I describe the way to install HDF5 &amp; CGNS in a Ubuntu 18.04 system.</p>

<p>The installation procedure follows what is given in CGNS website, but with some important tweaks to get it to succeed installing in Ubuntu 18.04.</p>

<p>Download the CGNS source code into some directory.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">git clone <span class="nt">-b</span> master https://github.com/CGNS/CGNS.git</code></pre></figure>

<p>Currently, this downloads CGNS 3.4.0 source code into a directory named <code class="language-plaintext highlighter-rouge">CGNS</code> in the present location. Although you can install HDF5 separately, and possibly a newer version from HDF5 website, I strongly advise against it. We want the CGNS library to work with our HDF5 installation. So, it is best to install the HDF5 version suggested by CGNS.</p>

<p>Change into the CGNS directory.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd </span>CGNS</code></pre></figure>

<p>There are install scripts provided in the <code class="language-plaintext highlighter-rouge">bin</code> folder. Three of them in fact, one to install HDF5 (<code class="language-plaintext highlighter-rouge">./bin/install-hdf.sh</code>), another to configure CGNS for our system (<code class="language-plaintext highlighter-rouge">./bin/config-cgns.sh</code>) and the last one to install the configured CGNS (<code class="language-plaintext highlighter-rouge">./bin/build-cgns.sh</code>). Of course, we need to do the installation in the order I have listed.</p>

<p>You can choose to use the <code class="language-plaintext highlighter-rouge">install-hdf.sh</code> script. Some options inside that script failed for me. So, instead, use the following commands:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">git clone https://bitbucket.hdfgroup.org/scm/hdffv/hdf5.git <span class="nt">--branch</span> hdf5_1_8 <span class="nt">--single-branch</span> hdf5_1_8</code></pre></figure>

<p>This will download the HDF5 v1.8 into the newly created directory <code class="language-plaintext highlighter-rouge">hdf5_1_8</code>. Change into this directory and use the following command:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd </span>hdf5_1_8
./configure <span class="nt">--enable-fortran</span> <span class="nt">--disable-hl</span> <span class="nt">--prefix</span><span class="o">=</span><span class="nv">$HOME</span>/hdf5 <span class="o">&amp;&amp;</span> make <span class="o">&gt;</span> result.txt 2&gt;&amp;1 <span class="o">&amp;&amp;</span> make <span class="nb">install</span></code></pre></figure>

<p>Note that I am choosing to install HDF5 in the location <code class="language-plaintext highlighter-rouge">$HOME/hdf5</code>, you can choose any other convenient location as well. But remember, this is the location you need to link any program trying to compile with HDF5 libraries.
The installation of HDF5 will take some time, and finally it will output the HDF5 configuration installed in the system. I have configured HDF5 to include Fortran bindings as well by the flag <code class="language-plaintext highlighter-rouge">--enable-fortran</code>. If you only want C binidings, you can replace that by <code class="language-plaintext highlighter-rouge">--disable-fortran</code>.</p>

<p>After this, we need to configure CGNS using the <code class="language-plaintext highlighter-rouge">./bin/config-cgns.sh</code> script. The default script looks like this:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
<span class="c">#</span>
<span class="c"># Configure CGNS for travis CI. </span>
<span class="c">#</span>
<span class="nb">set</span> <span class="nt">-e</span>
<span class="nb">cd </span>src
<span class="k">if</span> <span class="o">[</span> <span class="nv">$TRAVIS_OS_NAME</span> <span class="o">=</span> <span class="s2">"linux"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">export </span><span class="nv">FLIBS</span><span class="o">=</span><span class="s2">"-Wl,--no-as-needed -ldl -lz"</span>
  <span class="nb">export </span><span class="nv">LIBS</span><span class="o">=</span><span class="s2">"-Wl,--no-as-needed -ldl -lz"</span>
<span class="k">fi</span>

./configure <span class="se">\</span>
<span class="nt">--prefix</span><span class="o">=</span><span class="nv">$PWD</span>/cgns_build <span class="se">\</span>
<span class="nt">--with-hdf5</span><span class="o">=</span><span class="nv">$HOME</span>/hdf5 <span class="se">\</span>
<span class="nt">--with-fortran</span> <span class="se">\</span>
<span class="nt">--enable-lfs</span> <span class="se">\</span>
<span class="nt">--enable-64bit</span> <span class="se">\</span>
<span class="nt">--disable-shared</span> <span class="se">\</span>
<span class="nt">--enable-debug</span> <span class="se">\</span>
<span class="nt">--with-zlib</span> <span class="se">\</span>
<span class="nt">--disable-cgnstools</span> <span class="se">\</span>
<span class="nt">--enable-64bit</span></code></pre></figure>

<p>Edit it to look like this:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
<span class="c">#</span>
<span class="c"># Configure CGNS for travis CI. </span>
<span class="c">#</span>
<span class="nb">set</span> <span class="nt">-e</span>
<span class="nb">cd </span>src
<span class="nb">export </span><span class="nv">FLIBS</span><span class="o">=</span><span class="s2">"-Wl,--no-as-needed -ldl -lz"</span>
<span class="nb">export </span><span class="nv">LIBS</span><span class="o">=</span><span class="s2">"-Wl,--no-as-needed -ldl -lz"</span>
./configure <span class="se">\</span>
<span class="nt">--prefix</span><span class="o">=</span><span class="nv">$HOME</span>/cgns <span class="se">\</span>
<span class="nt">--with-hdf5</span><span class="o">=</span><span class="nv">$HOME</span>/hdf5 <span class="se">\</span>
<span class="nt">--with-fortran</span> <span class="se">\</span>
<span class="nt">--enable-lfs</span> <span class="se">\</span>
<span class="nt">--enable-64bit</span> <span class="se">\</span>
<span class="nt">--disable-shared</span> <span class="se">\</span>
<span class="nt">--enable-debug</span> <span class="se">\</span>
<span class="nt">--with-zlib</span> <span class="se">\</span>
<span class="nt">--disable-cgnstools</span> <span class="se">\</span>
<span class="nt">--enable-64bit</span></code></pre></figure>

<p>Apart from some specific modifications to make it work for Ubuntu, I have also specified the CGNS installation directory as <code class="language-plaintext highlighter-rouge">$HOME/cgns</code>. You can change this to any other convenient location. But again, this is where the built CGNS libraries and headers will be kept. You will be required to link to this location when compiling and linking programs with CGNS.
(If you have installed the HDF5 to some location other than <code class="language-plaintext highlighter-rouge">$HOME/hdf5</code>, you should specify that here in the flag <code class="language-plaintext highlighter-rouge">--with-hdf5=</code>)</p>

<p>Run this configure script using:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">sh ./bin/config-cgns.sh</code></pre></figure>

<p>This will configure the CGNS for your system.</p>

<p>Finally, install CGNS using:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">sh ./bin/build-cgns.sh</code></pre></figure>

<p>That’s it. You have installed HDF5 &amp; CGNS in your Ubuntu system at <code class="language-plaintext highlighter-rouge">$HOME/hdf5</code> and <code class="language-plaintext highlighter-rouge">$HOME/cgns</code> respectively.</p>

<p>There are test codes available inside the downloaded CGNS repo at: <code class="language-plaintext highlighter-rouge">./CGNS/src/Test_UserGuideCode</code>. They would have been tested during the build process as well.
If you have a program <code class="language-plaintext highlighter-rouge">test.c</code> which uses CGNS functions, then you would want to compile and link the program as follows:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">gcc <span class="nt">-I</span><span class="nv">$HOME</span>/cgns/include <span class="nt">-g</span> <span class="nt">-g</span> <span class="nt">-O2</span> <span class="nt">-c</span> test.c
gcc <span class="nt">-o</span> test.out test.o <span class="nv">$HOME</span>/cgns/lib/libcgns.a <span class="nv">$HOME</span>/hdf5/lib/libhdf5.a <span class="nt">-lz</span> <span class="nt">-lm</span> <span class="nt">-lm</span> <span class="nt">-lz</span> <span class="nt">-Wl</span>,--no-as-needed <span class="nt">-ldl</span> <span class="nt">-lz</span></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">test.out</code> is the final executable created for the program.</p>

<p>Notice that we have chose not to install the utility tools for CGNS by choosing the flag <code class="language-plaintext highlighter-rouge">--disable-cgnstools</code> in out configure script. Enabling this fails for me. I tried to install cgnstools using various other methods as well, but it fails for me in Ubuntu 18.04.</p>

<p><strong>HDFVIEW</strong></p>

<p>If you want a GUI for viewing your CGNS/HDF5 files, you may want to use HDFVIEW software. Note that the <code class="language-plaintext highlighter-rouge">hdfview</code> available in ubuntu repositories does not work. Even the source code compilation process always fails in ubuntu. Just download the prebuilt binary for CentOS7 from the HDF <a href="https://www.hdfgroup.org/downloads/hdfview/">website</a>. And run the <code class="language-plaintext highlighter-rouge">HDFView-3.1.0-Linux.sh</code> script inside the downloaded archive to create the binary. It works properly in Ubuntu 18.04. I found this useful tip from Michael Hirsch’s <a href="https://www.scivision.dev/view-hdf5-data-gui/">blog</a>.</p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="Installing Software" /><category term="CGNS" /><category term="HDF5" /><category term="hdfview" /><category term="install" /><category term="linux" /><category term="ubuntu" /><category term="ubuntu 18.04" /><summary type="html"><![CDATA[If you are a computational fluid dynamics (CFD) practitioner, at some point, you would have to use Hierarchical Data Format 5 (HDF5) and CFD General Notation System (CGNS) libraries in your programs. HDF5 format provides an advanced way of organizing any data and allows efficient access to the data. CGNS prescribes specific methods and practices to use when writing CFD simulation data into HDF5 format. You can find more info on everything about these libraries in their website. But here, I describe the way to install HDF5 &amp; CGNS in a Ubuntu 18.04 system.]]></summary></entry><entry><title type="html">Numerical Solution of the Falkner - Skan Equation</title><link href="https://flow-physics.github.io//fluid%20dynamics/mathematical%20physics/2015/02/27/numerical-solution-of-the-falkner-skan-equation.html" rel="alternate" type="text/html" title="Numerical Solution of the Falkner - Skan Equation" /><published>2015-02-27T05:49:16+00:00</published><updated>2015-02-27T05:49:16+00:00</updated><id>https://flow-physics.github.io//fluid%20dynamics/mathematical%20physics/2015/02/27/numerical-solution-of-the-falkner-skan-equation</id><content type="html" xml:base="https://flow-physics.github.io//fluid%20dynamics/mathematical%20physics/2015/02/27/numerical-solution-of-the-falkner-skan-equation.html"><![CDATA[<p>Falkner - Skan equation is a third order non-linear ordinary differential equation which arises in the laminar boundary layer flow past wedge-like objects. (More details <a href="http://en.wikipedia.org/wiki/Blasius_boundary_layer">here</a>). The equation reads,</p>

<p>$ f’’’+\frac{m+1}{2}f f’'+m\left[1-(f’)^2\right]= 0 $</p>

<p>where <em>m</em> is a constant representing the pressure gradient parameter. Our objective is to solve this differential equation for $ f(\eta)$ , for a given value of <em>m</em> using the boundary conditions,</p>

<p>$ f(0)=0 \quad \rightarrow \text{no wall transpiration}$</p>

<p>$ f’(0)=0 \quad \rightarrow \text{ no-slip condition at the wall}$</p>

<p>$ f’(1)=1 \, \rightarrow \text{free-stream velocity is reached at the edge of the boundary layer}$</p>

<p>Generally, initial value problems (IVP) are preferred for ODEs. In the case of IVP, We will be given where to start and which direction to proceed. Then we use the differential equation to progress in that direction in a step by step manner. But here we have a boundary value problem. Shooting method is used in situations where a boundary value problem has to be solved using initial value methods. The method is described as follows.</p>

<h2 id="shooting-method">Shooting method</h2>

<ol>
  <li>
    <p>Guess two values for $ f’'(0)$.</p>
  </li>
  <li>
    <p>Solve the FS equation using <a href="http://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods">RK4 method</a> with initial conditions $ f(0)=0, f’(0)=0, f’'(0) = Guess1$.</p>
  </li>
  <li>
    <p>Solve the FS equation using RK4 method with initial conditions  $ f(0)=0, f’(0)=0, f’'(0) = Guess2$.</p>
  </li>
  <li>
    <p>Find out the resulting boundary value $ f’(1)$ from both these solutions.</p>
  </li>
  <li>
    <p>If the boundary value $ f’(1)$ is different from the required value $ f’(1)=1$, find a better initial guess using <a href="http://en.wikipedia.org/wiki/Secant_method">Secant method</a>.</p>
  </li>
  <li>
    <p>Solve the FS equation by RK4 method using the new initial value guess (obtained from secant method).</p>
  </li>
  <li>
    <p>Repeat the process until the required boundary value $ f’(1)$ is obtained.</p>
  </li>
</ol>

<p>A Fortran program for solving the Falkner-Skan equation implementing the above algorithm is provided below. (Click on any part of the code and use right arrow key to scroll to right).</p>

<figure class="highlight"><pre><code class="language-fortran" data-lang="fortran"><span class="k">program</span><span class="w"> </span><span class="n">falkner_skan</span><span class="w">
</span><span class="c1">! this program solves the Falkner-Skan equation using shooting method and fourth order Runge-Kutta method.</span><span class="w">
</span><span class="c1">! the FS equation is given by,</span><span class="w">
</span><span class="c1">! f'''+((m+1)/2)ff''+ m*(1-f'^2) = 0</span><span class="w">
</span><span class="c1">! m,the pressure gradient parameter, as in u_inf=u0*x^m.</span><span class="w">
</span><span class="c1">! boundary conditions: f(0)=0, f'(0)=0, f'(1)=1</span><span class="w">
</span><span class="c1">! the third order ode is converted into a system of three first order odes where the</span><span class="w">
</span><span class="c1">! dependent variables are f, u (=f'), v (=u'=f'').</span><span class="w">
</span><span class="c1">! we solve the initial value problem f(0)=0,f'(0)=0, f''(0)=guess and shoot for solutions such that f'(1)=1.</span><span class="w">

</span><span class="k">implicit</span><span class="w"> </span><span class="k">none</span><span class="w">
</span><span class="kt">integer</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">,</span><span class="n">np</span><span class="p">,</span><span class="n">niter</span><span class="w">
</span><span class="kt">real</span><span class="p">,</span><span class="k">allocatable</span><span class="p">,</span><span class="k">dimension</span><span class="p">(:)</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="n">u</span><span class="p">,</span><span class="n">v</span><span class="p">,</span><span class="n">eta</span><span class="w">
</span><span class="kt">real</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="n">mh</span><span class="p">,</span><span class="n">re</span><span class="p">,</span><span class="n">ue</span><span class="p">,</span><span class="n">f0</span><span class="p">,</span><span class="n">u0</span><span class="p">,</span><span class="n">v0</span><span class="p">,</span><span class="n">deta</span><span class="p">,</span><span class="n">x</span><span class="p">,</span><span class="n">eps</span><span class="p">,</span><span class="n">cnu</span><span class="p">,</span><span class="n">slope</span><span class="p">,</span><span class="n">unp</span><span class="p">,</span><span class="n">unp1</span><span class="p">,</span><span class="n">vnp</span><span class="p">,</span><span class="n">vnp1</span><span class="w">
</span><span class="kt">real</span><span class="p">,</span><span class="k">allocatable</span><span class="p">,</span><span class="k">dimension</span><span class="p">(:)</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">vvel</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">vvel1</span><span class="w">
</span><span class="kt">real</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">delta_star_temp</span><span class="p">,</span><span class="n">theta_temp</span><span class="p">,</span><span class="n">deltas</span><span class="p">,</span><span class="n">theta</span><span class="w">

</span><span class="n">niter</span><span class="o">=</span><span class="mi">1000</span><span class="w"> </span><span class="c1">! no. of iterations for the shooting method</span><span class="w">
</span><span class="n">deta</span><span class="o">=</span><span class="mf">0.001</span><span class="w"> </span><span class="c1">! non-dimensional spacing in the wall-normal direction</span><span class="w">
</span><span class="n">np</span><span class="o">=</span><span class="mi">8001</span><span class="w"> </span><span class="c1">! no. of points in the wall-normal direction</span><span class="w">
</span><span class="n">eps</span><span class="o">=</span><span class="mf">1.0e-16</span><span class="w"> </span><span class="c1">! error margin used in shooting method iteration</span><span class="w">
</span><span class="n">m</span><span class="o">=</span><span class="mf">0.0</span><span class="w"> </span><span class="c1">! falkner-skan pressure gradient parameter</span><span class="w">
</span><span class="n">mh</span><span class="o">=</span><span class="mf">0.5</span><span class="o">*</span><span class="p">(</span><span class="n">m</span><span class="mf">+1.0</span><span class="p">)</span><span class="w">

</span><span class="k">allocate</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">u</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">v</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">eta</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">vvel</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">vvel1</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">y</span><span class="p">(</span><span class="n">np</span><span class="p">))</span><span class="w">

</span><span class="c1">! form the grid in the wall-normal direction</span><span class="w">
</span><span class="n">eta</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=</span><span class="mf">0.0</span><span class="w">
</span><span class="k">do</span><span class="w"> </span><span class="n">j</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span><span class="n">np</span><span class="w">
</span><span class="n">eta</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">=</span><span class="n">eta</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="n">deta</span><span class="w">
</span><span class="k">enddo</span><span class="w">

</span><span class="c1">! initial values set 1</span><span class="w">
</span><span class="n">f0</span><span class="o">=</span><span class="mf">0.0</span><span class="w">
</span><span class="n">u0</span><span class="o">=</span><span class="mf">0.0</span><span class="w">
</span><span class="n">v0</span><span class="o">=</span><span class="mf">0.335</span><span class="w">

</span><span class="k">call</span><span class="w"> </span><span class="n">rk4_fs</span><span class="p">(</span><span class="n">f</span><span class="p">,</span><span class="n">u</span><span class="p">,</span><span class="n">v</span><span class="p">,</span><span class="n">deta</span><span class="p">,</span><span class="n">mh</span><span class="p">,</span><span class="n">m</span><span class="p">,</span><span class="n">np</span><span class="p">,</span><span class="n">f0</span><span class="p">,</span><span class="n">u0</span><span class="p">,</span><span class="n">v0</span><span class="p">)</span><span class="w">

</span><span class="n">vnp</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w">
</span><span class="n">unp</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">np</span><span class="p">)</span><span class="w">

</span><span class="c1">! initial values set 2</span><span class="w">
</span><span class="n">f0</span><span class="o">=</span><span class="mf">0.0</span><span class="w">
</span><span class="n">u0</span><span class="o">=</span><span class="mf">0.0</span><span class="w">
</span><span class="n">v0</span><span class="o">=</span><span class="mf">0.33</span><span class="w">

</span><span class="k">call</span><span class="w"> </span><span class="n">rk4_fs</span><span class="p">(</span><span class="n">f</span><span class="p">,</span><span class="n">u</span><span class="p">,</span><span class="n">v</span><span class="p">,</span><span class="n">deta</span><span class="p">,</span><span class="n">mh</span><span class="p">,</span><span class="n">m</span><span class="p">,</span><span class="n">np</span><span class="p">,</span><span class="n">f0</span><span class="p">,</span><span class="n">u0</span><span class="p">,</span><span class="n">v0</span><span class="p">)</span><span class="w">

</span><span class="n">vnp1</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w">
</span><span class="n">unp1</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">np</span><span class="p">)</span><span class="w">

</span><span class="c1">! loop for the shooting method ********************************************************************</span><span class="w">
</span><span class="k">do</span><span class="w"> </span><span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">niter</span><span class="w">
</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">unp1</span><span class="mf">-1.0</span><span class="p">)</span><span class="w"> </span><span class="ow">.ge.</span><span class="w"> </span><span class="n">eps</span><span class="p">)</span><span class="w"> </span><span class="k">then</span><span class="w">
</span><span class="n">slope</span><span class="o">=</span><span class="p">(</span><span class="n">vnp1</span><span class="o">-</span><span class="n">vnp</span><span class="p">)/(</span><span class="n">unp1</span><span class="o">-</span><span class="n">unp</span><span class="p">)</span><span class="w">
</span><span class="n">v0</span><span class="o">=</span><span class="n">vnp1</span><span class="o">+</span><span class="n">slope</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="n">unp1</span><span class="p">)</span><span class="w">
</span><span class="n">unp</span><span class="o">=</span><span class="n">unp1</span><span class="w">
</span><span class="n">vnp</span><span class="o">=</span><span class="n">vnp1</span><span class="w">
</span><span class="k">call</span><span class="w"> </span><span class="n">rk4_fs</span><span class="p">(</span><span class="n">f</span><span class="p">,</span><span class="n">u</span><span class="p">,</span><span class="n">v</span><span class="p">,</span><span class="n">deta</span><span class="p">,</span><span class="n">mh</span><span class="p">,</span><span class="n">m</span><span class="p">,</span><span class="n">np</span><span class="p">,</span><span class="n">f0</span><span class="p">,</span><span class="n">u0</span><span class="p">,</span><span class="n">v0</span><span class="p">)</span><span class="w">
</span><span class="n">vnp1</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w">
</span><span class="n">unp1</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">np</span><span class="p">)</span><span class="w">
</span><span class="k">else</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="s1">'iteration converged'</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="s1">'v(1)='</span><span class="p">,</span><span class="n">v</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w">
</span><span class="k">exit</span><span class="w">
</span><span class="k">endif</span><span class="w">

</span><span class="k">if</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="ow">.eq.</span><span class="w"> </span><span class="n">niter</span><span class="p">)</span><span class="w"> </span><span class="k">then</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="s1">'maximum number of iterations reached'</span><span class="w">
</span><span class="k">endif</span><span class="w">
</span><span class="k">enddo</span><span class="w">

</span><span class="c1">! end of shooting method **************************************************************************</span><span class="w">

</span><span class="k">do</span><span class="w"> </span><span class="n">j</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">np</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="mi">38</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">eta</span><span class="p">(</span><span class="n">j</span><span class="p">),</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="p">),</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="p">),</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="w"> </span><span class="c1">! these are transformed variables - input for box method</span><span class="w">
</span><span class="k">enddo</span><span class="w">

</span><span class="c1">! calculation of normal velocity component profile</span><span class="w">
</span><span class="n">x</span><span class="o">=</span><span class="mf">0.1</span><span class="w">
</span><span class="n">u0</span><span class="o">=</span><span class="mf">1.0</span><span class="w">
</span><span class="n">ue</span><span class="o">=</span><span class="n">u0</span><span class="o">*</span><span class="p">(</span><span class="n">x</span><span class="o">**</span><span class="n">m</span><span class="p">)</span><span class="w">
</span><span class="n">cnu</span><span class="o">=</span><span class="mf">15.0e-06</span><span class="w">
</span><span class="n">re</span><span class="o">=</span><span class="n">ue</span><span class="o">*</span><span class="n">x</span><span class="p">/</span><span class="n">cnu</span><span class="w">
</span><span class="k">do</span><span class="w"> </span><span class="n">j</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">np</span><span class="w">
</span><span class="n">y</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">=</span><span class="n">eta</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">*</span><span class="nb">sqrt</span><span class="p">(</span><span class="n">cnu</span><span class="o">*</span><span class="n">x</span><span class="p">/</span><span class="n">ue</span><span class="p">)</span><span class="w"> </span><span class="c1">! distance from the wall in meters</span><span class="w">
</span><span class="n">vvel</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">=-</span><span class="p">((</span><span class="n">m</span><span class="mf">-1.0</span><span class="p">)</span><span class="o">*</span><span class="n">eta</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">*</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="n">m</span><span class="mf">+1.0</span><span class="p">)</span><span class="o">*</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="p">))</span><span class="o">*</span><span class="p">(</span><span class="mf">0.5</span><span class="p">/</span><span class="nb">sqrt</span><span class="p">(</span><span class="n">re</span><span class="p">))</span><span class="w"> </span><span class="c1">! vvel=v/u_inf</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="mi">48</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">eta</span><span class="p">(</span><span class="n">j</span><span class="p">),</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="p">),</span><span class="n">vvel</span><span class="p">(</span><span class="n">j</span><span class="p">),</span><span class="n">y</span><span class="p">(</span><span class="n">j</span><span class="p">),</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">*</span><span class="n">ue</span><span class="p">,</span><span class="n">vvel</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">*</span><span class="nb">sqrt</span><span class="p">(</span><span class="n">re</span><span class="p">)</span><span class="w">
</span><span class="k">enddo</span><span class="w">

</span><span class="c1">! calculation of boundary layer parameters</span><span class="w">
</span><span class="n">theta_temp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0.0</span><span class="w">
</span><span class="n">delta_star_temp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0.0</span><span class="w">
</span><span class="k">do</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,(</span><span class="n">np</span><span class="mi">-1</span><span class="p">)</span><span class="w">
</span><span class="n">theta_temp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">theta_temp</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">0.5</span><span class="o">*</span><span class="p">(</span><span class="n">eta</span><span class="p">(</span><span class="n">i</span><span class="mi">+1</span><span class="p">)</span><span class="o">-</span><span class="n">eta</span><span class="p">(</span><span class="n">i</span><span class="p">))</span><span class="o">*</span><span class="p">(</span><span class="n">u</span><span class="p">(</span><span class="n">i</span><span class="mi">+1</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="n">u</span><span class="p">(</span><span class="n">i</span><span class="mi">+1</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">u</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="n">u</span><span class="p">(</span><span class="n">i</span><span class="p">))</span><span class="w"> </span><span class="p">)</span><span class="w">
</span><span class="n">delta_star_temp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">delta_star_temp</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">0.5</span><span class="o">*</span><span class="p">(</span><span class="n">eta</span><span class="p">(</span><span class="n">i</span><span class="mi">+1</span><span class="p">)</span><span class="o">-</span><span class="n">eta</span><span class="p">(</span><span class="n">i</span><span class="p">))</span><span class="o">*</span><span class="p">((</span><span class="mf">1.0</span><span class="o">-</span><span class="n">u</span><span class="p">(</span><span class="n">i</span><span class="mi">+1</span><span class="p">))</span><span class="o">+</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="n">u</span><span class="p">(</span><span class="n">i</span><span class="p">)))</span><span class="w">
</span><span class="k">enddo</span><span class="w">

</span><span class="n">deltas</span><span class="o">=</span><span class="n">delta_star_temp</span><span class="o">*</span><span class="n">x</span><span class="p">/</span><span class="nb">sqrt</span><span class="p">(</span><span class="n">re</span><span class="p">)</span><span class="w">
</span><span class="n">theta</span><span class="o">=</span><span class="n">theta_temp</span><span class="o">*</span><span class="n">x</span><span class="p">/</span><span class="nb">sqrt</span><span class="p">(</span><span class="n">re</span><span class="p">)</span><span class="w">

</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="s1">'************************************************************************'</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="s1">'Solution of the Falkner-Skan equation'</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="s1">'************************************************************************'</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="s1">'(a,f8.5)'</span><span class="p">)</span><span class="w"> </span><span class="s1">'pressure gradient parameter, m='</span><span class="p">,</span><span class="n">m</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="s1">'(a,f8.5,a)'</span><span class="p">)</span><span class="w"> </span><span class="s1">'distance from the leading edge, x='</span><span class="p">,</span><span class="n">x</span><span class="p">,</span><span class="s1">' m'</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="s1">'(a,f8.2)'</span><span class="p">)</span><span class="w"> </span><span class="s1">'Re = u_inf*x/cnu = '</span><span class="p">,</span><span class="n">re</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="s1">'(a,f8.5,a)'</span><span class="p">)</span><span class="w"> </span><span class="s1">'displacement thickness, delta*='</span><span class="p">,</span><span class="n">deltas</span><span class="p">,</span><span class="s1">' m'</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="s1">'(a,f8.5,a)'</span><span class="p">)</span><span class="w"> </span><span class="s1">'momentum thickness, theta='</span><span class="p">,</span><span class="n">theta</span><span class="p">,</span><span class="s1">' m'</span><span class="w">
</span><span class="k">write</span><span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="s1">'************************************************************************'</span><span class="w">

</span><span class="k">call</span><span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="s1">'gnuplot -p FS.plt'</span><span class="p">)</span><span class="w">

</span><span class="k">return</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="k">program</span><span class="w"> </span><span class="n">falkner_skan</span><span class="w">

</span><span class="k">subroutine</span><span class="w"> </span><span class="n">rk4_fs</span><span class="p">(</span><span class="n">f</span><span class="p">,</span><span class="n">u</span><span class="p">,</span><span class="n">v</span><span class="p">,</span><span class="n">deta</span><span class="p">,</span><span class="n">mh</span><span class="p">,</span><span class="n">m</span><span class="p">,</span><span class="n">np</span><span class="p">,</span><span class="n">f0</span><span class="p">,</span><span class="n">u0</span><span class="p">,</span><span class="n">v0</span><span class="p">)</span><span class="w">
</span><span class="c1">! subroutine for solving a system of three first order odes with fourth order Runge-Kutta method</span><span class="w">
</span><span class="c1">! f0,u0,v0 are the initial values</span><span class="w">
</span><span class="c1">! f,u,v arrays give the solution</span><span class="w">
</span><span class="c1">! initial values are propagated to np steps using step spacing deta.</span><span class="w">
</span><span class="kt">integer</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">j</span><span class="w">
</span><span class="kt">real</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">kf</span><span class="p">(</span><span class="mi">4</span><span class="p">),</span><span class="n">ku</span><span class="p">(</span><span class="mi">4</span><span class="p">),</span><span class="n">kv</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="w">
</span><span class="kt">real</span><span class="p">,</span><span class="k">intent</span><span class="p">(</span><span class="k">in</span><span class="p">)</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="n">mh</span><span class="p">,</span><span class="n">deta</span><span class="p">,</span><span class="n">f0</span><span class="p">,</span><span class="n">u0</span><span class="p">,</span><span class="n">v0</span><span class="w">
</span><span class="kt">integer</span><span class="p">,</span><span class="k">intent</span><span class="p">(</span><span class="k">in</span><span class="p">)</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">np</span><span class="w">
</span><span class="kt">real</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">f</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">u</span><span class="p">(</span><span class="n">np</span><span class="p">),</span><span class="n">v</span><span class="p">(</span><span class="n">np</span><span class="p">)</span><span class="w">

</span><span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=</span><span class="n">f0</span><span class="w">
</span><span class="n">u</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=</span><span class="n">u0</span><span class="w">
</span><span class="n">v</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=</span><span class="n">v0</span><span class="w">

</span><span class="k">do</span><span class="w"> </span><span class="n">j</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span><span class="n">np</span><span class="w">
</span><span class="n">ku</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="w">
</span><span class="n">kf</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="w">
</span><span class="n">kv</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=-</span><span class="n">mh</span><span class="o">*</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">*</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">-</span><span class="n">m</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">*</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">))</span><span class="w">

</span><span class="n">ku</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">kv</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="w">
</span><span class="n">kf</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">ku</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="w">
</span><span class="n">kv</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">=-</span><span class="n">mh</span><span class="o">*</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">kf</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">kv</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">-</span><span class="n">m</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="p">(</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">ku</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="w">

</span><span class="n">ku</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">kv</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="w">
</span><span class="n">kf</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">ku</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="w">
</span><span class="n">kv</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">=-</span><span class="n">mh</span><span class="o">*</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">kf</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">kv</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">-</span><span class="n">m</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="p">(</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="mf">+0.5</span><span class="o">*</span><span class="n">ku</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="w">

</span><span class="n">ku</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="n">kv</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="w">
</span><span class="n">kf</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="n">ku</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="w">
</span><span class="n">kv</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">=-</span><span class="n">mh</span><span class="o">*</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="n">kf</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="n">kv</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">-</span><span class="n">m</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="o">-</span><span class="p">(</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="n">ku</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="w">

</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">=</span><span class="n">f</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="mf">1.0</span><span class="p">/</span><span class="mf">6.0</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="o">*</span><span class="p">(</span><span class="n">kf</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="mf">+2.0</span><span class="o">*</span><span class="n">kf</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="mf">+2.0</span><span class="o">*</span><span class="n">kf</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">+</span><span class="n">kf</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span><span class="w">
</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">=</span><span class="n">u</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="mf">1.0</span><span class="p">/</span><span class="mf">6.0</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="o">*</span><span class="p">(</span><span class="n">ku</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="mf">+2.0</span><span class="o">*</span><span class="n">ku</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="mf">+2.0</span><span class="o">*</span><span class="n">ku</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">+</span><span class="n">ku</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span><span class="w">
</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">=</span><span class="n">v</span><span class="p">(</span><span class="n">j</span><span class="mi">-1</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="mf">1.0</span><span class="p">/</span><span class="mf">6.0</span><span class="p">)</span><span class="o">*</span><span class="n">deta</span><span class="o">*</span><span class="p">(</span><span class="n">kv</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="mf">+2.0</span><span class="o">*</span><span class="n">kv</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="mf">+2.0</span><span class="o">*</span><span class="n">kv</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">+</span><span class="n">kv</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span><span class="w">
</span><span class="k">enddo</span><span class="w">

</span><span class="k">end</span><span class="w"> </span><span class="k">subroutine</span><span class="w"> </span><span class="n">rk4_fs</span></code></pre></figure>

<p>Line 114 in the above code calls the <em>gnuplot</em> program <a href="/assets/img/2015/02/fs-plt.pdf">FS.plt</a> to plot the solution. The most relevant plots for fluid dynamics in this problem are the streamwise velocity profile $ f’(\eta)$ and the wall-normal velocity profile given by,</p>

<p>$ \frac{v}{U}=-\frac{1}{2\sqrt{Re_x}}\left[ (m+1)f+(m-1)\eta f’ \right] $</p>

<p>Sample results for the case m=0 (Blasius boundary layer) are shown below.<br />
<img src="/assets/img/2015/02/u.png" alt="u" /><br />
<img src="/assets/img/2015/02/v.png" alt="v" /></p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="Fluid Dynamics" /><category term="Mathematical Physics" /><category term="falkner-skan equation" /><category term="fluid dynamics" /><category term="shooting method" /><summary type="html"><![CDATA[Falkner - Skan equation is a third order non-linear ordinary differential equation which arises in the laminar boundary layer flow past wedge-like objects. (More details here). The equation reads,]]></summary></entry><entry><title type="html">Why are sound waves longitudinal?</title><link href="https://flow-physics.github.io//mathematical%20physics/2014/10/12/why-are-sound-waves-longitudinal.html" rel="alternate" type="text/html" title="Why are sound waves longitudinal?" /><published>2014-10-12T06:00:48+00:00</published><updated>2014-10-12T06:00:48+00:00</updated><id>https://flow-physics.github.io//mathematical%20physics/2014/10/12/why-are-sound-waves-longitudinal</id><content type="html" xml:base="https://flow-physics.github.io//mathematical%20physics/2014/10/12/why-are-sound-waves-longitudinal.html"><![CDATA[<p>“Give me some example for waves in nature! “</p>

<p>“Sound is a wave.. Light is a wave.. Of course we see water waves.. “</p>

<p>Thats the usual answer we think about when asked that question.  Moreover, we are told that sound waves are longitudinal waves (compression waves) and light waves are transverse waves.  But we do not often ask the question ‘Why?’.  Why are sound waves longitudinal?.  I give a short answer and a lengthy one.</p>

<h2 id="short-answer">Short Answer</h2>

<p>Both light propagation and sound propagation (in air or water) are governed by the same wave equation.  But in case of a light wave or traveling waves on a string, the variable governed by the wave equation is the disturbance itself.  Thus leading to a transverse wave.  In the case of a sound wave, the variable governed by the wave equation is the velocity potential $\phi$.  This is related to the disturbance velocity in the following way,</p>

\[\vec{V}=\nabla \phi\]

<p>And from vector analysis, it is easy to show that the gradient vector $ \nabla \phi$ is perpendicular to constant lines of the original field $ \phi$.  Hence, even though $ \phi$ propagates like a transverse wave, the disturbance velocity $ \vec{V}$ propagates like a longitudinal wave.</p>

<h2 id="long-answer">Long Answer</h2>

<p>Waves are everywhere around us.  In simple terms, a wave is a disturbance propagating through a medium (say,  air or water).  Most wave phenomenon we see in nature are governed by the so-called wave equation,</p>

\[\frac{\partial^2 \psi}{\partial t^2}=c^2\frac{\partial ^2 \psi}{\partial x^2}\]

<p>The solution to the above equation is any (good!) function traveling in the <em>x</em>-direction with speed <em>c</em>. Formally written as $ \psi=f(x\pm ct)$.  For example, in the case of a ripple in a pond, a change in the height of the water plays the role of the disturbance.  This change propagates through the pond from the source that created the disturbance.  In this case, $ \psi $ is the displacement of water from the undisturbed level.</p>

<p>Similarly, for a light wave, the thing that changes is the electromagnetic field.  And this change is propagated through space.  There are two types of waves.  Longitudinal waves and transverse waves.  In the example given above, the change in the water level is propagating outwards throughout the pond.  But the change itself makes the water go either up/down at a given location.  Thus we say, the wave propagates in one direction and the displacement of the medium is at right angles to that direction.  This kind of wave is called a transverse wave.  An illustration of a transverse wave is given below: (taken from Wikipedia)</p>

<p><img src="/assets/img/2014/10/onde_cisaillement_impulsion_1d_30_petit.gif" alt="" /></p>

<p>On the other hand, we have longitudinal waves where the displacement of the medium occurs in the same direction as that of wave propagation.  This is illustrated in the following image: (taken from Wikipedia)</p>

<p><img src="/assets/img/2014/10/onde_compression_impulsion_1d_30_petit.gif" alt="" /></p>

<p>When sound propagates from left to right, the air molecules are compressed and rarefied just like the vertical grid lines in the above figure.   During sound propagation in the <em>x</em>-direction, the velocity potential obeys the wave equation.</p>

\[\frac{\partial^2 \phi}{\partial t^2}=c^2\frac{\partial ^2 \phi}{\partial x^2}\]

<p>(This equation is derived from the Navier-Stokes equation after linearization and some other assumptions).</p>

<p>The solution to the above equation is given as,</p>

\[\phi=f(x\pm ct)\]

<p>From this solution and the definition of the velocity potential ($ \vec{V}=\nabla\phi$),  we can find the disturbance velocity field as,</p>

\[u=f'(x \pm ct)\]

\[v=0\]

\[w=0\]

<p>Thus we see that the transverse wave of $ \phi$ in the <em>x</em>-direction leads to a disturbance velocity involving only the <em>x</em>-component velocity <em>u</em>. So the air/water molecules are displaced in the same direction as the direction of propagation.</p>

<h3 id="note">Note</h3>

<p>Sound waves propagate as longitudinal waves in fluid media. In solids, however, they travel both as longitudinal and transverse waves.</p>]]></content><author><name>Rajesh Venkatesan</name></author><category term="Mathematical Physics" /><category term="longitudinal waves" /><category term="sound waves" /><category term="transverse waves" /><category term="velocity potential" /><category term="waves" /><summary type="html"><![CDATA[“Give me some example for waves in nature! “]]></summary></entry></feed>