Skip to content

Add particle BC: angle_threshold (thermalize vs reflective by incidence angle)#862

Open
PLASMAsDJ wants to merge 3 commits intoSmileiPIC:masterfrom
PLASMAsDJ:particle-bc-angle-threshold
Open

Add particle BC: angle_threshold (thermalize vs reflective by incidence angle)#862
PLASMAsDJ wants to merge 3 commits intoSmileiPIC:masterfrom
PLASMAsDJ:particle-bc-angle-threshold

Conversation

@PLASMAsDJ
Copy link
Copy Markdown

@PLASMAsDJ PLASMAsDJ commented Feb 6, 2026

Summary
This PR adds a new particle boundary condition, angle_threshold, which switches between thermalize and reflective depending on the particle incidence angle relative to the outward normal of the boundary.

Motivation / reference
This implementation is inspired by the boundary-treatment approach described in:
[Chen, H., et al, Geophysical research letters, 52, e2025GL116478 (2025)]

Definition
Let n be the outward normal vector of the boundary. The particle velocity is decomposed into components parallel and perpendicular to n, and the incidence angle is defined as:

α = tan⁻¹(v⊥ / v∥)

If α ≤ α₀ (user-defined threshold), the particle is handled with thermalize; otherwise, reflective is applied.

Implementation
In the updated implementation:

  • the threshold angle is parsed once during initialization,
  • the parsed value is stored as a species parameter,
  • and the runtime boundary-condition routine reuses the stored value instead of parsing the boundary-condition string repeatedly.

Modified files

  • src/ParticleBC/BoundaryConditionType.cpp
  • src/ParticleBC/BoundaryConditionType.h
  • src/ParticleBC/PartBoundCond.cpp
  • src/Python/pyinit.py
  • src/Species/Species.h
  • src/Species/SpeciesFactory.h

Tests
I rechecked the basic behavior with 1D and 2D test runs after the refactor.
If needed, I can provide a minimal namelist and a small test to reproduce the results.

Example namelists:

Note
This PR is mainly intended to get feedback on the implementation approach and code structure.

Namelist usage
1D:
boundary_conditions = [
["angle_threshold", "angle_threshold"],
]
threshold_angle = [
[a_xmin, a_xmax],
]

2D (x and y both use angle_threshold):
boundary_conditions = [
["angle_threshold", "angle_threshold"],
["angle_threshold", "angle_threshold"],
]
threshold_angle = [
[a_xmin, a_xmax],
[a_ymin, a_ymax],
]

2D (only y uses angle_threshold):
boundary_conditions = [
["reflective", "reflective"],
["angle_threshold", "angle_threshold"],
]
threshold_angle = [
[a_ymin, a_ymax],
]

@PLASMAsDJ PLASMAsDJ closed this Feb 6, 2026
@PLASMAsDJ PLASMAsDJ reopened this Feb 6, 2026
@PLASMAsDJ
Copy link
Copy Markdown
Author

This pull request is opened primarily for review/evaluation and to provide an easy-to-read diff of the proposed changes.
There is no expectation that it must be merged; feedback is very welcome.

Copy link
Copy Markdown
Contributor

@mccoys mccoys left a comment

Choose a reason for hiding this comment

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

I didn't check fully your code but there are some comments

namespace {

// Trim whitespace
inline std::string strip_spaces( std::string s )
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Inline will likely not be used here

// "angle_threshold:0.05" -> 0.05 radians (default unit)
// "angle_threshold:0.05rad" -> 0.05 radians
// If no parameter is provided ("angle_threshold"), defaults to pi/2 (≈ always thermalize).
inline double parse_angle0_rad_from_bc( const std::string &bc )
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same

// If no parameter is provided ("angle_threshold"), defaults to pi/2 (≈ always thermalize).
inline double parse_angle0_rad_from_bc( const std::string &bc )
{
const double pi = std::acos( -1.0 );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can use M_PI, i believe, for pi. To be checked

std::string s = strip_spaces( bc.substr( pos + 1 ) );
bool is_deg = false;
bool is_rad = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I suggest not giving the choice of degrees. This makes the input too difficult to benchmark when there are too many possibilities. Users can easily switch from the python script

#pragma omp declare target
#endif

inline double compute_alpha( int direction,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Again I am not sure you can defined inline functions here. If needed on gpu, this might cost something

{
// Decide alpha0 from the BC string on this side
const std::string &bc = species->boundary_conditions_[direction][0];
const double alpha0 = parse_angle0_rad_from_bc( bc );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These tests should not be done here. Parse the string art the initialization, otherwise this will be to slow

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@mccoys Thanks for the comment. I was able to address the other comments rather straightforwardly, but this one seemed a bit more involved, so I just wanted to check if I understood your suggestion correctly.

Would you recommend parsing the boundary-condition string only once during initialization, then storing the parsed angle-threshold value as a parameter to be reused at runtime, instead of parsing it inside BoundaryConditionType.cpp?

If that is what you mean, I was thinking it might require a small refactor on the initialization side, for example by storing the value in Species and setting it from SpeciesFactory. Would that be the right direction?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes that sounds exactly right

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@mccoys Thanks again for the suggestion. I updated the implementation accordingly, and there have been several revisions to the code. I would be very grateful if you could review it again when you have time.

I agree that optimization is important. At the same time, my main focus here is to implement the intended particle boundary-condition mechanism, where the particle behavior at the boundary is determined by the incidence angle with respect to the boundary normal. I would also appreciate your feedback on whether this mechanism is represented appropriately in the current implementation.

The threshold angle is now parsed once during initialization, stored as a species parameter, and reused at runtime instead of being parsed inside BoundaryConditionType.cpp.

I also updated the PR description and rechecked the behavior with 1D and 2D test runs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants