# -*- coding: utf-8 -*-
"""
ion_cloud_generation: Generation of ion cloud initialization files
"""
import numpy as np
from IDSimPy.analysis.constants import *
[docs]
def write_cloud_file(ion_cloud, filename):
"""
Writes an ion cloud to an ion cloud file
:param ion_cloud: an np.array with the columns:
[x pos, y pos, z pos, x velo, y velo, z velo, charge (in elem. charges), mass (in amu)]
:type ion_cloud: numpy.Array
:param filename: name of the file in which the tabular ion cloud data is written to
:type filename: str
"""
with open(filename, 'w') as file:
file.write('#pos x; pos y; pos z; vx; vy; vz; charge; mass_amu; collision diameter (angstroem); TOB\n')
for i_ion in range(np.shape(ion_cloud)[0]):
line = ion_cloud[i_ion, :]
for v in line:
file.write(str(v) + ';')
file.write('\n')
[docs]
def velo_from_kinetic_energy(ke_eV, mass_amu):
ke_J = ke_eV * JOULE_PER_EV
m_kg = mass_amu * KG_PER_AMU
v = np.sqrt(2.0 * ke_J / m_kg)
return v
[docs]
def random_sphere(radius, n_samples):
# http://mathworld.wolfram.com/SpherePointPicking.html
z = 2 * np.random.rand(n_samples) - 1 # uniform in -1, 1
t = 2 * np.pi * np.random.rand(n_samples) # uniform in 0, 2*pi
x = np.sqrt(1 - z ** 2) * np.cos(t)
y = np.sqrt(1 - z ** 2) * np.sin(t)
coords = np.transpose(np.vstack([x, y, z])) * radius
return coords
[docs]
def set_kinetic_energy_in_z_dir(ion_cloud, ke):
ion_cloud[:, 5] = velo_from_kinetic_energy(ke, ion_cloud[:, 7])
return ion_cloud
[docs]
def add_thermalized_kinetic_energy(ion_cloud, ke):
n_ions = np.shape(ion_cloud)[0]
thermal_velo_mag = velo_from_kinetic_energy(ke, ion_cloud[:, 7])
thermal_velo = random_sphere(np.transpose([thermal_velo_mag]), n_ions)
ion_cloud[:, 3:6] = ion_cloud[:, 3:6] + thermal_velo
return ion_cloud
[docs]
def define_xy_grid(n_x, n_y, w_x, w_y, o_x, o_y, mass):
"""
Defines a grid in the x-y direction (z=0)
(grid is from -width to width)
:param int n_x: ions in x direction
:param int n_y: ions in y direction
:param float w_x: width in x direction
:param float w_y: width in y direction
:param float o_x: offset in x direction
:param float o_y: offset in y direction
:param float mass: the mass of the ions in the grid
:return: the ion cloud in an np.array with the structure as expected by write_cloud_file
"""
x_vec = np.linspace(-w_x, w_x, n_x) + o_x
y_vec = np.linspace(-w_y, w_y, n_y) + o_y
result = np.zeros([n_x * n_y, 10])
i = 0
for x in x_vec:
for y in y_vec:
result[i, :] = np.array([x, y, 0, 0, 0, 0, 1, mass, 0.0, 0])
i += 1
return result
[docs]
def define_origin_centered_block(n_ions, w_x, w_y, w_z, mass):
"""
Defines a block of random ions around the coordinate system origin
:param int n_ions: the number of ions in the block
:param float w_x: the width in x direction
:param float w_y: the width in y direction
:param float w_z: the width in z direction
:param float mass: the mass of the ions (in amu)
:return: the ion cloud in an np.array with the structure as expected by write_cloud_file
"""
positions = np.random.rand(n_ions, 3)
velocity = np.zeros([n_ions, 3])
ion_masses = np.zeros([n_ions, 1]) + mass
charges = np.zeros([n_ions, 1]) + 1
diameters= np.zeros([n_ions, 1])
time_of_birts = np.zeros([n_ions, 1])
positions[:, 0] = (positions[:, 0] * (2 * w_x)) - w_x
positions[:, 1] = (positions[:, 1] * (2 * w_y)) - w_y
positions[:, 2] = (positions[:, 2] * (2 * w_z)) - w_z
result = np.hstack([positions, velocity, charges, ion_masses, diameters, time_of_birts])
return result
[docs]
def define_cylinder_z_dir(n_ions, r, z, charge, mass):
"""
Defines a cylinder with the cylinder axis parallel to the z-axis and the center of one face of the cylinder on
the origin of the coordinate system filled with random ions.
The cylinder cross section, a disk, must be filled uniformly with ions, see
http://mathworld.wolfram.com/DiskPointPicking.html for details and argument how to do this.
:param int n_ions: The number of ions in the cylinder
:param float r: Radius of the cylinder
:param float z: Height of the cylinder
:param float charge: Charge of the ions in the generated cylinder
:param float mass: Mass of the ions in the generated cylinder
:return: Array with parameters of the particles in the defined cylinder. Columns are:
[x,y,z, vx, vy, vz, charge, mass, time of birth]
"""
R = np.sqrt(np.random.rand(n_ions, 1)) * r
phi = np.random.rand(n_ions, 1) * 2 * np.pi
Z = np.random.rand(n_ions, 1) * z
X = np.cos(phi) * R
Y = np.sin(phi) * R
V = np.zeros([n_ions, 3])
C = np.zeros([n_ions, 1]) + charge
M = np.zeros([n_ions, 1]) + mass
DIAM = np.zeros([n_ions, 1])
TOB = np.zeros([n_ions, 1])
result = np.hstack([X, Y, Z, V, C, M, DIAM, TOB])
return result
[docs]
def define_cylinder_x_dir(n_ions, r, x, charge, mass):
"""
Defines a cylinder with the cylinder axis parallel to the x-axis and the center of one face of the cylinder on
the origin of the coordinate system filled with random ions
The cylinder cross section, a disk, must be filled uniformly with ions, see
http://mathworld.wolfram.com/DiskPointPicking.html for details and argument how to do this.
:param int n_ions: The number of ions in the cylinder
:param float r: the radius of the cylinder
:param float x: length ("radius") in x direction
:param float charge: Charge of the ions in the generated cylinder
:param float mass: Mass of the ions in the generated cylinder
:return: Array with parameters of the particles in the defined cylinder. Columns are:
[x,y,z, vx, vy, vz, charge, mass, time of birth]
"""
R = np.sqrt(np.random.rand(n_ions, 1)) * r
phi = np.random.rand(n_ions, 1) * 2 * np.pi
X = (np.random.rand(n_ions, 1)-0.5) * 2.0 * x
Z = np.cos(phi) * R
Y = np.sin(phi) * R
V = np.zeros([n_ions, 3])
C = np.zeros([n_ions, 1]) + charge
M = np.zeros([n_ions, 1]) + mass
DIAM = np.zeros([n_ions, 1])
TOB = np.zeros([n_ions, 1])
result = np.hstack([X, Y, Z, V, C, M, DIAM, TOB])
return result