bem_simulator_charge.f90 Source File


This file depends on

sourcefile~~bem_simulator_charge.f90~~EfferentGraph sourcefile~bem_simulator_charge.f90 bem_simulator_charge.f90 sourcefile~bem_charge_ledger.f90 bem_charge_ledger.f90 sourcefile~bem_simulator_charge.f90->sourcefile~bem_charge_ledger.f90 sourcefile~bem_runtime_modes.f90 bem_runtime_modes.f90 sourcefile~bem_simulator_charge.f90->sourcefile~bem_runtime_modes.f90 sourcefile~bem_simulator.f90 bem_simulator.f90 sourcefile~bem_simulator_charge.f90->sourcefile~bem_simulator.f90 sourcefile~bem_simulator_workspace.f90 bem_simulator_workspace.f90 sourcefile~bem_simulator_charge.f90->sourcefile~bem_simulator_workspace.f90 sourcefile~bem_surface_closure_contract.f90 bem_surface_closure_contract.f90 sourcefile~bem_simulator_charge.f90->sourcefile~bem_surface_closure_contract.f90 sourcefile~bem_kinds.f90 bem_kinds.f90 sourcefile~bem_charge_ledger.f90->sourcefile~bem_kinds.f90 sourcefile~bem_runtime_modes.f90->sourcefile~bem_kinds.f90 sourcefile~bem_string_utils.f90 bem_string_utils.f90 sourcefile~bem_runtime_modes.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_simulator.f90->sourcefile~bem_charge_ledger.f90 sourcefile~bem_simulator.f90->sourcefile~bem_simulator_workspace.f90 sourcefile~bem_simulator.f90->sourcefile~bem_surface_closure_contract.f90 sourcefile~bem_app_config.f90 bem_app_config.f90 sourcefile~bem_simulator.f90->sourcefile~bem_app_config.f90 sourcefile~bem_app_config_runtime.f90 bem_app_config_runtime.f90 sourcefile~bem_simulator.f90->sourcefile~bem_app_config_runtime.f90 sourcefile~bem_collision.f90 bem_collision.f90 sourcefile~bem_simulator.f90->sourcefile~bem_collision.f90 sourcefile~bem_config_helpers.f90 bem_config_helpers.f90 sourcefile~bem_simulator.f90->sourcefile~bem_config_helpers.f90 sourcefile~bem_electrostatic_snapshot.f90 bem_electrostatic_snapshot.f90 sourcefile~bem_simulator.f90->sourcefile~bem_electrostatic_snapshot.f90 sourcefile~bem_external_boundary_contract.f90 bem_external_boundary_contract.f90 sourcefile~bem_simulator.f90->sourcefile~bem_external_boundary_contract.f90 sourcefile~bem_irradiation_cell_contract.f90 bem_irradiation_cell_contract.f90 sourcefile~bem_simulator.f90->sourcefile~bem_irradiation_cell_contract.f90 sourcefile~bem_simulator.f90->sourcefile~bem_kinds.f90 sourcefile~bem_mesh_identity.f90 bem_mesh_identity.f90 sourcefile~bem_simulator.f90->sourcefile~bem_mesh_identity.f90 sourcefile~bem_mpi.f90 bem_mpi.F90 sourcefile~bem_simulator.f90->sourcefile~bem_mpi.f90 sourcefile~bem_output_writer.f90 bem_output_writer.f90 sourcefile~bem_simulator.f90->sourcefile~bem_output_writer.f90 sourcefile~bem_particle_stepper.f90 bem_particle_stepper.f90 sourcefile~bem_simulator.f90->sourcefile~bem_particle_stepper.f90 sourcefile~bem_physics_config_types.f90 bem_physics_config_types.f90 sourcefile~bem_simulator.f90->sourcefile~bem_physics_config_types.f90 sourcefile~bem_run_outputs.f90 bem_run_outputs.f90 sourcefile~bem_simulator.f90->sourcefile~bem_run_outputs.f90 sourcefile~bem_status_codes.f90 bem_status_codes.f90 sourcefile~bem_simulator.f90->sourcefile~bem_status_codes.f90 sourcefile~bem_simulator.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_surface_current_model.f90 bem_surface_current_model.f90 sourcefile~bem_simulator.f90->sourcefile~bem_surface_current_model.f90 sourcefile~bem_surface_models.f90 bem_surface_models.f90 sourcefile~bem_simulator.f90->sourcefile~bem_surface_models.f90 sourcefile~bem_types.f90 bem_types.f90 sourcefile~bem_simulator.f90->sourcefile~bem_types.f90 sourcefile~bem_simulator_workspace.f90->sourcefile~bem_kinds.f90 sourcefile~bem_outflow_spectrum.f90 bem_outflow_spectrum.f90 sourcefile~bem_simulator_workspace.f90->sourcefile~bem_outflow_spectrum.f90 sourcefile~bem_surface_closure_contract.f90->sourcefile~bem_runtime_modes.f90 sourcefile~bem_surface_closure_contract.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config.f90->sourcefile~bem_app_config_runtime.f90 sourcefile~bem_app_config.f90->sourcefile~bem_physics_config_types.f90 sourcefile~bem_app_config.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_app_config_parser.f90 bem_app_config_parser.f90 sourcefile~bem_app_config.f90->sourcefile~bem_app_config_parser.f90 sourcefile~bem_app_config_types.f90 bem_app_config_types.f90 sourcefile~bem_app_config.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_app_config_mesh_runtime.f90 bem_app_config_mesh_runtime.f90 sourcefile~bem_app_config_runtime.f90->sourcefile~bem_app_config_mesh_runtime.f90 sourcefile~bem_app_config_particle_runtime.f90 bem_app_config_particle_runtime.f90 sourcefile~bem_app_config_runtime.f90->sourcefile~bem_app_config_particle_runtime.f90 sourcefile~bem_app_config_potential_runtime.f90 bem_app_config_potential_runtime.f90 sourcefile~bem_app_config_runtime.f90->sourcefile~bem_app_config_potential_runtime.f90 sourcefile~bem_collision.f90->sourcefile~bem_kinds.f90 sourcefile~bem_collision.f90->sourcefile~bem_status_codes.f90 sourcefile~bem_collision.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_collision.f90->sourcefile~bem_types.f90 sourcefile~bem_config_helpers.f90->sourcefile~bem_kinds.f90 sourcefile~bem_config_helpers.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_config_helpers.f90->sourcefile~bem_types.f90 sourcefile~bem_config_helpers.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_runtime_modes.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_kinds.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_mpi.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_physics_config_types.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_types.f90 sourcefile~bem_constants.f90 bem_constants.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_constants.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_reference.f90 bem_coulomb_fmm_periodic_nonzero_reference.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_coulomb_fmm_periodic_nonzero_reference.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_upper_vacuum.f90 bem_coulomb_fmm_periodic_nonzero_upper_vacuum.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_coulomb_fmm_periodic_nonzero_upper_vacuum.f90 sourcefile~bem_field_solver.f90 bem_field_solver.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_field_solver.f90 sourcefile~bem_panel_geometry.f90 bem_panel_geometry.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_panel_geometry.f90 sourcefile~bem_panel_kernel.f90 bem_panel_kernel.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_panel_kernel.f90 sourcefile~bem_periodic_zero_mode_eval.f90 bem_periodic_zero_mode_eval.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_periodic_zero_mode_eval.f90 sourcefile~bem_periodic_zero_mode_plan.f90 bem_periodic_zero_mode_plan.f90 sourcefile~bem_electrostatic_snapshot.f90->sourcefile~bem_periodic_zero_mode_plan.f90 sourcefile~bem_external_boundary_contract.f90->sourcefile~bem_kinds.f90 sourcefile~bem_external_boundary_contract.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_irradiation_cell_contract.f90->sourcefile~bem_kinds.f90 sourcefile~bem_irradiation_cell_contract.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_irradiation_cell_contract.f90->sourcefile~bem_types.f90 sourcefile~bem_irradiation_cell_contract.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_mesh_identity.f90->sourcefile~bem_kinds.f90 sourcefile~bem_mesh_identity.f90->sourcefile~bem_types.f90 sourcefile~bem_mpi.f90->sourcefile~bem_kinds.f90 sourcefile~bem_outflow_spectrum.f90->sourcefile~bem_kinds.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_charge_ledger.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_electrostatic_snapshot.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_kinds.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_surface_current_model.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_types.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_checkpoint_contract.f90 bem_checkpoint_contract.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_checkpoint_contract.f90 sourcefile~bem_filesystem.f90 bem_filesystem.f90 sourcefile~bem_output_writer.f90->sourcefile~bem_filesystem.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_collision.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_electrostatic_snapshot.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_external_boundary_contract.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_irradiation_cell_contract.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_kinds.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_status_codes.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_types.f90 sourcefile~bem_boundary.f90 bem_boundary.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_boundary.f90 sourcefile~bem_counter_rng.f90 bem_counter_rng.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_counter_rng.f90 sourcefile~bem_pusher.f90 bem_pusher.f90 sourcefile~bem_particle_stepper.f90->sourcefile~bem_pusher.f90 sourcefile~bem_physics_config_types.f90->sourcefile~bem_kinds.f90 sourcefile~bem_physics_config_types.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_physics_config_types.f90->sourcefile~bem_types.f90 sourcefile~bem_run_outputs.f90->sourcefile~bem_kinds.f90 sourcefile~bem_run_outputs.f90->sourcefile~bem_output_writer.f90 sourcefile~bem_run_outputs.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_periodic_checkpoint.f90 bem_periodic_checkpoint.f90 sourcefile~bem_run_outputs.f90->sourcefile~bem_periodic_checkpoint.f90 sourcefile~bem_status_codes.f90->sourcefile~bem_kinds.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_surface_closure_contract.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_config_helpers.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_irradiation_cell_contract.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_kinds.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_outflow_spectrum.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_surface_current_model.f90->sourcefile~bem_constants.f90 sourcefile~bem_surface_models.f90->sourcefile~bem_electrostatic_snapshot.f90 sourcefile~bem_surface_models.f90->sourcefile~bem_kinds.f90 sourcefile~bem_surface_models.f90->sourcefile~bem_types.f90 sourcefile~bem_types.f90->sourcefile~bem_kinds.f90 sourcefile~bem_types.f90->sourcefile~bem_outflow_spectrum.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_types.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_importers.f90 bem_importers.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_importers.f90 sourcefile~bem_mesh.f90 bem_mesh.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_mesh.f90 sourcefile~bem_panel_surface_sides.f90 bem_panel_surface_sides.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_panel_surface_sides.f90 sourcefile~bem_templates.f90 bem_templates.f90 sourcefile~bem_app_config_mesh_runtime.f90->sourcefile~bem_templates.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_physics_config_types.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_types.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_app_config_authoring.f90 bem_app_config_authoring.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_app_config_authoring.f90 sourcefile~bem_config_layout.f90 bem_config_layout.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_config_layout.f90 sourcefile~bem_config_toml.f90 bem_config_toml.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_config_toml.f90 sourcefile~bem_injection_flux.f90 bem_injection_flux.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_injection_flux.f90 sourcefile~bem_injection_geometry.f90 bem_injection_geometry.f90 sourcefile~bem_app_config_parser.f90->sourcefile~bem_injection_geometry.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_config_helpers.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_electrostatic_snapshot.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_external_boundary_contract.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_mpi.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_status_codes.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_types.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_app_config_potential_runtime.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_constants.f90 sourcefile~bem_injection.f90 bem_injection.f90 sourcefile~bem_app_config_particle_runtime.f90->sourcefile~bem_injection.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_config_helpers.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_electrostatic_snapshot.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_irradiation_cell_contract.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_types.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_app_config_potential_runtime.f90->sourcefile~bem_constants.f90 sourcefile~bem_app_config_types.f90->sourcefile~bem_external_boundary_contract.f90 sourcefile~bem_app_config_types.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_types.f90->sourcefile~bem_physics_config_types.f90 sourcefile~bem_app_config_types.f90->sourcefile~bem_types.f90 sourcefile~bem_app_config_types.f90->sourcefile~bem_filesystem.f90 sourcefile~bem_boundary.f90->sourcefile~bem_kinds.f90 sourcefile~bem_boundary.f90->sourcefile~bem_status_codes.f90 sourcefile~bem_boundary.f90->sourcefile~bem_types.f90 sourcefile~bem_checkpoint_contract.f90->sourcefile~bem_kinds.f90 sourcefile~bem_checkpoint_contract.f90->sourcefile~bem_filesystem.f90 sourcefile~bem_constants.f90->sourcefile~bem_kinds.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_reference.f90->sourcefile~bem_kinds.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_reference.f90->sourcefile~bem_types.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_reference.f90->sourcefile~bem_constants.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_reference.f90->sourcefile~bem_panel_geometry.f90 sourcefile~bem_triangle_quadrature.f90 bem_triangle_quadrature.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_reference.f90->sourcefile~bem_triangle_quadrature.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_upper_vacuum.f90->sourcefile~bem_kinds.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_upper_vacuum.f90->sourcefile~bem_types.f90 sourcefile~bem_coulomb_fmm_periodic_nonzero_upper_vacuum.f90->sourcefile~bem_constants.f90 sourcefile~bem_counter_rng.f90->sourcefile~bem_kinds.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_runtime_modes.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_kinds.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_mpi.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_physics_config_types.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_types.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_constants.f90 sourcefile~bem_coulomb_fmm_core.f90 bem_coulomb_fmm_core.f90 sourcefile~bem_field_solver.f90->sourcefile~bem_coulomb_fmm_core.f90 sourcefile~bem_panel_geometry.f90->sourcefile~bem_kinds.f90 sourcefile~bem_panel_kernel.f90->sourcefile~bem_kinds.f90 sourcefile~bem_panel_kernel.f90->sourcefile~bem_constants.f90 sourcefile~bem_panel_kernel.f90->sourcefile~bem_panel_geometry.f90 sourcefile~bem_panel_self_terms.f90 bem_panel_self_terms.f90 sourcefile~bem_panel_kernel.f90->sourcefile~bem_panel_self_terms.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_charge_ledger.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_kinds.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_mpi.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_output_writer.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_types.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_checkpoint_contract.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_filesystem.f90 sourcefile~bem_restart.f90 bem_restart.f90 sourcefile~bem_periodic_checkpoint.f90->sourcefile~bem_restart.f90 sourcefile~bem_periodic_zero_mode_eval.f90->sourcefile~bem_kinds.f90 sourcefile~bem_periodic_zero_mode_eval.f90->sourcefile~bem_constants.f90 sourcefile~bem_periodic_zero_mode_eval.f90->sourcefile~bem_periodic_zero_mode_plan.f90 sourcefile~bem_periodic_zero_mode_plan.f90->sourcefile~bem_kinds.f90 sourcefile~bem_periodic_zero_mode_plan.f90->sourcefile~bem_types.f90 sourcefile~bem_periodic_zero_mode_plan.f90->sourcefile~bem_constants.f90 sourcefile~bem_sorting.f90 bem_sorting.f90 sourcefile~bem_periodic_zero_mode_plan.f90->sourcefile~bem_sorting.f90 sourcefile~bem_pusher.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_authoring.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_authoring.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_app_config_authoring_types.f90 bem_app_config_authoring_types.f90 sourcefile~bem_app_config_authoring.f90->sourcefile~bem_app_config_authoring_types.f90 sourcefile~bem_config_layout.f90->sourcefile~bem_kinds.f90 sourcefile~bem_config_layout.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_config_layout.f90->sourcefile~bem_config_toml.f90 sourcefile~bem_config_layout_paths.f90 bem_config_layout_paths.f90 sourcefile~bem_config_layout.f90->sourcefile~bem_config_layout_paths.f90 sourcefile~bem_config_toml.f90->sourcefile~bem_kinds.f90 sourcefile~bem_config_toml.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_config_toml.f90->sourcefile~bem_types.f90 sourcefile~bem_config_toml.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_coulomb_fmm_core.f90->sourcefile~bem_kinds.f90 sourcefile~bem_coulomb_fmm_core.f90->sourcefile~bem_mpi.f90 sourcefile~bem_coulomb_fmm_types.f90 bem_coulomb_fmm_types.f90 sourcefile~bem_coulomb_fmm_core.f90->sourcefile~bem_coulomb_fmm_types.f90 sourcefile~bem_importers.f90->sourcefile~bem_kinds.f90 sourcefile~bem_importers.f90->sourcefile~bem_types.f90 sourcefile~bem_importers.f90->sourcefile~bem_mesh.f90 sourcefile~bem_injection.f90->sourcefile~bem_injection_flux.f90 sourcefile~bem_injection.f90->sourcefile~bem_injection_geometry.f90 sourcefile~bem_injection_random.f90 bem_injection_random.f90 sourcefile~bem_injection.f90->sourcefile~bem_injection_random.f90 sourcefile~bem_photoelectron_injection.f90 bem_photoelectron_injection.f90 sourcefile~bem_injection.f90->sourcefile~bem_photoelectron_injection.f90 sourcefile~bem_injection_flux.f90->sourcefile~bem_kinds.f90 sourcefile~bem_injection_flux.f90->sourcefile~bem_constants.f90 sourcefile~bem_injection_flux.f90->sourcefile~bem_counter_rng.f90 sourcefile~bem_injection_flux.f90->sourcefile~bem_injection_geometry.f90 sourcefile~bem_injection_flux.f90->sourcefile~bem_injection_random.f90 sourcefile~bem_injection_geometry.f90->sourcefile~bem_kinds.f90 sourcefile~bem_injection_geometry.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_mesh.f90->sourcefile~bem_kinds.f90 sourcefile~bem_mesh.f90->sourcefile~bem_types.f90 sourcefile~bem_mesh.f90->sourcefile~bem_panel_geometry.f90 sourcefile~bem_mesh.f90->sourcefile~bem_triangle_quadrature.f90 sourcefile~bem_periodic2_axes.f90 bem_periodic2_axes.f90 sourcefile~bem_mesh.f90->sourcefile~bem_periodic2_axes.f90 sourcefile~bem_panel_self_terms.f90->sourcefile~bem_kinds.f90 sourcefile~bem_panel_self_terms.f90->sourcefile~bem_panel_geometry.f90 sourcefile~bem_panel_surface_sides.f90->sourcefile~bem_kinds.f90 sourcefile~bem_panel_surface_sides.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_panel_surface_sides.f90->sourcefile~bem_types.f90 sourcefile~bem_restart.f90->sourcefile~bem_charge_ledger.f90 sourcefile~bem_restart.f90->sourcefile~bem_kinds.f90 sourcefile~bem_restart.f90->sourcefile~bem_mpi.f90 sourcefile~bem_restart.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_restart.f90->sourcefile~bem_types.f90 sourcefile~bem_restart.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_restart.f90->sourcefile~bem_checkpoint_contract.f90 sourcefile~bem_sorting.f90->sourcefile~bem_kinds.f90 sourcefile~bem_templates.f90->sourcefile~bem_kinds.f90 sourcefile~bem_templates.f90->sourcefile~bem_types.f90 sourcefile~bem_templates.f90->sourcefile~bem_mesh.f90 sourcefile~bem_triangle_quadrature.f90->sourcefile~bem_kinds.f90 sourcefile~bem_triangle_quadrature.f90->sourcefile~bem_constants.f90 sourcefile~bem_triangle_quadrature.f90->sourcefile~bem_panel_geometry.f90 sourcefile~bem_app_config_authoring_types.f90->sourcefile~bem_kinds.f90 sourcefile~bem_app_config_authoring_types.f90->sourcefile~bem_app_config_types.f90 sourcefile~bem_coulomb_fmm_types.f90->sourcefile~bem_kinds.f90 sourcefile~bem_coulomb_fmm_types.f90->sourcefile~bem_mpi.f90 sourcefile~bem_coulomb_fmm_types.f90->sourcefile~bem_panel_geometry.f90 sourcefile~bem_coulomb_fmm_types.f90->sourcefile~bem_periodic_zero_mode_plan.f90 sourcefile~bem_periodic_shell_mean.f90 bem_periodic_shell_mean.f90 sourcefile~bem_coulomb_fmm_types.f90->sourcefile~bem_periodic_shell_mean.f90 sourcefile~bem_injection_random.f90->sourcefile~bem_kinds.f90 sourcefile~bem_injection_random.f90->sourcefile~bem_constants.f90 sourcefile~bem_injection_random.f90->sourcefile~bem_counter_rng.f90 sourcefile~bem_random_stream.f90 bem_random_stream.f90 sourcefile~bem_injection_random.f90->sourcefile~bem_random_stream.f90 sourcefile~bem_periodic2_axes.f90->sourcefile~bem_kinds.f90 sourcefile~bem_periodic2_axes.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_periodic2_axes.f90->sourcefile~bem_types.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_collision.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_kinds.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_status_codes.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_string_utils.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_types.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_boundary.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_constants.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_counter_rng.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_injection_flux.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_injection_geometry.f90 sourcefile~bem_photoelectron_injection.f90->sourcefile~bem_injection_random.f90 sourcefile~bem_periodic_shell_mean.f90->sourcefile~bem_kinds.f90 sourcefile~bem_random_stream.f90->sourcefile~bem_kinds.f90 sourcefile~bem_random_stream.f90->sourcefile~bem_counter_rng.f90

