<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://tianzengsky.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://tianzengsky.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2025-04-24T08:00:38+00:00</updated><id>https://tianzengsky.github.io/feed.xml</id><title type="html">ZENG Tian</title><subtitle>Welcome! This is a peaceful little world, drifting outside the great universe</subtitle><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><entry><title type="html">How to Create a Universe: A Simple Starting Point of Running a Cosmological Simulation on your PC</title><link href="https://tianzengsky.github.io/2025/03/25/How-to-create-a-universe/" rel="alternate" type="text/html" title="How to Create a Universe: A Simple Starting Point of Running a Cosmological Simulation on your PC" /><published>2025-03-25T07:53:00+00:00</published><updated>2025-03-25T07:53:00+00:00</updated><id>https://tianzengsky.github.io/2025/03/25/How-to-create-a-universe</id><content type="html" xml:base="https://tianzengsky.github.io/2025/03/25/How-to-create-a-universe/"><![CDATA[<p><strong><em>This project originated from one of my master’s courses. The author is a student passionate about astrophysics but did not learn it systematically - any advice or constructive criticism is <a href="&quot;tianzeng7-c@my.cityu.edu.hk&quot; &lt;tianzeng7-c@my.cityu.edu.hk&gt;">welcome</a>!.</em></strong></p>

<p><strong><em>Example scripts will be uploaded to <a href="https://www.istarshooter.com/user/34977">Github</a>.</em></strong></p>

<p>Have you ever imagined that someday create a universe of your own in your childhood? Have you ever heared that some scientists on our little blue planet are working on simulating how the universe forms and evolves? And have you realized that you could try to run your own simulation on your PC? This post is actually a guide of making a very very simple cosmological simulation code.</p>

<p><strong>Step 1: Physical Scenario</strong></p>

<p>Modern cosmology theory tells us:</p>

<p>The early universe had near-uniform matter distribution with tiny density fluctuations. Over hundreds of millions of years, gravity amplified these fluctuations - dense regions attracted more matter while voids lost material. Dark matter formed cosmic webs of filaments and halos, within which gas collapsed to form stars and galaxies, ultimately creating today’s universe of galaxy clusters connected by filaments, separated by vast voids. This gravitational dance continues shaping cosmic structure. This is the big picture of our simple simulation.</p>

<div style="text-align: center;">
  <img src="/assets/images/boxImage_TNG100-1_stars-coldens_3840.png" alt="Cosmic web" width="100%" />
</div>
<p>*This is how the large-scale stucture looks like. The figure is from <a href="https://www.tng-project.org/media/">IllustrisTNG project</a>.</p>

<p>However, as a very very simple one, our simulation won’t consider all of these elements. We have some assumptions to simplify the situation.</p>

<p><em><strong>Assumption 1:</strong> no cosmic expansion here. In order to reduce the computational cost, we do not consider the cosmic expansion. By the way, it could be experesssed as that our result just represents some certain region of our universe.</em></p>

<p><em><strong>Assumption 2:</strong> only dark matter. <strong>This is one of the core assumptions of this simulation.</strong> On the one hand, dark matter is usually considered as some kind of mysterious collisionless, purely-gravitational particles, which means that we could save many computational resources: no collision, no Navier-Stokes equation, no chemical evolution… On the other hand, baryonic matters are not that important for a simplified simulation: although they dominate the feedback processes, they just take a very small fraction of our universe.</em></p>

<p><em><strong>Assumption 3:</strong> this is a 2D universe. <strong>This is one of the core assumptions of this simulation</strong> as well. Reducing spatial dimensions:</em></p>

<p><strong><em>· Cuts a lot of data storage needs.</em></strong></p>

<p><strong><em>· Maintains qualitative structure formation patterns</em></strong></p>

<p><strong><em>· Enables personal computer execution</em></strong></p>

<p><em><strong>Assumption 4:</strong> we adopt newtonian gravitation completely. It is very understandable as well: save resource and is still precise enough due to almost no relativistic process here(we even abandoned Friedmann equations!).</em></p>

<p><strong>Step 2: Simulate your own universe</strong></p>

<p>We have reviewed the basic physics of our simulation. Let’s translate physics into Python code (using Jupyter Notebook):</p>

<p><strong><em>Block 1: some initial setup works</em></strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
</pre></td><td class="rouge-code"><pre><span class="c1">## Initial conditions
# ======================
</span>
<span class="kn">import</span> <span class="nn">numba</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># Setup the number of CPU cores of computation
</span><span class="n">numba</span><span class="p">.</span><span class="n">set_num_threads</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>

