光線を注入面からレイキャストし、最初の命中要素から光電子を放出する。
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(mesh_type), | intent(in) | :: | mesh |
交差判定に使うメッシュ。 |
||
| type(sim_config), | intent(in) | :: | sim |
ボックス境界条件とバッチ時間を含むシミュレーション設定。 |
||
| character(len=*), | intent(in) | :: | inject_face |
照射面識別子( |
||
| real(kind=dp), | intent(in) | :: | pos_low(3) | |||
| real(kind=dp), | intent(in) | :: | pos_high(3) | |||
| real(kind=dp), | intent(in) | :: | ray_direction(3) | |||
| real(kind=dp), | intent(in) | :: | m_particle |
粒子1個あたりの質量 [kg]。 |
||
| real(kind=dp), | intent(in) | :: | temperature_k |
粒子1個あたりの質量 [kg]。 放出温度 [K]。 |
||
| real(kind=dp), | intent(in) | :: | normal_drift_speed |
粒子1個あたりの質量 [kg]。 放出温度 [K]。 放出法線方向のシフト速度 [m/s]。 |
||
| real(kind=dp), | intent(in) | :: | emit_current_density_a_m2 |
レイ垂直面基準の放出電流面密度 [A/m^2]。 |
||
| real(kind=dp), | intent(in) | :: | q_particle |
レイ垂直面基準の放出電流面密度 [A/m^2]。 粒子1個あたりの電荷 [C]。 |
||
| integer(kind=i32), | intent(in) | :: | rays_per_batch |
このrankで発射するレイ本数。 |
||
| real(kind=dp), | intent(out) | :: | x(:,:) | |||
| real(kind=dp), | intent(out) | :: | v(:,:) | |||
| real(kind=dp), | intent(out) | :: | w(:) | |||
| integer(kind=i32), | intent(out) | :: | n_emit |
実際に放出された粒子数( |
||
| integer(kind=i32), | intent(out), | optional | :: | emit_elem_idx(:) | ||
| integer(kind=i32), | intent(in), | optional | :: | global_rays_per_batch |
全rank合計のレイ本数(省略時は |
|
| real(kind=dp), | intent(in), | optional | :: | vmin_normal |
放出法線速度の下限 [m/s](省略時は 0)。 |
subroutine sample_photo_raycast_particles( & mesh, sim, inject_face, pos_low, pos_high, ray_direction, m_particle, temperature_k, normal_drift_speed, & emit_current_density_a_m2, q_particle, rays_per_batch, x, v, w, n_emit, emit_elem_idx, global_rays_per_batch, & vmin_normal & ) type(mesh_type), intent(in) :: mesh type(sim_config), intent(in) :: sim character(len=*), intent(in) :: inject_face real(dp), intent(in) :: pos_low(3), pos_high(3) real(dp), intent(in) :: ray_direction(3) real(dp), intent(in) :: m_particle, temperature_k, normal_drift_speed real(dp), intent(in) :: emit_current_density_a_m2, q_particle integer(i32), intent(in) :: rays_per_batch real(dp), intent(out) :: x(:, :) real(dp), intent(out) :: v(:, :) real(dp), intent(out) :: w(:) integer(i32), intent(out) :: n_emit integer(i32), intent(out), optional :: emit_elem_idx(:) integer(i32), intent(in), optional :: global_rays_per_batch real(dp), intent(in), optional :: vmin_normal real(dp), parameter :: eps = 1.0d-12 integer(i32) :: i, total_rays integer(i32) :: bounce_count integer :: axis_n, axis_t1, axis_t2 real(dp) :: boundary_value, inward_normal(3), launch_dir(3), launch_dir_norm, inward_dot real(dp) :: launch_area, projected_area, w_hit, sigma real(dp) :: ray_pos(3), ray_dir(3), seg_end(3), boundary_probe(3), boundary_dir(3) real(dp) :: surf_normal(3), tangent1(3), tangent2(3) real(dp), allocatable :: u(:, :) logical :: reached_boundary, alive, escaped_boundary type(hit_info) :: hit if (size(x, 1) /= 3 .or. size(v, 1) /= 3) error stop "photo_raycast particle arrays must have first dimension 3" if (size(x, 2) < rays_per_batch .or. size(v, 2) < rays_per_batch) then error stop "photo_raycast x/v arrays are smaller than rays_per_batch" end if if (size(w) < rays_per_batch) error stop "photo_raycast w array is smaller than rays_per_batch" if (present(emit_elem_idx)) then if (size(emit_elem_idx) < rays_per_batch) error stop "photo_raycast emit_elem_idx is smaller than rays_per_batch" emit_elem_idx = -1_i32 end if if (rays_per_batch <= 0_i32) error stop "rays_per_batch must be > 0" total_rays = rays_per_batch if (present(global_rays_per_batch)) total_rays = global_rays_per_batch if (total_rays <= 0_i32) error stop "global_rays_per_batch must be > 0" if (.not. sim%use_box) error stop "photo_raycast requires sim.use_box = true" if (sim%batch_duration <= 0.0_dp) error stop "photo_raycast requires sim.batch_duration > 0" if (m_particle <= 0.0_dp) error stop "m_particle must be > 0" if (temperature_k < 0.0_dp) error stop "temperature_k must be >= 0" if (emit_current_density_a_m2 <= 0.0_dp) error stop "emit_current_density_a_m2 must be > 0" if (abs(q_particle) <= 0.0_dp) error stop "q_particle must be non-zero" call resolve_face_geometry(sim%box_min, sim%box_max, inject_face, axis_n, boundary_value, inward_normal) call resolve_face_axes(inject_face, axis_t1, axis_t2) launch_dir = ray_direction launch_dir_norm = sqrt(sum(launch_dir*launch_dir)) if (launch_dir_norm <= 0.0_dp) error stop "ray_direction norm must be > 0" launch_dir = launch_dir/launch_dir_norm inward_dot = dot_product(launch_dir, inward_normal) if (inward_dot <= 0.0_dp) error stop "ray_direction must point inward from inject_face" launch_area = compute_face_area_from_bounds(inject_face, pos_low, pos_high) if (launch_area <= 0.0_dp) error stop "photo_raycast opening area must be positive" projected_area = launch_area*abs(inward_dot) w_hit = emit_current_density_a_m2*projected_area*sim%batch_duration/(abs(q_particle)*real(total_rays, dp)) if (w_hit <= 0.0_dp) error stop "photo_raycast produced invalid w_hit" sigma = sqrt(k_boltzmann*temperature_k/m_particle) n_emit = 0_i32 x = 0.0_dp v = 0.0_dp w = 0.0_dp allocate (u(2, rays_per_batch)) call random_number(u) do i = 1_i32, rays_per_batch ray_pos = 0.0_dp ray_pos(axis_n) = boundary_value ray_pos(axis_t1) = pos_low(axis_t1) + (pos_high(axis_t1) - pos_low(axis_t1))*u(1, i) ray_pos(axis_t2) = pos_low(axis_t2) + (pos_high(axis_t2) - pos_low(axis_t2))*u(2, i) ray_dir = launch_dir ray_pos = ray_pos + ray_dir*eps alive = .true. bounce_count = 0_i32 do while (alive .and. bounce_count <= sim%raycast_max_bounce) call step_ray_to_boundary(sim%box_min, sim%box_max, ray_pos, ray_dir, seg_end, reached_boundary) if (.not. reached_boundary) exit call find_first_hit( & mesh, ray_pos, seg_end, hit, sim=sim, box_min=sim%box_min, box_max=sim%box_max, require_elem_inside=.true. & ) if (hit%has_hit) then if (n_emit >= int(size(w), i32)) error stop "photo_raycast emitted particle buffer overflow" n_emit = n_emit + 1_i32 surf_normal = mesh%normals(:, hit%elem_idx) if (dot_product(surf_normal, ray_dir) > 0.0_dp) surf_normal = -surf_normal call build_tangent_basis(surf_normal, tangent1, tangent2) if (present(vmin_normal)) then call sample_photo_emission_velocity( & sigma, normal_drift_speed, surf_normal, tangent1, tangent2, v(:, n_emit), vmin_normal=vmin_normal & ) else call sample_photo_emission_velocity(sigma, normal_drift_speed, surf_normal, tangent1, tangent2, v(:, n_emit)) end if if (trim(lower_ascii(sim%field_bc_mode)) == 'periodic2') then x(:, n_emit) = hit%pos_wrapped + surf_normal*eps else x(:, n_emit) = hit%pos + surf_normal*eps end if w(n_emit) = w_hit if (present(emit_elem_idx)) emit_elem_idx(n_emit) = hit%elem_idx exit end if boundary_probe = seg_end + ray_dir*eps boundary_dir = ray_dir escaped_boundary = .false. call apply_box_boundary(sim, boundary_probe, boundary_dir, alive, escaped_boundary) if (.not. alive) exit ray_dir = boundary_dir/sqrt(sum(boundary_dir*boundary_dir)) ray_pos = boundary_probe + ray_dir*eps bounce_count = bounce_count + 1_i32 end do end do end subroutine sample_photo_raycast_particles