Source Code

!> 電荷差分の確定反映、表面電流補正、バッチ電荷台帳を実装する。
submodule(bem_simulator) bem_simulator_charge
  use, intrinsic :: iso_fortran_env, only: error_unit
  use bem_charge_ledger, only: checked_accumulate_charge, finite_charge_sum
  use bem_runtime_modes, only: surface_charge_closure_id, surface_charge_closure_fixed_current, &
                               surface_charge_closure_neutral_return, closure_mode_none, closure_mode_target, &
                               closure_mode_tracked_source
  use bem_surface_closure_contract, only: species_closure_mode
  use bem_simulator_workspace, only: fixed_charge_absorbed, fixed_charge_emission, fixed_charge_escaped, &
                                     fixed_charge_barrier_return, neutral_charge_emitted, neutral_charge_absorbed, &
                                     neutral_charge_unresolved, neutral_charge_escaped, neutral_charge_soft, &
                                     neutral_charge_invalid, neutral_count_escaped, neutral_count_soft, &
                                     neutral_count_invalid
  implicit none
  real(dp), parameter :: neutral_return_max_unresolved_fraction = 0.05_dp
contains

  module procedure commit_batch_charge
  real(dp) :: norm_dq, norm_q
  if (.not. all(ieee_is_finite(mesh%q_elem))) then
    error stop 'committed surface charge is not finite before batch update.'
  end if
  workspace%q_before = mesh%q_elem
  if (workspace%charge_candidate_ready) then
    mesh%q_elem = workspace%candidate_charge
  else
    workspace%dq = sum(workspace%dq_thread, dim=2) + sum(workspace%photo_emission_dq, dim=2)
    call mpi_allreduce_sum_real_dp_array(mpi, workspace%dq)
    call validate_finite_charge_addition( &
      mesh%q_elem, workspace%dq, 'batch surface-charge update' &
      )
    mesh%q_elem = mesh%q_elem + workspace%dq
  end if
  if (.not. all(ieee_is_finite(mesh%q_elem))) then
    error stop 'batch surface-charge update produced a non-finite charge.'
  end if
  ! adaptive candidate は prepare 側で既に等電位化済み。
  if (conductor%active() .and. .not. workspace%charge_candidate_ready) then
    call conductor%relax(mesh, sim, snapshot)
  end if
  call validate_finite_charge_addition( &
    mesh%q_elem, -workspace%q_before, 'batch surface-charge difference' &
    )
  workspace%dq = mesh%q_elem - workspace%q_before
  if (.not. all(ieee_is_finite(workspace%dq))) then
    error stop 'batch surface-charge difference is not finite.'
  end if
  norm_dq = stable_l2_norm(workspace%dq)
  norm_q = stable_l2_norm(mesh%q_elem)
  rel = finite_nonnegative_ratio(norm_dq, max(norm_q, sim%q_floor))
  workspace%charge_candidate_ready = .false.
  end procedure commit_batch_charge

  module procedure prepare_adaptive_charge_candidate
  real(dp), allocatable :: committed_charge(:)
  workspace%dq = sum(workspace%dq_thread, dim=2) + sum(workspace%photo_emission_dq, dim=2)
  call mpi_allreduce_sum_real_dp_array(mpi, workspace%dq)
  call validate_finite_charge_addition( &
    mesh%q_elem, workspace%dq, 'adaptive candidate surface-charge update' &
    )
  workspace%candidate_charge = mesh%q_elem + workspace%dq
  if (.not. all(ieee_is_finite(workspace%candidate_charge))) then
    error stop 'adaptive candidate charge is not finite.'
  end if
  if (conductor%active()) then
    ! 電位ステップの判定は再配分後の候補電荷で行う。snapshot は確定電荷の状態へ戻す。
    committed_charge = mesh%q_elem
    mesh%q_elem = workspace%candidate_charge
    call conductor%relax(mesh, sim, snapshot)
    workspace%candidate_charge = mesh%q_elem
    mesh%q_elem = committed_charge
    call snapshot%refresh(mesh)
  end if
  workspace%charge_candidate_ready = .true.
  end procedure prepare_adaptive_charge_candidate

  !> Finiteな二項の加算が表現可能範囲を越えないことを、演算前に検証する。
  subroutine validate_finite_charge_addition(base, increment, operation)
    real(dp), intent(in) :: base(:), increment(:)
    character(len=*), intent(in) :: operation
    integer :: i

    if (.not. all(ieee_is_finite(base)) .or. .not. all(ieee_is_finite(increment))) then
      error stop trim(operation)//' contains a non-finite operand.'
    end if
    do i = 1, size(base)
      if (increment(i) > 0.0_dp .and. base(i) > huge(base(i)) - increment(i)) then
        error stop trim(operation)//' overflowed.'
      end if
      if (increment(i) < 0.0_dp .and. base(i) < -huge(base(i)) - increment(i)) then
        error stop trim(operation)//' overflowed.'
      end if
    end do
  end subroutine validate_finite_charge_addition

  !> 二乗の中間overflowを避けて有限vectorのL2 normを返す。
  pure real(dp) function stable_l2_norm(values) result(norm)
    real(dp), intent(in) :: values(:)
    real(dp) :: scale, unit_norm

    if (size(values) == 0) then
      norm = 0.0_dp
      return
    end if
    scale = maxval(abs(values))
    if (scale == 0.0_dp) then
      norm = 0.0_dp
      return
    end if
    unit_norm = sqrt(sum((values/scale)*(values/scale)))
    if (scale > huge(norm)/unit_norm) then
      norm = huge(norm)
    else
      norm = scale*unit_norm
      if (.not. ieee_is_finite(norm)) norm = huge(norm)
    end if
  end function stable_l2_norm

  !> 非負の有限比をoverflow時は最大有限値へ飽和させる。
  pure real(dp) function finite_nonnegative_ratio(numerator, denominator) result(ratio)
    real(dp), intent(in) :: numerator, denominator

    if (numerator == 0.0_dp) then
      ratio = 0.0_dp
    else if (denominator < 1.0_dp .and. numerator > huge(ratio)*denominator) then
      ratio = huge(ratio)
    else
      ratio = numerator/denominator
      if (.not. ieee_is_finite(ratio)) ratio = huge(ratio)
    end if
  end function finite_nonnegative_ratio

  module procedure apply_neutral_return_surface_closure
  integer(i32) :: i, species_idx, elem_idx, n, terminal_count
  integer(i64) :: escaped_count, soft_count, invalid_count
  real(dp) :: macro_charge, emitted_charge, absorbed_charge, unresolved_charge
  real(dp) :: escaped_charge, soft_charge, invalid_charge
  real(dp) :: charge_scale, charge_tolerance
  real(dp) :: weight_scale, correction_charge, unresolved_fraction
  logical :: has_neutral_return
  logical :: neutral_return_species(app%n_particle_species)

  n = app%n_particle_species
  ! 種ごとの closure をバッチの先頭で一度だけ解決し、粒子ループでは文字列を比べない。
  do species_idx = 1_i32, n
    neutral_return_species(species_idx) = &
      surface_charge_closure_id(app%particle_species(species_idx)%surface_charge_closure) == &
      surface_charge_closure_neutral_return
  end do
  has_neutral_return = .false.
  do species_idx = 1_i32, n
    if (.not. app%particle_species(species_idx)%enabled) cycle
    if (neutral_return_species(species_idx)) then
      has_neutral_return = .true.
    end if
  end do
  if (.not. has_neutral_return) return
  if (.not. app%sim%use_box) error stop 'neutral_return requires a finite box.'

  workspace%neutral_return_charge_values = 0.0_dp
  workspace%neutral_return_terminal_counts = 0_i64
  do i = 1_i32, pcls_batch%n
    species_idx = pcls_batch%species_id(i)
    if (.not. neutral_return_species(species_idx)) cycle
    macro_charge = pcls_batch%q(i)*pcls_batch%w(i)
    if (.not. ieee_is_finite(macro_charge) .or. macro_charge >= 0.0_dp .or. i > fresh_particle_count) then
      call count_invalid_terminal()
      cycle
    end if
    workspace%neutral_return_charge_values(species_idx, neutral_charge_emitted) = &
      workspace%neutral_return_charge_values(species_idx, neutral_charge_emitted) + macro_charge
    terminal_count = merge(1_i32, 0_i32, workspace%absorbed_flag(i)) + &
                     merge(1_i32, 0_i32, workspace%escaped_boundary_flag(i)) + &
                     merge(1_i32, 0_i32, workspace%soft_discarded_boundary_flag(i)) + &
                     merge(1_i32, 0_i32, pcls_batch%alive(i))
    if (terminal_count /= 1_i32) then
      call count_invalid_terminal()
    else if (workspace%absorbed_flag(i)) then
      elem_idx = workspace%absorbed_element(i)
      if (elem_idx >= 1_i32 .and. elem_idx <= size(workspace%dq_thread, 1)) then
        workspace%neutral_return_charge_values(species_idx, neutral_charge_absorbed) = &
          workspace%neutral_return_charge_values(species_idx, neutral_charge_absorbed) + macro_charge
      else
        call count_invalid_terminal()
      end if
    else if (workspace%escaped_boundary_flag(i)) then
      workspace%neutral_return_charge_values(species_idx, neutral_charge_escaped) = &
        workspace%neutral_return_charge_values(species_idx, neutral_charge_escaped) + macro_charge
      workspace%neutral_return_terminal_counts(species_idx, neutral_count_escaped) = &
        workspace%neutral_return_terminal_counts(species_idx, neutral_count_escaped) + 1_i64
    else if (workspace%soft_discarded_boundary_flag(i)) then
      workspace%neutral_return_charge_values(species_idx, neutral_charge_soft) = &
        workspace%neutral_return_charge_values(species_idx, neutral_charge_soft) + macro_charge
      workspace%neutral_return_terminal_counts(species_idx, neutral_count_soft) = &
        workspace%neutral_return_terminal_counts(species_idx, neutral_count_soft) + 1_i64
    else
      workspace%neutral_return_charge_values(species_idx, neutral_charge_unresolved) = &
        workspace%neutral_return_charge_values(species_idx, neutral_charge_unresolved) + macro_charge
    end if
  end do

  call allreduce_sum_real_matrix(mpi, workspace%neutral_return_charge_values)
  call allreduce_sum_i64_matrix(mpi, workspace%neutral_return_terminal_counts)
  workspace%neutral_return_emitted_charge = workspace%neutral_return_charge_values(:, neutral_charge_emitted)
  workspace%neutral_return_absorbed_charge = workspace%neutral_return_charge_values(:, neutral_charge_absorbed)
  workspace%neutral_return_unresolved_charge = workspace%neutral_return_charge_values(:, neutral_charge_unresolved)

  do species_idx = 1_i32, n
    if (.not. neutral_return_species(species_idx)) cycle
    emitted_charge = workspace%neutral_return_emitted_charge(species_idx)
    absorbed_charge = workspace%neutral_return_absorbed_charge(species_idx)
    unresolved_charge = workspace%neutral_return_unresolved_charge(species_idx)
    escaped_charge = workspace%neutral_return_charge_values(species_idx, neutral_charge_escaped)
    soft_charge = workspace%neutral_return_charge_values(species_idx, neutral_charge_soft)
    invalid_charge = workspace%neutral_return_charge_values(species_idx, neutral_charge_invalid)
    escaped_count = workspace%neutral_return_terminal_counts(species_idx, neutral_count_escaped)
    soft_count = workspace%neutral_return_terminal_counts(species_idx, neutral_count_soft)
    invalid_count = workspace%neutral_return_terminal_counts(species_idx, neutral_count_invalid)
    if (escaped_count > 0_i64 .or. soft_count > 0_i64 .or. invalid_count > 0_i64) then
      write (error_unit, '(a,i0,3(a,i0),3(a,es13.5))') &
        'neutral_return has unsupported terminal outcome for species ', species_idx, &
        ': escaped=', escaped_count, ' soft=', soft_count, ' invalid=', invalid_count, &
        ' escaped_C=', escaped_charge, ' soft_C=', soft_charge, ' invalid_C=', invalid_charge
      error stop 'neutral_return terminal outcome is unsupported.'
    end if
    charge_scale = max(abs(emitted_charge), abs(absorbed_charge), abs(unresolved_charge), tiny(1.0_dp))
    charge_tolerance = 4096.0_dp*epsilon(1.0_dp)*charge_scale
    if (abs(emitted_charge) <= charge_tolerance) cycle
    if (emitted_charge >= 0.0_dp .or. absorbed_charge >= -charge_tolerance) then
      error stop 'neutral_return charge signs are invalid.'
    end if
    weight_scale = emitted_charge/absorbed_charge
    correction_charge = emitted_charge - absorbed_charge
    unresolved_fraction = unresolved_charge/emitted_charge
    if (.not. all(ieee_is_finite([weight_scale, correction_charge, unresolved_fraction])) .or. &
        unresolved_fraction > neutral_return_max_unresolved_fraction + sqrt(epsilon(1.0_dp))) then
      error stop 'neutral_return unresolved fraction exceeds the applicability limit.'
    end if
    workspace%neutral_return_weight_scale(species_idx) = max(1.0_dp, weight_scale)
    workspace%neutral_return_correction(species_idx) = correction_charge
    workspace%neutral_return_unresolved_fraction(species_idx) = max(0.0_dp, unresolved_fraction)
  end do

  do i = 1_i32, fresh_particle_count
    species_idx = pcls_batch%species_id(i)
    if (.not. neutral_return_species(species_idx)) cycle
    if (.not. workspace%absorbed_flag(i)) cycle
    elem_idx = workspace%absorbed_element(i)
    macro_charge = pcls_batch%q(i)*pcls_batch%w(i)
    workspace%dq_thread(elem_idx, 1) = workspace%dq_thread(elem_idx, 1) + &
                                       (workspace%neutral_return_weight_scale(species_idx) - 1.0_dp)*macro_charge
  end do