<span class="c1"># Parameters
</span><span class="n">n</span> <span class="o">=</span> <span class="mi">120</span>            <span class="c1"># number of particles is n*n
</span><span class="n">L</span> <span class="o">=</span> <span class="mf">100.0</span>         <span class="c1"># Length of this universe
</span><span class="n">sigma</span> <span class="o">=</span> <span class="mf">1e-2</span> <span class="o">*</span> <span class="n">L</span>  <span class="c1"># factor of primordial perturbations
</span><span class="n">r_cut</span> <span class="o">=</span> <span class="mf">20.0</span>       <span class="c1"># cut radius
</span><span class="n">softening</span> <span class="o">=</span> <span class="mf">0.4</span>    <span class="c1"># avoiding infinite gravitation
</span><span class="n">dt</span> <span class="o">=</span> <span class="mf">0.01</span>          <span class="c1"># time step
</span><span class="n">steps</span> <span class="o">=</span> <span class="mi">1000</span>        <span class="c1"># total steps
</span><span class="n">grid_size</span> <span class="o">=</span> <span class="mi">500</span>  <span class="c1"># girds of figures autosaved
</span><span class="n">v_factor</span> <span class="o">=</span> <span class="mf">1e0</span>  <span class="c1"># factor for Gaussian velocity field
</span>
<span class="c1"># Create center coordinates of each grid
</span><span class="n">grid</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">endpoint</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span> <span class="o">+</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">L</span><span class="o">/</span><span class="n">n</span>
<span class="n">x_centers</span><span class="p">,</span> <span class="n">y_centers</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">meshgrid</span><span class="p">(</span><span class="n">grid</span><span class="p">,</span> <span class="n">grid</span><span class="p">)</span>

<span class="c1"># Create Guassian random displacement field
</span><span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
<span class="n">dx</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">normal</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">sigma</span><span class="o">/</span><span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">))</span>
<span class="n">dy</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">normal</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">sigma</span><span class="o">/</span><span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">))</span>

<span class="c1"># Periodic packing function
</span><span class="k">def</span> <span class="nf">periodic_wrap</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">L</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">pos</span> <span class="o">%</span> <span class="n">L</span>

<span class="c1"># Initial velocity and displacement fields
</span><span class="n">x_init</span> <span class="o">=</span> <span class="n">periodic_wrap</span><span class="p">(</span><span class="n">x_centers</span> <span class="o">+</span> <span class="n">dx</span><span class="p">,</span> <span class="n">L</span><span class="p">)</span>
<span class="n">y_init</span> <span class="o">=</span> <span class="n">periodic_wrap</span><span class="p">(</span><span class="n">y_centers</span> <span class="o">+</span> <span class="n">dy</span><span class="p">,</span> <span class="n">L</span><span class="p">)</span>

<span class="n">vx</span> <span class="o">=</span> <span class="n">v_factor</span> <span class="o">*</span> <span class="n">dx</span> <span class="o">/</span> <span class="n">sigma</span>
<span class="n">vy</span> <span class="o">=</span> <span class="n">v_factor</span> <span class="o">*</span> <span class="n">dy</span> <span class="o">/</span> <span class="n">sigma</span>

<span class="n">particles</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">stack</span><span class="p">([</span><span class="n">x_init</span><span class="p">.</span><span class="n">ravel</span><span class="p">(),</span> <span class="n">y_init</span><span class="p">.</span><span class="n">ravel</span><span class="p">(),</span> <span class="n">vx</span><span class="p">.</span><span class="n">ravel</span><span class="p">(),</span> <span class="n">vy</span><span class="p">.</span><span class="n">ravel</span><span class="p">()],</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>

