Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add support for electron impact ionization in binary collisions (DSMC module) #5388

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -268,32 +268,40 @@ def setup_run(self):
#######################################################################

cross_sec_direc = "../../../../warpx-data/MCC_cross_sections/He/"
electron_colls = picmi.MCCCollisions(
name="coll_elec",
species=self.electrons,
background_density=self.gas_density,
background_temperature=self.gas_temp,
background_mass=self.ions.mass,
ndt=self.mcc_subcycling_steps,
scattering_processes={
"elastic": {
"cross_section": cross_sec_direc + "electron_scattering.dat"
},
"excitation1": {
"cross_section": cross_sec_direc + "excitation_1.dat",
"energy": 19.82,
},
"excitation2": {
"cross_section": cross_sec_direc + "excitation_2.dat",
"energy": 20.61,
},
"ionization": {
"cross_section": cross_sec_direc + "ionization.dat",
"energy": 24.55,
"species": self.ions,
},
electron_scattering_processes = {
"elastic": {"cross_section": cross_sec_direc + "electron_scattering.dat"},
"excitation1": {
"cross_section": cross_sec_direc + "excitation_1.dat",
"energy": 19.82,
},
)
"excitation2": {
"cross_section": cross_sec_direc + "excitation_2.dat",
"energy": 20.61,
},
"ionization": {
"cross_section": cross_sec_direc + "ionization.dat",
"energy": 24.55,
"species": self.ions,
},
}

if self.dsmc:
electron_colls = picmi.DSMCCollisions(
name="coll_elec",
species=[self.electrons, self.neutrals],
ndt=5,
scattering_processes=electron_scattering_processes,
)
else:
electron_colls = picmi.MCCCollisions(
name="coll_elec",
species=self.electrons,
background_density=self.gas_density,
background_temperature=self.gas_temp,
background_mass=self.ions.mass,
ndt=self.mcc_subcycling_steps,
scattering_processes=electron_scattering_processes,
)

ion_scattering_processes = {
"elastic": {"cross_section": cross_sec_direc + "ion_scattering.dat"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,15 @@ void CollisionPairFilter (const amrex::ParticleReal u1x, const amrex::ParticleRe

// Evaluate the cross-section for each scattering process to determine
// the total collision probability.
// TODO: Why only 4 previously?
// Note: this is brittle because it is not AMREX_ALWAYS_ASSERT_WITH_MESSAGE
// In practice, a user can specify e.g. 10 scattering processes and not get an error.
// We should check this in the constructor of DSMCFunc ahead of time.
AMREX_ASSERT_WITH_MESSAGE(
(process_count < 4), "Too many scattering processes in DSMC routine."
(process_count < 5), "Too many scattering processes in DSMC routine."
);
int coll_type[4] = {0, 0, 0, 0};
amrex::ParticleReal sigma_sums[4] = {0._prt, 0._prt, 0._prt, 0._prt};
int coll_type[5] = {0, 0, 0, 0, 0};
amrex::ParticleReal sigma_sums[5] = {0._prt, 0._prt, 0._prt, 0._prt, 0._prt};
for (int ii = 0; ii < process_count; ii++) {
auto const& scattering_process = scattering_processes[ii];
coll_type[ii] = int(scattering_process.m_type);
Expand Down
4 changes: 4 additions & 0 deletions Source/Particles/Collision/BinaryCollision/DSMC/DSMCFunc.H
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ public:
private:
amrex::Vector<ScatteringProcess> m_scattering_processes;
amrex::Gpu::DeviceVector<ScatteringProcess::Executor> m_scattering_processes_exe;

bool ionization_flag = false;

amrex::Vector<std::string> m_species_names; // TODO: is this the right place to store this?
bool m_isSameSpecies;

Executor m_exe;
Expand Down
22 changes: 17 additions & 5 deletions Source/Particles/Collision/BinaryCollision/DSMC/DSMCFunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,32 @@ DSMCFunc::DSMCFunc (
WARPX_ALWAYS_ASSERT_WITH_MESSAGE(process.type() != ScatteringProcessType::INVALID,
"Cannot add an unknown scattering process type");

// if the scattering process is ionization get the secondary species
// only one ionization process is supported
if (process.type() == ScatteringProcessType::IONIZATION) {
WARPX_ALWAYS_ASSERT_WITH_MESSAGE(!ionization_flag,
"Background MCC only supports a single ionization process");
ionization_flag = true;

// TODO: Add a check that the first species is the electron species
// (This should be done for impact ionization with MCC too)

std::string secondary_species;
pp_collision_name.get("ionization_species", secondary_species);
m_species_names.push_back(secondary_species);
}
m_scattering_processes.push_back(std::move(process));
}

const int process_count = static_cast<int>(m_scattering_processes.size());

// Store ScatteringProcess::Executor(s).
#ifdef AMREX_USE_GPU
amrex::Gpu::HostVector<ScatteringProcess::Executor> h_scattering_processes_exe;
for (auto const& p : m_scattering_processes) {
h_scattering_processes_exe.push_back(p.executor());
}
m_scattering_processes_exe.resize(process_count);
m_scattering_processes_exe.resize(h_scattering_processes_exe.size());
amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, h_scattering_processes_exe.begin(),
h_scattering_processes_exe.end(), m_scattering_processes_exe.begin());
h_scattering_processes_exe.end(), m_scattering_processes_exe.begin());
amrex::Gpu::streamSynchronize();
#else
for (auto const& p : m_scattering_processes) {
Expand All @@ -75,6 +87,6 @@ DSMCFunc::DSMCFunc (

// Link executor to appropriate ScatteringProcess executors
m_exe.m_scattering_processes_data = m_scattering_processes_exe.data();
m_exe.m_process_count = process_count;
m_exe.m_process_count = static_cast<int>(m_scattering_processes_exe.size());
m_exe.m_isSameSpecies = m_isSameSpecies;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,37 @@ SplitAndScatterFunc::SplitAndScatterFunc (const std::string& collision_name,
{
const amrex::ParmParse pp_collision_name(collision_name);

// Check if ionization is one of the scattering processes
bool ionization_flag = false;
amrex::Vector<std::string> scattering_process_names;
pp_collision_name.queryarr("scattering_processes", scattering_process_names);
for (const auto& scattering_process : scattering_process_names) {
if (scattering_process.find("excitation") != std::string::npos) {
RemiLehe marked this conversation as resolved.
Show resolved Hide resolved
ionization_flag = true;
}
}

if (m_collision_type == CollisionType::DSMC)
{
// here we can add logic to deal with cases where products are created,
// for example with impact ionization
m_num_product_species = 2;
m_num_products_host.push_back(1);
m_num_products_host.push_back(1);
if (ionization_flag) {
// Product species include the ion
// TODO: as an alternative, we could use the runtime attribute `ionization_level` for this species
m_num_product_species = 3;
m_num_products_host.push_back(2); // electron species:
Copy link
Member Author

@RemiLehe RemiLehe Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted to fit impact ionization within the DSMC framework, but I now realize that this is making the book keeping complicated:

The amount of memory to be allocated (in SplitAndScatter) after the first pass (Executor, that checks which type of reaction is occurring for each pair) will depend on the type of reaction for each pair. (Because, if a pair does impact ionization, we need to allocate space for the scattered electron, the produced electron and the ionized species, whereas if a pair does one of the other reactions, we allocate space only for the scattered electron and the scattered target.) As a consequence, the offset (in SplitAndScatter will depend on the type of reaction.)

To simplify things, here I will always allocate space for the ionized species and the new electron, but if the reaction is not impact ionization, I will set the ID of the ionized species and the new electron to invalid.

// potentially 2 products per reaction: the scattered incoming electron, and the new electron from ionization
m_num_products_host.push_back(1);
m_num_products_host.push_back(1); // corresponds to the ionized species
} else {
m_num_product_species = 2;
m_num_products_host.push_back(1);
m_num_products_host.push_back(1);
}

#ifndef AMREX_USE_GPU
// On CPU, the device vector can be filled immediately
m_num_products_device.push_back(1);
m_num_products_device.push_back(1);
for (int i = 0; i < m_num_product_species; i++) {
m_num_products_device.push_back(m_num_products_host[i]);
}
#endif
}
else
Expand Down