contains

  !> 終端が一意でない、または電荷・吸収要素が不正な粒子を invalid として数える。
  subroutine count_invalid_terminal()
    workspace%neutral_return_charge_values(species_idx, neutral_charge_invalid) = &
      workspace%neutral_return_charge_values(species_idx, neutral_charge_invalid) + macro_charge
    workspace%neutral_return_terminal_counts(species_idx, neutral_count_invalid) = &
      workspace%neutral_return_terminal_counts(species_idx, neutral_count_invalid) + 1_i64
  end subroutine count_invalid_terminal
  end procedure apply_neutral_return_surface_closure

  module procedure apply_fixed_surface_current_closure
  integer(i32) :: species_idx, n, mode, pass
  real(dp) :: partner_charge, reflected_escape, reflected_return, folded_return_charge, charge_sign
  logical :: tracked_source, tally_needed
  logical :: fixed_current_species(app%n_particle_species), credits_reflected(app%n_particle_species)
  integer(i32) :: closure_modes(app%n_particle_species)
  !> 電子の種ごとに、放出電子側の目標が反射電子の分として減らした表面電荷(帯電符号の大きさ)。
  real(dp) :: reflected_credit(app%n_particle_species)
  !> 吸収した fresh 粒子の index を種ごとに元の順で並べたもの。種 s は `absorbed_start(s):absorbed_start(s+1)-1`。
  integer(i32), allocatable :: absorbed_by_species(:), absorbed_start(:)

  n = app%n_particle_species
  do species_idx = 1_i32, n
    fixed_current_species(species_idx) = &
      surface_charge_closure_id(app%particle_species(species_idx)%surface_charge_closure) == &
      surface_charge_closure_fixed_current
    closure_modes(species_idx) = species_closure_mode(current_model, species_idx, fixed_current_species(species_idx))
  end do
  credits_reflected = .false.
  if (allocated(current_model%absorbed_target_credits_tracked_escape)) then
    credits_reflected = current_model%absorbed_target_credits_tracked_escape(1:n)
  end if
  reflected_credit = 0.0_dp
  workspace%fixed_escape_closed_charge = 0.0_dp
  workspace%fixed_escape_unclosed_charge = 0.0_dp
  ! Without a closed species or another reader the raw tallies, their particle pass and the MPI sum are skipped;
  ! the tallies and the surface budget keep the zeros of reset_before_injection.
  tally_needed = .true.
  if (present(raw_charge_needed)) tally_needed = raw_charge_needed .or. any(closure_modes /= closure_mode_none)
  if (.not. tally_needed) return
  call tally_raw_charge()

  ! 反射電子の由来となった電子の吸収目標は放出電子側が実際に差し引いた量で決まるので、他の種の後に閉じる。
  do pass = 1, 2
    do species_idx = 1_i32, n
      if (credits_reflected(species_idx) .neqv. pass == 2) cycle
      mode = closure_modes(species_idx)
      if (mode == closure_mode_none) cycle
      tracked_source = mode == closure_mode_tracked_source
      charge_sign = sign(1.0_dp, app%particle_species(species_idx)%q_particle)
      partner_charge = 0.0_dp
      reflected_escape = 0.0_dp
      reflected_return = 0.0_dp
      if (mode /= closure_mode_target) call attribute_tracked_escape()
      folded_return_charge = 0.0_dp
      if (app%particle_species(species_idx)%has_target_absorbed_current_a .or. &
          current_model%has_absorbed_target(species_idx)) call close_absorbed_channel()
      if (app%particle_species(species_idx)%has_target_emission_current_a .or. &
          current_model%has_emission_target(species_idx)) call close_emission_channel()
      if (current_model%has_escape_target(species_idx)) call close_escape_channel()
    end do
  end do
  call record_surface_budget()