<span class="c1"># Visulization
</span><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span><span class="mi">10</span><span class="p">))</span>
<span class="n">plt</span><span class="p">.</span><span class="n">subplot</span><span class="p">(</span><span class="mi">121</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">scatter</span><span class="p">(</span><span class="n">particles</span><span class="p">[:,</span><span class="mi">0</span><span class="p">],</span> <span class="n">particles</span><span class="p">[:,</span><span class="mi">1</span><span class="p">],</span> <span class="n">s</span><span class="o">=</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">c</span><span class="o">=</span><span class="s">'b'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Initial Positions"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"x "</span><span class="p">),</span> <span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"y "</span><span class="p">)</span>

<span class="n">plt</span><span class="p">.</span><span class="n">subplot</span><span class="p">(</span><span class="mi">122</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">quiver</span><span class="p">(</span><span class="n">particles</span><span class="p">[:,</span><span class="mi">0</span><span class="p">],</span> <span class="n">particles</span><span class="p">[:,</span><span class="mi">1</span><span class="p">],</span> <span class="n">particles</span><span class="p">[:,</span><span class="mi">2</span><span class="p">],</span> <span class="n">particles</span><span class="p">[:,</span><span class="mi">3</span><span class="p">],</span> <span class="n">scale</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Initial Velocity Field"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>


</pre></td></tr></tbody></table></code></pre></div></div>

<p>We create a 2D “universe box” with particles arranged in a near-uniform grid plus Gaussian perturbations, mimicking primordial density fluctuations from cosmic inflation. Key implementation notes:</p>

<p>· Gaussian displacements (dx, dy) model quantum fluctuations</p>

<p>· Perturbation amplitude (sigma): Controls structure formation speed</p>

<p>· Scale normalization: /np.sqrt(2) ensures proper variance distribution in 2D</p>

<p>· Initial velocity assumptions: Links initial velocities to density gradients</p>

<p>· Reproducibility: np.random.seed(42) enables parameter comparison</p>

<p>OK, let’s see the output:</p>
<div style="text-align: center;">
  <img src="/assets/images/output1.png" alt="output 1" width="90%" />
</div>

<div style="text-align: left;">
  <img src="/assets/images/output2.png" alt="output 1-heatmap" width="55%" />
</div>

<p><strong><em>Block 2: computing the gravitation</em></strong></p>

<p>The code for computing the gravitation (more accurately, acceleration) among all the particles is as follows:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
</pre></td><td class="rouge-code"><pre><span class="c1">## Computing gravitation
# ======================
</span>
<span class="kn">from</span> <span class="nn">numba</span> <span class="kn">import</span> <span class="n">njit</span><span class="p">,</span> <span class="n">prange</span>

<span class="o">@</span><span class="n">njit</span><span class="p">(</span><span class="n">parallel</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">fastmath</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">compute_accelerations</span><span class="p">(</span><span class="n">positions</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">r_cut</span><span class="p">,</span> <span class="n">softening</span><span class="p">):</span>
    <span class="n">N</span> <span class="o">=</span> <span class="n">positions</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">acc</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros_like</span><span class="p">(</span><span class="n">positions</span><span class="p">)</span>
    <span class="n">factor</span> <span class="o">=</span> <span class="p">(</span><span class="n">L</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="o">/</span> <span class="n">N</span>  <span class="c1"># Equivalent to G*m
</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">prange</span><span class="p">(</span><span class="n">N</span><span class="p">):</span>
        <span class="n">pos_i</span> <span class="o">=</span> <span class="n">positions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">N</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="n">j</span><span class="p">:</span> <span class="k">continue</span>  <span class="c1"># Avoid to compute gravitation between a particle and itself
</span>            
            <span class="c1"># Periodic mirror
</span>            <span class="n">delta</span> <span class="o">=</span> <span class="n">positions</span><span class="p">[</span><span class="n">j</span><span class="p">,</span> <span class="p">:</span><span class="mi">2</span><span class="p">]</span> <span class="o">-</span> <span class="n">pos_i</span><span class="p">[:</span><span class="mi">2</span><span class="p">]</span>  
            <span class="n">delta</span> <span class="o">-=</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">delta</span> <span class="o">/</span> <span class="n">L</span><span class="p">)</span> <span class="o">*</span> <span class="n">L</span>
            
            <span class="c1"># Computing the distance
</span>            <span class="n">r_sq</span> <span class="o">=</span> <span class="n">delta</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="n">delta</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="n">softening</span><span class="o">**</span><span class="mi">2</span>  
            <span class="k">if</span> <span class="n">r_sq</span> <span class="o">&lt;</span> <span class="n">r_cut</span><span class="o">**</span><span class="mi">2</span> <span class="ow">and</span> <span class="n">r_sq</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">inv_r3</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="p">(</span><span class="n">r_sq</span> <span class="o">**</span> <span class="mf">1.5</span><span class="p">)</span>  
                <span class="n">acc</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="p">:</span><span class="mi">2</span><span class="p">]</span> <span class="o">+=</span> <span class="n">delta</span> <span class="o">*</span> <span class="n">inv_r3</span>  <span class="c1"># a ∝ 1/r²
</span>        
        <span class="n">acc</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*=</span> <span class="n">factor</span>  <span class="c1"># Normalization
</span>
    <span class="k">return</span> <span class="n">acc</span>


</pre></td></tr></tbody></table></code></pre></div></div>

<p>Let me explain some important points:
<strong><em>1.</em></strong> njit is a method to increase computational efficiency. The effect depends on:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="c1"># Setup the number of CPU cores of computation
</span><span class="n">numba</span><span class="p">.</span><span class="n">set_num_threads</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The number(here 16) is the number of CPU cores you would like to use. <strong>So be aware of it</strong>, making sure to set a correct number.</p>

<p><strong><em>2.</em></strong> We use softening length to avoid infinite gravitation. So you can change the value of it to test the effect. It would be interesting. Usually, a relatively small value will be better.</p>

<p><strong><em>3.</em></strong> You might be confused about the introduction of factor. This is because we should let the average density be approximately irrelevant to the spatial scale L. We could see the areal density \(\sigma\) satisfies:</p>

\[\sigma = \frac{N \cdot m}{L^2},\]

<p>where m is the mass of each particle; N is the total number of particles.</p>

<p>And according to the formula of acceleration, we could know factor here replaces G*m, let:</p>

\[\frac{L^2}{N} = G \cdot m\]

<p>Then it is easy to see letting fatcor be current form could ensure the density irrelevant to L. You might ask why \(\sigma\) seems like equal to G. The reason is clear: all of these variables have been parameterized. The exact values are not important. The laws of their changes matter much more.</p>

<p><strong><em>Block 3: update velocity, position and acceleration</em></strong></p>

<p>The code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="c1"># Update v, a, x
# ======================
</span>
<span class="c1"># Leapfrog method
</span><span class="k">def</span> <span class="nf">leapfrog_step</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">vel</span><span class="p">,</span> <span class="n">acc</span><span class="p">,</span> <span class="n">dt</span><span class="p">):</span>
    <span class="n">vel_half</span> <span class="o">=</span> <span class="n">vel</span> <span class="o">+</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">acc</span> <span class="o">*</span> <span class="n">dt</span>     <span class="c1"># half new velocity
</span>    <span class="n">pos_new</span> <span class="o">=</span> <span class="n">pos</span> <span class="o">+</span> <span class="n">vel_half</span> <span class="o">*</span> <span class="n">dt</span>       <span class="c1"># full new position
</span>    <span class="n">pos_new</span> <span class="o">=</span> <span class="n">periodic_wrap</span><span class="p">(</span><span class="n">pos_new</span><span class="p">,</span> <span class="n">L</span><span class="p">)</span> <span class="c1"># periodic boundary
</span>    <span class="n">acc_new</span> <span class="o">=</span> <span class="n">compute_accelerations</span><span class="p">(</span><span class="n">pos_new</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">r_cut</span><span class="p">,</span> <span class="n">softening</span><span class="p">)</span> <span class="c1"># full new acceleration
</span>    <span class="n">vel_new</span> <span class="o">=</span> <span class="n">vel_half</span> <span class="o">+</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">acc_new</span> <span class="o">*</span> <span class="n">dt</span> <span class="c1"># full new velocity
</span>    <span class="k">return</span> <span class="n">pos_new</span><span class="p">,</span> <span class="n">vel_new</span><span class="p">,</span> <span class="n">acc_new</span>


</pre></td></tr></tbody></table></code></pre></div></div>

<p>Leapfrog method advantages:</p>

<p>· Energy conservation (vs Euler method’s error accumulation)</p>

<p>· Phase-space accuracy</p>

<p>· Symplectic time-reversibility</p>

<p><strong><em>Block 4: perform the calculations and save the results</em></strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
</pre></td><td class="rouge-code"><pre><span class="c1"># Perform the calculations and save the results
# ======================
</span>
<span class="k">def</span> <span class="nf">generate_heatmap</span><span class="p">(</span><span class="n">positions</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">grid_size</span><span class="p">):</span>  <span class="c1"># Grid size is here.
</span>    
    <span class="n">bins</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">grid_size</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">density</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">histogram2d</span><span class="p">(</span>
        <span class="n">positions</span><span class="p">[:,</span><span class="mi">0</span><span class="p">],</span> <span class="n">positions</span><span class="p">[:,</span><span class="mi">1</span><span class="p">],</span>
        <span class="n">bins</span><span class="o">=</span><span class="p">(</span><span class="n">bins</span><span class="p">,</span> <span class="n">bins</span><span class="p">)</span>
    <span class="p">)</span>
    
    <span class="c1"># Normalize to [0,1]
</span>    <span class="n">density</span> <span class="o">=</span> <span class="p">(</span><span class="n">density</span> <span class="o">-</span> <span class="n">density</span><span class="p">.</span><span class="nb">min</span><span class="p">())</span> <span class="o">/</span> <span class="p">(</span><span class="n">density</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="o">-</span> <span class="n">density</span><span class="p">.</span><span class="nb">min</span><span class="p">()</span> <span class="o">+</span> <span class="mf">1e-8</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">density</span><span class="p">.</span><span class="n">T</span>  


<span class="c1"># Simulation
</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="nn">os</span>

<span class="n">output_dir</span> <span class="o">=</span> <span class="s">"./density_heatmaps"</span>
<span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">output_dir</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="n">positions</span> <span class="o">=</span> <span class="n">particles</span><span class="p">[:,</span> <span class="p">:</span><span class="mi">2</span><span class="p">].</span><span class="n">copy</span><span class="p">()</span>
<span class="n">velocities</span> <span class="o">=</span> <span class="n">particles</span><span class="p">[:,</span> <span class="mi">2</span><span class="p">:].</span><span class="n">copy</span><span class="p">()</span>

<span class="n">acc</span> <span class="o">=</span> <span class="n">compute_accelerations</span><span class="p">(</span><span class="n">positions</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">r_cut</span><span class="p">,</span> <span class="n">softening</span><span class="p">)</span>

<span class="c1"># Perform the simulation
</span><span class="k">for</span> <span class="n">step</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">steps</span><span class="p">):</span>
    <span class="c1"># Update
</span>    <span class="n">positions</span><span class="p">,</span> <span class="n">velocities</span><span class="p">,</span> <span class="n">acc</span> <span class="o">=</span> <span class="n">leapfrog_step</span><span class="p">(</span><span class="n">positions</span><span class="p">,</span> <span class="n">velocities</span><span class="p">,</span> <span class="n">acc</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
    
    <span class="c1"># Create heatmaps
</span>    <span class="n">density</span> <span class="o">=</span> <span class="n">generate_heatmap</span><span class="p">(</span><span class="n">positions</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">grid_size</span><span class="p">)</span>  <span class="c1"># Grid size is here.
</span>
    <span class="c1"># Avoid zero
</span>    <span class="n">density</span> <span class="o">+=</span> <span class="mi">1</span>
    
    <span class="c1"># Visualization
</span>    <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span><span class="mi">6</span><span class="p">))</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">imshow</span><span class="p">(</span><span class="n">density</span><span class="p">,</span> <span class="n">origin</span><span class="o">=</span><span class="s">'lower'</span><span class="p">,</span> <span class="n">extent</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">L</span><span class="p">],</span>
              <span class="n">cmap</span><span class="o">=</span><span class="s">'jet'</span><span class="p">)</span><span class="c1">#, norm=LogNorm(vmax=1.8))
</span>    
    <span class="n">cbar</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">colorbar</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s">'Log Density (count+1)'</span><span class="p">)</span>
    <span class="n">cbar</span><span class="p">.</span><span class="n">formatter</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">LogFormatter</span><span class="p">()</span>
    <span class="n">cbar</span><span class="p">.</span><span class="n">update_ticks</span><span class="p">()</span>

    <span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="sa">f</span><span class="s">"Step </span><span class="si">{</span><span class="n">step</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">steps</span><span class="si">}</span><span class="s">, t = </span><span class="si">{</span><span class="p">(</span><span class="n">step</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">dt</span><span class="si">:</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"x"</span><span class="p">),</span> <span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"y"</span><span class="p">)</span>
    
    <span class="c1"># Save
</span>    <span class="n">plt</span><span class="p">.</span><span class="n">savefig</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">output_dir</span><span class="si">}</span><span class="s">/heatmap_</span><span class="si">{</span><span class="n">step</span><span class="si">:</span><span class="mi">03</span><span class="n">d</span><span class="si">}</span><span class="s">.png"</span><span class="p">,</span> <span class="n">dpi</span><span class="o">=</span><span class="mi">150</span><span class="p">,</span> <span class="n">bbox_inches</span><span class="o">=</span><span class="s">'tight'</span><span class="p">)</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
    
    <span class="c1"># Display the completion progress
</span>    <span class="k">if</span> <span class="p">(</span><span class="n">step</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">10</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Saved heatmap </span><span class="si">{</span><span class="n">step</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">steps</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"All figures are saved in: </span><span class="si">{</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">output_dir</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


</pre></td></tr></tbody></table></code></pre></div></div>

<p>OKay. When you run the simulation, all figures will be saved in the folder you specified. Now we have completed the whole simulation. Although it is very simple and crude, it could roughly show the growth of structures(such as large-scale structures or several halos/ galaxies, depending on your parameters) under pure gravitation. It is funny, but have severe limitations. For example, we just simply ignored the cosmic expansion, and also, there is no feedback process due to the absence of baryonic components and black holes. Maybe you could try to add them on your computer ^_^</p>

<p>Last but not least, we can see one example gif of the result. It shows the growth and merger of several galaxies. Due to the limitation of this site, I just used a part of frames to create the gif. And the code is attached below as well:</p>

<div style="text-align: center;">
  <img src="/assets/images/output.gif" alt="output gif" width="100%" />
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
</pre></td><td class="rouge-code"><pre><span class="c1"># Creating gif
# ======================
</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="kn">import</span> <span class="nn">os</span>

<span class="c1"># Input figures and output path
</span><span class="n">input_path</span> <span class="o">=</span> <span class="sa">r</span><span class="s">"c:\Users\zt\Desktop\SemB\Modern Topics\cosmic_web_simulation\density_heatmaps"</span>
<span class="n">output_gif</span> <span class="o">=</span> <span class="sa">r</span><span class="s">"c:\Users\zt\Desktop\SemB\Modern Topics\cosmic_web_simulation\density_heatmaps\output.gif"</span>      
<span class="n">duration</span> <span class="o">=</span> <span class="mi">20</span>                        <span class="c1"># ms
</span><span class="n">loop</span> <span class="o">=</span> <span class="mi">1</span>                             <span class="c1"># 0 for infinite loops
</span>
<span class="c1"># Get all figures
</span><span class="n">image_files</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">input_path</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> 
    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="p">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">input_path</span><span class="p">)</span> 
    <span class="k">if</span> <span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="p">().</span><span class="n">endswith</span><span class="p">((</span><span class="s">'.png'</span><span class="p">,</span> <span class="s">'.jpg'</span><span class="p">,</span> <span class="s">'.jpeg'</span><span class="p">))</span><span class="c1">#, '.gif', '.bmp'))
</span><span class="p">]</span>

<span class="c1"># Sort by name
</span><span class="n">image_files</span><span class="p">.</span><span class="n">sort</span><span class="p">()</span>

<span class="c1"># Check
</span><span class="k">if</span> <span class="ow">not</span> <span class="n">image_files</span><span class="p">:</span>
    <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"No valid file！"</span><span class="p">)</span>


<span class="n">frames</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">try</span><span class="p">:</span>
    <span class="c1"># Use the 1st fig. as standard
</span>    <span class="k">with</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">image_files</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">as</span> <span class="n">img</span><span class="p">:</span>
        <span class="n">base_size</span> <span class="o">=</span> <span class="n">img</span><span class="p">.</span><span class="n">size</span>
    
    <span class="k">for</span> <span class="n">image_file</span> <span class="ow">in</span> <span class="n">image_files</span><span class="p">:</span>
        <span class="k">with</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">image_file</span><span class="p">)</span> <span class="k">as</span> <span class="n">img</span><span class="p">:</span>
            <span class="c1"># Transfer to RGB
</span>            <span class="n">img</span> <span class="o">=</span> <span class="n">img</span><span class="p">.</span><span class="n">convert</span><span class="p">(</span><span class="s">"RGB"</span><span class="p">)</span>
           
            <span class="n">img</span> <span class="o">=</span> <span class="n">img</span><span class="p">.</span><span class="n">resize</span><span class="p">(</span><span class="n">base_size</span><span class="p">)</span>
            <span class="n">frames</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">img</span><span class="p">.</span><span class="n">copy</span><span class="p">())</span>
            
    <span class="c1"># Save as .gif
</span>    <span class="n">frames</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">save</span><span class="p">(</span>
        <span class="n">output_gif</span><span class="p">,</span>
        <span class="n">save_all</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
        <span class="n">append_images</span><span class="o">=</span><span class="n">frames</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span>
        <span class="n">duration</span><span class="o">=</span><span class="n">duration</span><span class="p">,</span>
        <span class="n">loop</span><span class="o">=</span><span class="n">loop</span><span class="p">,</span>
        <span class="n">optimize</span><span class="o">=</span><span class="bp">True</span>
    <span class="p">)</span>
    
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Success！Saved to：</span><span class="si">{</span><span class="n">output_gif</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    
<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"error：</span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


</pre></td></tr></tbody></table></code></pre></div></div>]]></content><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><category term="astrophysics" /><category term="N-body Simulation" /><category term="Python" /><summary type="html"><![CDATA[This project originated from one of my master’s courses. The author is a student passionate about astrophysics but did not learn it systematically - any advice or constructive criticism is welcome!.]]></summary></entry><entry><title type="html">M101: The Windmill in the Northern Sky</title><link href="https://tianzengsky.github.io/2025/03/02/M101/" rel="alternate" type="text/html" title="M101: The Windmill in the Northern Sky" /><published>2025-03-02T02:30:00+00:00</published><updated>2025-03-02T02:30:00+00:00</updated><id>https://tianzengsky.github.io/2025/03/02/M101</id><content type="html" xml:base="https://tianzengsky.github.io/2025/03/02/M101/"><![CDATA[<p>*Data from myself, processed by myself.</p>

<div style="text-align: center;">
  <img src="/assets/images/M101_Fin.jpg" alt="M101" width="95%" />
</div>

<p>This is a photo of the Messier 101 galaxy, taken by me two years ago using the Skywatcher BK 150 and a Nikon D5100 camera on the rooftop of my house. I only had three hours of exposure time, so there is some noise in the image, but I’m happy with the results. It is my first attempt at capturing a galaxy. I can still recall the slightly cold and damp air of that night.</p>

<p>M101 is a beautiful spiral galaxy located about 21 million light-years away in the constellation Ursa Major. It’s also known as the “Pinwheel Galaxy” due to its spiral shape. The galaxy has a similar structure to our own Milky Way but is much larger, with a lot of star-forming regions. It’s one of the most studied galaxies because of its striking appearance and the way it shows the process of star birth. Despite the noise from my short exposure, you can still make out the swirling arms of M101, which are filled with bright stars and nebulae.</p>

<p>Looking at M101, I can’t help but feel a sense of wonder and awe. It’s so far away, that the light from its stars has spent millions of years to reach us, reminding us how vast the universe truly is.</p>]]></content><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><category term="astrophotography" /><summary type="html"><![CDATA[*Data from myself, processed by myself.]]></summary></entry><entry><title type="html">The Sombrero Galaxy (M104): A Cosmic Hat with Hidden Secrets</title><link href="https://tianzengsky.github.io/2025/02/26/M104/" rel="alternate" type="text/html" title="The Sombrero Galaxy (M104): A Cosmic Hat with Hidden Secrets" /><published>2025-02-26T04:25:00+00:00</published><updated>2025-02-26T04:25:00+00:00</updated><id>https://tianzengsky.github.io/2025/02/26/M104</id><content type="html" xml:base="https://tianzengsky.github.io/2025/02/26/M104/"><![CDATA[<p>*Data from <a href="https://www.astrobin.com/users/shyzhang127/collections/">Alpha Zhang</a>, processed by me.</p>

