Tutorial 1: Create 2D dam break

In this tutorial, we will create the initial state for a two-phase gas-liquid dam break simulation using GeoParticle. The expected result is as follows:

_images/dam.png

What we will learn in this tutorial:

  • Creating 2D geometries: ThickRectangle and FilledRectangle

  • Performing operations: subtract and shift

  • Accessing Geometry class member: xs, ys, and flatten_coords

Specify the parameters

First, we need to import the necessary libraries and set up the simulation parameters:

 1import numpy as np
 2from lammps import lammps
 3import geoparticle as gp
 4
 5dl = 1e-4 # particle spacing
 6l_box = 0.01 # lengt of the box
 7h_box = 0.006 # height of the box
 8l_water = 0.003 # length of the water column
 9h_water = 0.0045 # height of the water column
10n_thick = 2 # number of particles in thickness direction

Obtain all the particle coordinates

Create the geometry of the solid wall:

1wall = gp.ThickRectangle(
2    length=l_box,  # inner length
3    width=h_box,  # inner height
4    n_thick=n_thick,  # number of particle layers
5    dl=dl,  # particle spacing
6    plane='XOY',  # plane of the rectangle, 'XOY' is the default
7    name='wall'  # name of the geometry for reference
8)

One may ask where the wall is located. All the geometries are created by placing their centers (for solids of revolution) or vertices (for other solids like the block) at the anchor. See the documentation of each geometry for its anchor definition. By default, the anchor is set to (0, 0, 0). If we do not specify the anchor, the shift method can be used to move the geometry to the desired location.

Now we should move the water region dl to the right and dl up to avoid overlapping with the wall, as well as the gas. To create the gas, we can create a large rectangle and subtract the water region from it:

Since a 2D geometry is created in the XOY plane by default, we do not need to specify the plane here.

 1water = gp.FilledRectangle(
 2    length=l_water, width=h_water, dl=dl, name='water',
 3    anchor=(dl, dl, 0)
 4)
 5gas = gp.FilledRectangle(
 6    l_box - 2 * dl, h_box - 2 * dl, dl, name='gas'
 7).shift(x=dl, y=dl)
 8gas = gas.subtract(
 9    water,  # which geometry to subtract
10    rmax=1e-6  # maximum distance to look for overlapping
11)

In addition to using gas = gas.subtract(water, rmax) for subtraction, three other methods are provided:

  • gas = gp.Subtract(gas, water, rmax)

  • gas = gas - water

  • gas -= water

The latter two methods use the default rmax value, i.e., 1e-5 (m), defined in the Geometry class.

Now we have obtained all the particle coordinates.

Create particles in LAMMPS

We should first create a LAMMPS instance:

1lmp = lammps(cmdargs=['-screen', 'none', '-log', 'none'])

Now create the simulation box. We can leave some buffer between the geometries and the box boundaries. Access the x-, y-, and z-coordinates of the wall geometry using the xs, ys, and zs properties.

 1fac_buf = 1
 2xlo = wall.xs.min() - wall.xs.max() * fac_buf
 3xhi = wall.xs.max() * (1 + fac_buf)
 4ylo = wall.ys.min() - wall.ys.max() * fac_buf
 5yhi = wall.ys.max() * (1 + fac_buf)
 6zlo = -3e-4
 7zhi = 3e-4
 8lmp.commands_string(f"""
 9dimension    2
10atom_style   rheo
11units                si
12newton               on
13boundary         f f p
14comm_modify  vel yes
15region       simulation_box block {xlo} {xhi} {ylo} {yhi} {zlo} {zhi}
16create_box   3 simulation_box
17""")

Now we can create particles for each geometry. The required method is create_atoms, which takes the number of atoms, their IDs, types, and coordinates as input arguments. We can use the flatten_coords property of each geometry to get the coordinates in the required format.

 1n_atoms_wall = wall.size
 2lmp.create_atoms(
 3    n_atoms_wall, np.arange(n_atoms_wall) + 1 + lmp.get_natoms(),
 4    np.full(n_atoms_wall, 1, dtype=int), wall.flatten_coords)
 5n_atoms_water = water.size
 6lmp.create_atoms(
 7    n_atoms_water, np.arange(n_atoms_water) + 1 + lmp.get_natoms(),
 8    np.full(n_atoms_water, 2, dtype=int), water.flatten_coords)
 9n_atoms_gas = gas.size
10lmp.create_atoms(
11    n_atoms_gas, np.arange(n_atoms_gas) + 1 + lmp.get_natoms(),
12    np.full(n_atoms_gas, 3, dtype=int), gas.flatten_coords)
13n_atoms_all = lmp.get_natoms()
14log_n_atom = f'n_atoms_wall: {n_atoms_wall},' \
15             f' n_atom_water: {n_atoms_water}' \
16             f' n_atoms_all: {n_atoms_all}.'
17print(log_n_atom)

Check the overlapping particles

Geoparticle already provides check_overlap method to check the overlapping particles, which is done automatically. However, it only checks the overlapping particles within each geometry. To check the entire system, one way is to compute the union of all geometries and use the check_overlap method. Here, we alternatively use LAMMPS commands for the check. We first delete the overlapping atoms. If the number of atoms remains the same after deleting overlapping atoms, then there is no overlapping atoms

 1# check atom overlapping
 2lmp.commands_string(f"""
 3pair_style      zero {dl * 2}
 4pair_coeff      * *
 5neighbor        {dl * 0.1} bin
 6delete_atoms    overlap {dl * 0.8} all all
 7""")
 8n_atoms_all_now = lmp.get_natoms()
 9if n_atoms_all == n_atoms_all_now:
10    print('Congrats, no atoms are overlapped!')
11else:
12    raise RuntimeError(f'{n_atoms_all - n_atoms_all_now} atoms are overlapped!')

Save the data file for LAMMPS for ensuing visualization and simulation. Note that we must assign mass to each particle before writing the data file.

1m0_fluid = dl ** 2 * 993
2m0_gas = dl ** 2 * 1.1
3lmp.commands_string(f"""
4mass 1*2 {m0_fluid}
5mass 3 {m0_gas}
6run 0
7write_data gas_liquid_dam2D.data
8""")