contains

  !> 種ごとの raw 電荷(吸収・放出反作用・脱出・外部障壁で戻った電荷)を数え、rank 合計する。
  !! 吸収した粒子は種ごとの index 列にも並べ、吸収 channel の重み補正が種ごとに全粒子を走査しないようにする。
  subroutine tally_raw_charge()
    integer(i32) :: i, s
    real(dp) :: macro_charge
    integer(i32), allocatable :: cursor(:)

    workspace%fixed_current_charge_values = 0.0_dp
    allocate (absorbed_start(n + 1_i32), cursor(n))
    absorbed_start = 0_i32
    do i = 1_i32, fresh_particle_count
      s = pcls_batch%species_id(i)
      macro_charge = pcls_batch%q(i)*pcls_batch%w(i)
      if (workspace%absorbed_flag(i)) then
        absorbed_start(s + 1_i32) = absorbed_start(s + 1_i32) + 1_i32
        workspace%fixed_current_charge_values(s, fixed_charge_absorbed) = &
          workspace%fixed_current_charge_values(s, fixed_charge_absorbed) + macro_charge
      else if (workspace%escaped_boundary_flag(i)) then
        workspace%fixed_current_charge_values(s, fixed_charge_escaped) = &
          workspace%fixed_current_charge_values(s, fixed_charge_escaped) + macro_charge
      end if
    end do
    absorbed_start(1) = 1_i32
    do s = 1_i32, n
      absorbed_start(s + 1_i32) = absorbed_start(s) + absorbed_start(s + 1_i32)
    end do
    allocate (absorbed_by_species(absorbed_start(n + 1_i32) - 1_i32))
    cursor = absorbed_start(1:n)
    do i = 1_i32, fresh_particle_count
      if (.not. workspace%absorbed_flag(i)) cycle
      s = pcls_batch%species_id(i)
      absorbed_by_species(cursor(s)) = i
      cursor(s) = cursor(s) + 1_i32
    end do
    do s = 1_i32, n
      workspace%fixed_current_charge_values(s, fixed_charge_emission) = sum(workspace%photo_emission_dq(:, s))
      ! 外部障壁で戻った粒子の電荷。H を外向きに通過したが上流へ抜けられず、セルへ戻された分。
      workspace%fixed_current_charge_values(s, fixed_charge_barrier_return) = &
        app%particle_species(s)%q_particle*sum(workspace%matching_plane_moments_thread(3, s, :))
    end do
    call allreduce_sum_real_matrix(mpi, workspace%fixed_current_charge_values)
  end subroutine tally_raw_charge

  !> 外部根の脱出束が partner species(追跡する SEE)や反射して H を抜けた上流電子と合算の値なら、それらが追跡で
  !! 脱出した電荷(帯電符号の大きさ)を根の脱出束まで差し引き、この species の脱出目標と帰還目標をその分だけ縮める。
  !! 超過は収支残差として残す。
  subroutine attribute_tracked_escape()
    integer(i32) :: partner_idx, reflected_idx
    real(dp) :: partner_escape_charge, total_escape_charge

    partner_escape_charge = 0.0_dp
    partner_idx = 0_i32
    reflected_idx = 0_i32
    if (allocated(current_model%escape_partner_species)) partner_idx = current_model%escape_partner_species(species_idx)
    if (allocated(current_model%reflected_electron_species)) then
      reflected_idx = current_model%reflected_electron_species(species_idx)
    end if
    if (partner_idx > 0_i32) then
      partner_escape_charge = max(0.0_dp, -workspace%fixed_current_charge_values(partner_idx, fixed_charge_escaped))
    end if
    if (reflected_idx > 0_i32) then
      reflected_escape = max(0.0_dp, -workspace%fixed_current_charge_values(reflected_idx, fixed_charge_escaped))
      reflected_return = max(0.0_dp, -workspace%fixed_current_charge_values(reflected_idx, fixed_charge_barrier_return))
    end if
    if ((partner_idx > 0_i32 .or. reflected_idx > 0_i32) .and. current_model%has_escape_target(species_idx)) then
      total_escape_charge = max(0.0_dp, -checked_fixed_target_charge( &
                                current_model%escaped_particle_current_a(species_idx), app%sim%batch_duration, &
                                'fixed_current escape'))
      partner_charge = min(partner_escape_charge + reflected_escape, total_escape_charge)
      workspace%fixed_escape_closed_charge(species_idx) = partner_charge
      workspace%fixed_escape_unclosed_charge(species_idx) = partner_escape_charge + reflected_escape - partner_charge
    end if
    if (reflected_idx > 0_i32) then
      ! 追跡した源の種は放出・帰還目標から反射電子の脱出全量を差し引く。partner を持つ放出電子は根の脱出束までしか
      ! 差し引けないので、SEE の脱出を先に充てた残りだけが反射電子の分になる。電子へはこの量だけを足し戻す(#93)。
      if (tracked_source .or. .not. current_model%has_escape_target(species_idx)) then
        reflected_credit(reflected_idx) = reflected_credit(reflected_idx) + reflected_escape
      else
        reflected_credit(reflected_idx) = reflected_credit(reflected_idx) + &
                                          partner_charge - min(partner_escape_charge, partner_charge)
      end if
    end if
  end subroutine attribute_tracked_escape

  !> 吸収(帰還)channel の重みを目標へ合わせる。追跡した源の種(tracked_source)は raw channel が空でも止めず、
  !! 帰還目標を放出の反作用電荷へ畳む(folded_return_charge)。
  subroutine close_absorbed_channel()
    integer(i32) :: i, k, elem_idx
    real(dp) :: raw_charge, target_current, target_charge, weight_scale, correction, charge_tolerance, macro_charge

    raw_charge = workspace%fixed_current_charge_values(species_idx, fixed_charge_absorbed)
    if (current_model%has_absorbed_target(species_idx)) then
      target_current = current_model%absorbed_current_a(species_idx)
    else
      target_current = app%particle_species(species_idx)%target_absorbed_current_a
    end if
    if (.not. all(ieee_is_finite([raw_charge, target_current, app%sim%batch_duration]))) then
      error stop 'fixed_current absorbed raw/current/duration value is not finite.'
    end if
    target_charge = checked_fixed_target_charge(target_current, app%sim%batch_duration, 'fixed_current absorbed')
    if (tracked_source) then
      ! 追跡した源の種では、外部障壁で戻った反射電子は電子の raw 吸収に含まれるので、帰還目標から外す。
      target_charge = target_charge + reflected_return
    else
      target_charge = target_charge - partner_charge
    end if
    if (credits_reflected(species_idx)) then
      ! 外部障壁を越えて脱出した反射電子は壁に届かないので、根の流入から差し引く(吸収目標は帯電符号で小さくなる)。
      ! 差し引くのは追跡した脱出ではなく、放出電子側の目標が反射電子の分として差し引いた量である。
      ! 追跡した脱出が根の脱出束を超えたときに超過まで差し引くと、その分だけ毎バッチ正味電荷が残る(#93)。
      target_charge = target_charge + reflected_credit(species_idx)
    end if
    ! 差し引きで目標の符号が粒子電荷と逆になれば、吸収なしまで縮める。
    if (target_charge*charge_sign < 0.0_dp) target_charge = 0.0_dp
    charge_tolerance = 4096.0_dp*epsilon(1.0_dp)*max(abs(raw_charge), abs(target_charge), tiny(1.0_dp))
    if (abs(raw_charge) <= charge_tolerance) then
      if (abs(target_charge) > charge_tolerance) then
        if (.not. tracked_source) then
          error stop 'fixed_current cannot map a nonzero absorbed target onto an empty raw channel.'
        end if
        ! 生の帰還がないバッチでは帰還目標を放出の反作用電荷へ畳む(放出 − 帰還 = 脱出は変わらない)。
        folded_return_charge = target_charge
        target_charge = 0.0_dp
      end if
      weight_scale = 1.0_dp
      correction = 0.0_dp
    else
      weight_scale = target_charge/raw_charge
      correction = target_charge - raw_charge
    end if
    if (.not. ieee_is_finite(weight_scale) .or. weight_scale < 0.0_dp) then
      error stop 'fixed_current absorbed scale is invalid.'
    end if
    workspace%fixed_absorbed_target_charge(species_idx) = target_charge
    workspace%fixed_absorbed_weight_scale(species_idx) = weight_scale
    workspace%fixed_channel_active(1, species_idx) = .true.
    workspace%fixed_current_correction(species_idx) = &
      workspace%fixed_current_correction(species_idx) + correction
    ! Particles of this species in their original order, so each element sums its corrections as before.
    do k = absorbed_start(species_idx), absorbed_start(species_idx + 1_i32) - 1_i32
      i = absorbed_by_species(k)
      elem_idx = workspace%absorbed_element(i)
      macro_charge = pcls_batch%q(i)*pcls_batch%w(i)
      workspace%dq_thread(elem_idx, 1) = workspace%dq_thread(elem_idx, 1) + &
                                         (weight_scale - 1.0_dp)*macro_charge
    end do
  end subroutine close_absorbed_channel

  !> 放出 channel の反作用電荷を目標へ合わせる。追跡した源の種は、放出がないバッチでは目標を載せず未閉として記録する。
  subroutine close_emission_channel()
    real(dp) :: raw_charge, target_current, target_charge, weight_scale, correction, charge_tolerance

    raw_charge = workspace%fixed_current_charge_values(species_idx, fixed_charge_emission)
    if (current_model%has_emission_target(species_idx)) then
      target_current = current_model%emission_current_a(species_idx)
    else
      target_current = app%particle_species(species_idx)%target_emission_current_a
    end if
    if (.not. all(ieee_is_finite([raw_charge, target_current, app%sim%batch_duration]))) then
      error stop 'fixed_current emission raw/current/duration value is not finite.'
    end if
    target_charge = checked_fixed_target_charge( &
                    target_current, app%sim%batch_duration, 'fixed_current emission' &
                    ) + folded_return_charge
    if (tracked_source) then
      ! 根の外側源には反射電子の外向き束も入っているので、追跡した反射電子の脱出と帰還を放出目標から外す。
      ! 放出の反作用電荷は粒子電荷と逆符号。差し引きで符号が入れ替われば、放出なしまで縮める。
      target_charge = target_charge - (reflected_escape + reflected_return)
      if (target_charge*charge_sign > 0.0_dp) then
        workspace%fixed_escape_unclosed_charge(species_idx) = &
          workspace%fixed_escape_unclosed_charge(species_idx) + abs(target_charge)
        target_charge = 0.0_dp
      end if
    end if
    charge_tolerance = 4096.0_dp*epsilon(1.0_dp)*max(abs(raw_charge), abs(target_charge), tiny(1.0_dp))
    if (abs(raw_charge) <= charge_tolerance) then
      if (abs(target_charge) > charge_tolerance) then
        if (.not. tracked_source) then
          error stop 'fixed_current cannot map a nonzero emission target onto an empty raw channel.'
        end if
        ! 放出がないバッチには目標を載せられない。根の脱出分が収支に残るので未閉として記録する。
        workspace%fixed_escape_unclosed_charge(species_idx) = abs(target_charge)
        target_charge = 0.0_dp
      end if
      weight_scale = 1.0_dp
      correction = 0.0_dp
    else
      weight_scale = target_charge/raw_charge
      correction = target_charge - raw_charge
    end if
    if (.not. ieee_is_finite(weight_scale) .or. weight_scale < 0.0_dp) then
      error stop 'fixed_current emission scale is invalid.'
    end if
    workspace%fixed_emission_target_charge(species_idx) = target_charge
    workspace%fixed_emission_weight_scale(species_idx) = weight_scale
    workspace%fixed_channel_active(2, species_idx) = .true.
    workspace%fixed_current_correction(species_idx) = &
      workspace%fixed_current_correction(species_idx) + correction
    workspace%photo_emission_dq(:, species_idx) = &
      weight_scale*workspace%photo_emission_dq(:, species_idx)
  end subroutine close_emission_channel

  !> 全 species の表面正味電荷(raw と closure 後)と、closure 後の各項の絶対値和を記録する。
  !! 外部根(zero_current)の closure 後の正味は、追跡した SEE の脱出が根の脱出束を超えるなど
  !! 目標を置けない batch を除き、丸め誤差の範囲で 0 になる。
  subroutine record_surface_budget()
    integer(i32) :: s
    real(dp) :: absorbed, emission

    workspace%fixed_surface_raw_charge = 0.0_dp
    workspace%fixed_surface_net_charge = 0.0_dp
    workspace%fixed_surface_charge_scale = 0.0_dp
    if (.not. any(workspace%fixed_channel_active)) return
    do s = 1_i32, n
      workspace%fixed_surface_raw_charge = workspace%fixed_surface_raw_charge + &
                                           workspace%fixed_current_charge_values(s, fixed_charge_absorbed) + &
                                           workspace%fixed_current_charge_values(s, fixed_charge_emission)
      ! 目標を適用した channel は目標、それ以外は raw の電荷が表面に置かれる。
      absorbed = workspace%fixed_current_charge_values(s, fixed_charge_absorbed)
      if (workspace%fixed_channel_active(1, s)) absorbed = workspace%fixed_absorbed_target_charge(s)
      emission = workspace%fixed_current_charge_values(s, fixed_charge_emission)
      if (workspace%fixed_channel_active(2, s)) emission = workspace%fixed_emission_target_charge(s)
      workspace%fixed_surface_net_charge = workspace%fixed_surface_net_charge + absorbed + emission
      workspace%fixed_surface_charge_scale = workspace%fixed_surface_charge_scale + abs(absorbed) + abs(emission)
    end do
  end subroutine record_surface_budget

  !> 脱出 channel の目標(partner・反射電子に充てた分を除いた根の脱出)と raw との差を補正として記録する。
  subroutine close_escape_channel()
    real(dp) :: raw_charge, target_current, target_charge

    raw_charge = workspace%fixed_current_charge_values(species_idx, fixed_charge_escaped)
    target_current = current_model%escaped_particle_current_a(species_idx)
    if (.not. all(ieee_is_finite([raw_charge, target_current, app%sim%batch_duration]))) then
      error stop 'fixed_current escape raw/current/duration value is not finite.'
    end if
    target_charge = checked_fixed_target_charge( &
                    target_current, app%sim%batch_duration, 'fixed_current escape' &
                    ) + partner_charge
    if (target_charge /= 0.0_dp .and. &
        sign(1.0_dp, target_charge) /= sign(1.0_dp, app%particle_species(species_idx)%q_particle)) then
      error stop 'fixed_current escape target sign must match the escaped particle charge.'
    end if
    workspace%fixed_escape_target_charge(species_idx) = target_charge
    workspace%fixed_escape_correction(species_idx) = target_charge - raw_charge
    workspace%fixed_channel_active(3, species_idx) = .true.
    if (tracked_source) then
      workspace%fixed_escape_closed_charge(species_idx) = &
        max(0.0_dp, abs(target_charge) - workspace%fixed_escape_unclosed_charge(species_idx))
    end if
    if (.not. ieee_is_finite(workspace%fixed_escape_correction(species_idx))) then
      error stop 'fixed_current escape correction is not finite.'
    end if
  end subroutine close_escape_channel
  end procedure apply_fixed_surface_current_closure

  !> species × channel の 2 次元配列を rank 合計する。列優先の並びは旧 1 次元配列と同じなので MPI の buffer も同じ。
  subroutine allreduce_sum_real_matrix(mpi, values)
    type(mpi_context), intent(in) :: mpi
    real(dp), intent(inout) :: values(:, :)
    real(dp) :: flat(size(values))

    flat = reshape(values, [size(values)])
    call mpi_allreduce_sum_real_dp_array(mpi, flat)
    values = reshape(flat, shape(values))
  end subroutine allreduce_sum_real_matrix

  !> 64bit 整数の species × kind 配列を rank 合計する。
  subroutine allreduce_sum_i64_matrix(mpi, values)
    type(mpi_context), intent(in) :: mpi
    integer(i64), intent(inout) :: values(:, :)
    integer(i64) :: flat(size(values))

    flat = reshape(values, [size(values)])
    call mpi_allreduce_sum_i64_array(mpi, flat)
    values = reshape(flat, shape(values))
  end subroutine allreduce_sum_i64_matrix

  !> finiteな電流とdurationの積を、丸め境界を含めて有限かつ非zero underflowなしに変換する。
  real(dp) function checked_fixed_target_charge(target_current, duration, context) result(target_charge)
    real(dp), intent(in) :: target_current, duration
    character(len=*), intent(in) :: context

    if (duration > 1.0_dp .and. abs(target_current) > huge(target_charge)/duration) then
      error stop trim(context)//' target charge overflowed for this batch duration.'
    end if
    target_charge = target_current*duration
    if (.not. ieee_is_finite(target_charge)) then
      error stop trim(context)//' target charge overflowed for this batch duration.'
    end if
    if (target_current /= 0.0_dp .and. target_charge == 0.0_dp) then
      error stop trim(context)//' target charge underflowed for this batch duration.'
    end if
  end function checked_fixed_target_charge

  module procedure record_batch_initial_charge
  integer(i32) :: i, species_idx
  real(dp) :: macro_charge
  do i = 1_i32, fresh_particle_count
    species_idx = pcls_batch%species_id(i)
    macro_charge = checked_macro_charge(pcls_batch%q(i), pcls_batch%w(i), 'initial batch charge ledger')
    if (pcls_batch%source_element(i) > 0_i32) then
      call checked_accumulate_charge( &
        ledger%emitted_from_surface(species_idx), macro_charge, 'local emitted charge ledger' &
        )
      ledger%emitted_count(species_idx) = ledger%emitted_count(species_idx) + 1_i64
    else
      call checked_accumulate_charge( &
        ledger%injected_from_remote(species_idx), macro_charge, 'local injected charge ledger' &
        )
      ledger%injected_count(species_idx) = ledger%injected_count(species_idx) + 1_i64
    end if
  end do
  end procedure record_batch_initial_charge

  module procedure record_batch_outcome_charge
  integer(i32) :: i, species_idx
  real(dp) :: macro_charge
  do i = 1_i32, pcls_batch%n
    species_idx = pcls_batch%species_id(i)
    macro_charge = checked_macro_charge(pcls_batch%q(i), pcls_batch%w(i), 'outcome batch charge ledger')
    if (absorbed_flag(i)) then
      call checked_accumulate_charge( &
        ledger%absorbed_on_surface(species_idx), macro_charge, 'local absorbed charge ledger' &
        )
      ledger%absorbed_count(species_idx) = ledger%absorbed_count(species_idx) + 1_i64
    else if (escaped_boundary_flag(i)) then
      call checked_accumulate_charge( &
        ledger%escaped_to_infinity(species_idx), macro_charge, 'local escaped charge ledger' &
        )
      ledger%escaped_count(species_idx) = ledger%escaped_count(species_idx) + 1_i64
    else if (soft_discarded_boundary_flag(i) .or. pcls_batch%alive(i)) then
      call checked_accumulate_charge( &
        ledger%discarded_unresolved(species_idx), macro_charge, 'local discarded charge ledger' &
        )
      ledger%discarded_unresolved_count(species_idx) = ledger%discarded_unresolved_count(species_idx) + 1_i64
    end if
  end do
  end procedure record_batch_outcome_charge

  module procedure reduce_charge_ledger_fluxes
  integer(i32) :: n
  n = ledger%nspecies
  workspace%ledger_charge_values = [ &
                                   ledger%injected_from_remote, ledger%emitted_from_surface, ledger%absorbed_on_surface, &
                                   ledger%escaped_to_infinity, ledger%discarded_unresolved &
                                   ]
  if (.not. all(ieee_is_finite(workspace%ledger_charge_values))) then
    error stop 'local batch charge ledger contains non-finite fluxes before MPI reduction.'
  end if
  call mpi_allreduce_sum_real_dp_array(mpi, workspace%ledger_charge_values)
  if (.not. all(ieee_is_finite(workspace%ledger_charge_values))) then
    error stop 'global batch charge ledger overflowed during MPI reduction.'
  end if
  ledger%injected_from_remote = workspace%ledger_charge_values(1:n)
  ledger%emitted_from_surface = workspace%ledger_charge_values(n + 1:2*n)
  ledger%absorbed_on_surface = workspace%ledger_charge_values(2*n + 1:3*n)
  ledger%escaped_to_infinity = workspace%ledger_charge_values(3*n + 1:4*n)
  ledger%discarded_unresolved = workspace%ledger_charge_values(4*n + 1:5*n)
  workspace%ledger_count_values = [ &
                                  ledger%injected_count, ledger%emitted_count, ledger%absorbed_count, ledger%escaped_count, &
                                  ledger%discarded_unresolved_count &
                                  ]
  if (any(workspace%ledger_count_values < 0_i64)) then
    error stop 'local batch charge ledger contains invalid counts before MPI reduction.'
  end if
  call mpi_allreduce_sum_i64_array(mpi, workspace%ledger_count_values)
  if (any(workspace%ledger_count_values < 0_i64)) then
    error stop 'global batch charge ledger count overflowed during MPI reduction.'
  end if
  ledger%injected_count = workspace%ledger_count_values(1:n)
  ledger%emitted_count = workspace%ledger_count_values(n + 1:2*n)
  ledger%absorbed_count = workspace%ledger_count_values(2*n + 1:3*n)
  ledger%escaped_count = workspace%ledger_count_values(3*n + 1:4*n)
  ledger%discarded_unresolved_count = workspace%ledger_count_values(4*n + 1:5*n)
  end procedure reduce_charge_ledger_fluxes

  !> finiteな粒子電荷とweightから、overflow/zero-underflowを拒否してmacro chargeを返す。
  real(dp) function checked_macro_charge(particle_charge, particle_weight, context) result(macro_charge)
    real(dp), intent(in) :: particle_charge, particle_weight
    character(len=*), intent(in) :: context

    if (.not. ieee_is_finite(particle_charge) .or. .not. ieee_is_finite(particle_weight)) then
      error stop trim(context)//' has a non-finite particle charge or weight.'
    end if
    if (abs(particle_weight) > 1.0_dp .and. abs(particle_charge) > huge(macro_charge)/abs(particle_weight)) then
      error stop trim(context)//' macro charge overflowed.'
    end if
    macro_charge = particle_charge*particle_weight
    if (.not. ieee_is_finite(macro_charge)) error stop trim(context)//' macro charge is not finite.'
    if (particle_charge /= 0.0_dp .and. particle_weight /= 0.0_dp .and. macro_charge == 0.0_dp) then
      error stop trim(context)//' macro charge underflowed to zero.'
    end if
  end function checked_macro_charge

end submodule bem_simulator_charge