<div style="text-align: center;">
  <img src="/assets/images/M104_Fin1.jpg" alt="M104" width="95%" />
</div>

<p>Floating in the constellation Virgo, this galaxy looks like a wide-brimmed Mexican hat—a bright center surrounded by a dark, dusty ring. But that “hat” is actually a giant island of stars, 28 million light-years away.</p>

<p>What makes it special?</p>

<p>The thick dust ring isn’t just decoration. It’s a busy factory where new stars are born, wrapped around the galaxy like a cosmic belt.</p>

<p>At its heart, scientists found a supermassive black hole—a silent monster weighing a billion times more than our Sun.</p>

<p>Here’s the wild part: the light we see today left this galaxy when Earth’s early mammals were still dinosaurs’ snacks. That dusty brim? It’s made of the same stuff that becomes planets (and people) given enough time.</p>

<p>The Sombrero reminds us that even in space, things aren’t always what they seem. What looks like a peaceful hat is really a slow-motion dance—stars swirling, dust collapsing, and a black hole quietly shaping everything around it. Your photo freezes this dance into a single moment, letting us peek at a story that’s been unfolding for billions of years.</p>]]></content><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><category term="astrophotography" /><summary type="html"><![CDATA[*Data from Alpha Zhang, processed by me.]]></summary></entry><entry><title type="html">Two Faces of California Nebula</title><link href="https://tianzengsky.github.io/2025/02/26/California-Nebula/" rel="alternate" type="text/html" title="Two Faces of California Nebula" /><published>2025-02-26T02:33:00+00:00</published><updated>2025-02-26T02:33:00+00:00</updated><id>https://tianzengsky.github.io/2025/02/26/California-Nebula</id><content type="html" xml:base="https://tianzengsky.github.io/2025/02/26/California-Nebula/"><![CDATA[<p>*Data from <a href="https://www.istarshooter.com/user/33756">Tim</a>, processed by me.</p>

<p>Spanning 100 light-years in the constellation Perseus, the California Nebula glows like a celestial silhouette of its namesake state. Its crimson hues are ignited by Xi Persei, a massive blue star whose ultraviolet radiation ionizes hydrogen gas, painting the nebula in the signature red of Hα emission (656 nm).</p>

<div style="text-align: center;">
  <img src="/assets/images/California Nebula.jpg" alt="SHO" width="95%" />
</div>

<p><strong>SHO (Sulfur-Hydrogen-Oxygen):</strong>
The “Hubble Palette” transforms sulfur (SII, 672 nm) into red, Hα into green, and OIII (500 nm) into blue. This false-color alchemy unveils shockwaves and turbulence invisible to human eyes, exposing the nebula’s violent dance of stellar winds and magnetic fields.</p>

<div style="text-align: center;">
  <img src="/assets/images/California Nebula_HOO.jpg" alt="HOO" width="95%" />
</div>

<p><strong>HOO (Hydrogen-Oxygen-Oxygen):</strong>
By mapping Hα to red and OIII to blue-green, this blend mimics the nebula’s natural glow. The delicate gradients highlight the interplay between hydrogen’s dominance and oxygen’s subtle traces—like cosmic brushstrokes of a stellar forge.</p>]]></content><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><category term="astrophotography" /><summary type="html"><![CDATA[*Data from Tim, processed by me.]]></summary></entry><entry><title type="html">The North America Nebula (NGC 7000): A Cosmic Tapestry of Light</title><link href="https://tianzengsky.github.io/2025/02/24/NGC7000/" rel="alternate" type="text/html" title="The North America Nebula (NGC 7000): A Cosmic Tapestry of Light" /><published>2025-02-24T15:53:00+00:00</published><updated>2025-02-24T15:53:00+00:00</updated><id>https://tianzengsky.github.io/2025/02/24/NGC7000</id><content type="html" xml:base="https://tianzengsky.github.io/2025/02/24/NGC7000/"><![CDATA[<p>*Data from <a href="https://www.istarshooter.com/user/34977">β Lib</a>, processed by me.</p>

<div style="text-align: center;">
  <img src="/assets/images/RGB_Fin1.jpg" alt="America Nebula" width="95%" />
</div>

<p>The North America Nebula is a giant cloud of gas in the constellation Cygnus (the Swan). Its shape looks like the continent of North America on Earth, which is how it got its name. This nebula is about 2,200 light-years away and stretches 120 light-years wide. It glows red because hydrogen gas inside it is lit up by strong light from young, hot stars nearby.</p>

<p>One special area, called the “Cygnus Wall,” acts like a star factory. Here, gas and dust slowly collapse to form new stars. The nebula’s red color comes from the energy of these newborn stars, which makes the hydrogen gas shine like a cosmic lamp.</p>

<p>What’s magical about this nebula? The light we see today left the cloud 2,200 years ago—around the time humans built the Great Wall of China. The gas here might one day form new stars and planets, just like our Sun and Earth were born from similar clouds long ago.</p>

<p>In simple words: This glowing red cloud is both a birthplace for stars and a time machine, showing us how the universe recycles old stardust into new worlds.</p>]]></content><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><category term="astrophotography" /><summary type="html"><![CDATA[*Data from β Lib, processed by me.]]></summary></entry><entry><title type="html">About This Tiny Universe</title><link href="https://tianzengsky.github.io/2025/02/23/about-this-tiny-universe/" rel="alternate" type="text/html" title="About This Tiny Universe" /><published>2025-02-23T08:16:00+00:00</published><updated>2025-02-23T08:16:00+00:00</updated><id>https://tianzengsky.github.io/2025/02/23/about-this-tiny-universe</id><content type="html" xml:base="https://tianzengsky.github.io/2025/02/23/about-this-tiny-universe/"><![CDATA[<p>Hello!</p>

<p>This is a small world, drifting beyond the great universe (if you’ve read Three Body you’ll get it, haha). It’s a space where I’d share some interesting things I’ve completed or am still working on — like some small practice of astrophysical knowledges, my astrophotography works and some thoughts and emotions of my everyday life.</p>]]></content><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><category term="thoughts" /><summary type="html"><![CDATA[Hello!]]></summary></entry><entry><title type="html">Beautiful Jewelry and Silk Around It: Cluster M38 and Diffuse Nebulae</title><link href="https://tianzengsky.github.io/2025/02/23/M38/" rel="alternate" type="text/html" title="Beautiful Jewelry and Silk Around It: Cluster M38 and Diffuse Nebulae" /><published>2025-02-23T08:16:00+00:00</published><updated>2025-02-23T08:16:00+00:00</updated><id>https://tianzengsky.github.io/2025/02/23/M38</id><content type="html" xml:base="https://tianzengsky.github.io/2025/02/23/M38/"><![CDATA[<p>*Data from <a href="https://www.istarshooter.com/user/19736">猪蹄酱</a>, processed by me.</p>

<div style="text-align: center;">
  <img src="/assets/images/RGBH.jpg" alt="M38 Cluster with H-alpha Nebulosity" width="95%" />
</div>

<p>Beneath the cold mathematics of light-years and spectral lines lies a cosmic canvas painted with stardust. The sapphire glitter of M38, ancient yet newborn, dances in gravitational harmony—a frozen firework from an era when Earth’s continents were still forming. And there, in the shadows between stars, the crimson whispers of hydrogen tell stories yet unfinished: ephemeral veils where gravity’s patient hand will sculpt new worlds from chaos, continuing the billion-year waltz of creation and decay.</p>

<p><strong>M38 (NGC 1912):</strong><br />
An open star cluster in the constellation Auriga, approximately 420 million years old and 3,200 light-years from Earth. Its distinctive “crossbar” shape, formed by a dense concentration of young blue stars, makes it a striking deep-sky object. The cluster spans about 25 light-years and contains over 100 identified members.</p>

<p><strong>Hydrogen-alpha (Hα) Nebulosity:</strong><br />
The faint reddish glow around M38 comes from hydrogen gas that was first ionized by ultraviolet light from young stars, then recombines to emit Hα photons. This delicate process requires both energetic stars (to ionize the gas) and cooler regions (for recombination to occur), revealing the dance between destruction and rebirth in stellar ecosystems.</p>]]></content><author><name>Songzi Vong</name><email>tianzeng7-c@my.cityu.edu.hk</email></author><category term="astrophotography" /><summary type="html"><![CDATA[*Data from 猪蹄酱, processed by me.]]></summary></entry></feed>