main Program

Uses

  • program~~main~~UsesGraph program~main main module~bem_app_config bem_app_config program~main->module~bem_app_config module~bem_kinds bem_kinds program~main->module~bem_kinds module~bem_mesh bem_mesh program~main->module~bem_mesh module~bem_mpi bem_mpi program~main->module~bem_mpi module~bem_output_writer bem_output_writer program~main->module~bem_output_writer module~bem_performance_profile bem_performance_profile program~main->module~bem_performance_profile module~bem_restart bem_restart program~main->module~bem_restart module~bem_simulator bem_simulator program~main->module~bem_simulator module~bem_types bem_types program~main->module~bem_types module~bem_app_config_parser bem_app_config_parser module~bem_app_config->module~bem_app_config_parser module~bem_app_config_runtime bem_app_config_runtime module~bem_app_config->module~bem_app_config_runtime module~bem_app_config_types bem_app_config_types module~bem_app_config->module~bem_app_config_types module~bem_string_utils bem_string_utils module~bem_app_config->module~bem_string_utils iso_fortran_env iso_fortran_env module~bem_kinds->iso_fortran_env module~bem_mesh->module~bem_kinds module~bem_mesh->module~bem_types module~bem_mesh->module~bem_string_utils module~bem_mpi->module~bem_kinds module~bem_output_writer->module~bem_kinds module~bem_output_writer->module~bem_types module~bem_output_writer->module~bem_app_config_types module~bem_output_writer->module~bem_string_utils module~bem_performance_profile->module~bem_kinds module~bem_performance_profile->module~bem_mpi module~bem_performance_profile->iso_fortran_env module~bem_performance_profile->module~bem_string_utils module~bem_restart->module~bem_kinds module~bem_restart->module~bem_mpi module~bem_restart->module~bem_types module~bem_simulator->module~bem_app_config module~bem_simulator->module~bem_kinds module~bem_simulator->module~bem_mpi module~bem_simulator->module~bem_types module~bem_simulator->iso_fortran_env module~bem_boundary bem_boundary module~bem_simulator->module~bem_boundary module~bem_collision bem_collision module~bem_simulator->module~bem_collision module~bem_field_solver bem_field_solver module~bem_simulator->module~bem_field_solver module~bem_pusher bem_pusher module~bem_simulator->module~bem_pusher module~bem_types->module~bem_kinds module~bem_app_config_parser->module~bem_kinds module~bem_app_config_parser->module~bem_types module~bem_app_config_parser->module~bem_app_config_types module~bem_app_config_parser->module~bem_string_utils ieee_arithmetic ieee_arithmetic module~bem_app_config_parser->ieee_arithmetic module~bem_constants bem_constants module~bem_app_config_parser->module~bem_constants module~bem_app_config_runtime->module~bem_kinds module~bem_app_config_runtime->module~bem_mesh module~bem_app_config_runtime->module~bem_mpi module~bem_app_config_runtime->module~bem_types module~bem_app_config_runtime->module~bem_app_config_types module~bem_app_config_runtime->module~bem_string_utils module~bem_app_config_runtime->ieee_arithmetic module~bem_config_helpers bem_config_helpers module~bem_app_config_runtime->module~bem_config_helpers module~bem_field bem_field module~bem_app_config_runtime->module~bem_field module~bem_importers bem_importers module~bem_app_config_runtime->module~bem_importers module~bem_injection bem_injection module~bem_app_config_runtime->module~bem_injection module~bem_particles bem_particles module~bem_app_config_runtime->module~bem_particles module~bem_sheath_injection_model bem_sheath_injection_model module~bem_app_config_runtime->module~bem_sheath_injection_model module~bem_templates bem_templates module~bem_app_config_runtime->module~bem_templates module~bem_app_config_types->module~bem_kinds module~bem_app_config_types->module~bem_types module~bem_boundary->module~bem_kinds module~bem_boundary->module~bem_types module~bem_collision->module~bem_kinds module~bem_collision->module~bem_types module~bem_collision->module~bem_string_utils module~bem_field_solver->module~bem_kinds module~bem_field_solver->module~bem_types module~bem_field_solver->module~bem_string_utils module~bem_field_solver->module~bem_constants module~bem_coulomb_fmm_core bem_coulomb_fmm_core module~bem_field_solver->module~bem_coulomb_fmm_core module~bem_field_solver->module~bem_field module~bem_pusher->module~bem_kinds module~bem_config_helpers->module~bem_kinds module~bem_config_helpers->module~bem_app_config_types module~bem_config_helpers->module~bem_string_utils module~bem_constants->module~bem_kinds module~bem_coulomb_fmm_core->module~bem_kinds module~bem_coulomb_fmm_types bem_coulomb_fmm_types module~bem_coulomb_fmm_core->module~bem_coulomb_fmm_types module~bem_field->module~bem_kinds module~bem_field->module~bem_types module~bem_field->module~bem_constants module~bem_importers->module~bem_kinds module~bem_importers->module~bem_mesh module~bem_importers->module~bem_types module~bem_injection->module~bem_kinds module~bem_injection->module~bem_types module~bem_injection->module~bem_boundary module~bem_injection->module~bem_collision module~bem_injection->module~bem_string_utils module~bem_injection->module~bem_constants module~bem_injection->module~bem_particles module~bem_particles->module~bem_kinds module~bem_particles->module~bem_types module~bem_sheath_runtime bem_sheath_runtime module~bem_sheath_injection_model->module~bem_sheath_runtime module~bem_templates->module~bem_kinds module~bem_templates->module~bem_mesh module~bem_templates->module~bem_types module~bem_coulomb_fmm_types->module~bem_kinds module~bem_sheath_runtime->module~bem_kinds module~bem_sheath_runtime->module~bem_types module~bem_sheath_runtime->module~bem_app_config_types module~bem_sheath_runtime->module~bem_string_utils module~bem_sheath_runtime->module~bem_config_helpers module~bem_sheath_runtime->module~bem_constants module~bem_sheath_model_core bem_sheath_model_core module~bem_sheath_runtime->module~bem_sheath_model_core module~bem_sheath_model_core->module~bem_kinds module~bem_sheath_model_core->module~bem_string_utils module~bem_sheath_model_core->ieee_arithmetic module~bem_sheath_model_core->module~bem_constants module~bem_sheath_model_core->module~bem_injection

設定読込・メッシュ生成・粒子初期化・シミュレーション実行・結果出力を順に行うCLIエントリーポイント。


Calls

program~~main~~CallsGraph program~main main interface~run_absorption_insulator run_absorption_insulator program~main->interface~run_absorption_insulator proc~ensure_output_dir ensure_output_dir program~main->proc~ensure_output_dir proc~load_or_init_run_state load_or_init_run_state program~main->proc~load_or_init_run_state proc~mpi_initialize mpi_initialize program~main->proc~mpi_initialize proc~mpi_is_root mpi_is_root program~main->proc~mpi_is_root proc~mpi_shutdown mpi_shutdown program~main->proc~mpi_shutdown proc~mpi_world_size mpi_world_size program~main->proc~mpi_world_size proc~open_history_writer open_history_writer program~main->proc~open_history_writer proc~open_potential_history_writer open_potential_history_writer program~main->proc~open_potential_history_writer proc~perf_configure_from_env perf_configure_from_env program~main->proc~perf_configure_from_env proc~perf_region_begin perf_region_begin program~main->proc~perf_region_begin proc~perf_region_end perf_region_end program~main->proc~perf_region_end proc~perf_set_output_context perf_set_output_context program~main->proc~perf_set_output_context proc~perf_write_outputs perf_write_outputs program~main->proc~perf_write_outputs proc~print_run_summary print_run_summary program~main->proc~print_run_summary proc~write_macro_residuals_file write_macro_residuals_file program~main->proc~write_macro_residuals_file proc~write_result_files write_result_files program~main->proc~write_result_files proc~write_rng_state_file write_rng_state_file program~main->proc~write_rng_state_file proc~load_or_init_run_state->proc~mpi_is_root proc~build_mesh_from_config build_mesh_from_config proc~load_or_init_run_state->proc~build_mesh_from_config proc~default_app_config default_app_config proc~load_or_init_run_state->proc~default_app_config proc~initialize_injection_state initialize_injection_state proc~load_or_init_run_state->proc~initialize_injection_state proc~load_app_config load_app_config proc~load_or_init_run_state->proc~load_app_config proc~load_restart_checkpoint load_restart_checkpoint proc~load_or_init_run_state->proc~load_restart_checkpoint proc~prepare_periodic2_collision_mesh prepare_periodic2_collision_mesh proc~load_or_init_run_state->proc~prepare_periodic2_collision_mesh proc~resolve_config_path resolve_config_path proc~load_or_init_run_state->proc~resolve_config_path proc~seed_particles_from_config seed_particles_from_config proc~load_or_init_run_state->proc~seed_particles_from_config proc~open_history_writer->proc~ensure_output_dir proc~open_potential_history_writer->proc~ensure_output_dir proc~lower_ascii lower_ascii proc~perf_configure_from_env->proc~lower_ascii proc~perf_configure perf_configure proc~perf_configure_from_env->proc~perf_configure proc~perf_wall_time_seconds perf_wall_time_seconds proc~perf_region_begin->proc~perf_wall_time_seconds proc~perf_add_elapsed perf_add_elapsed proc~perf_region_end->proc~perf_add_elapsed proc~perf_region_end->proc~perf_wall_time_seconds proc~perf_write_outputs->proc~mpi_is_root proc~mpi_allreduce_max_real_dp_array mpi_allreduce_max_real_dp_array proc~perf_write_outputs->proc~mpi_allreduce_max_real_dp_array proc~mpi_allreduce_min_real_dp_array mpi_allreduce_min_real_dp_array proc~perf_write_outputs->proc~mpi_allreduce_min_real_dp_array proc~mpi_allreduce_sum_i32_array mpi_allreduce_sum_i32_array proc~perf_write_outputs->proc~mpi_allreduce_sum_i32_array proc~mpi_allreduce_sum_real_dp_array mpi_allreduce_sum_real_dp_array proc~perf_write_outputs->proc~mpi_allreduce_sum_real_dp_array proc~mpi_get_rank_size mpi_get_rank_size proc~perf_write_outputs->proc~mpi_get_rank_size proc~write_macro_residuals_file->proc~mpi_get_rank_size proc~restart_macro_residual_path restart_macro_residual_path proc~write_macro_residuals_file->proc~restart_macro_residual_path proc~write_result_files->proc~ensure_output_dir proc~write_result_files->proc~lower_ascii proc~write_rng_state_file->proc~mpi_get_rank_size proc~restart_rng_state_path restart_rng_state_path proc~write_rng_state_file->proc~restart_rng_state_path proc~build_mesh_from_config->proc~lower_ascii proc~apply_obj_transform apply_obj_transform proc~build_mesh_from_config->proc~apply_obj_transform proc~build_template_mesh build_template_mesh proc~build_mesh_from_config->proc~build_template_mesh proc~load_obj_mesh load_obj_mesh proc~build_mesh_from_config->proc~load_obj_mesh proc~load_app_config->proc~lower_ascii interface~ends_with ends_with proc~load_app_config->interface~ends_with proc~load_toml_config load_toml_config proc~load_app_config->proc~load_toml_config proc~load_restart_checkpoint->proc~mpi_get_rank_size proc~load_restart_checkpoint->proc~restart_macro_residual_path proc~load_restart_checkpoint->proc~restart_rng_state_path proc~perf_reset perf_reset proc~perf_configure->proc~perf_reset proc~resolve_periodic2_collision_config resolve_periodic2_collision_config proc~prepare_periodic2_collision_mesh->proc~resolve_periodic2_collision_config proc~update_mesh_geometry update_mesh_geometry proc~prepare_periodic2_collision_mesh->proc~update_mesh_geometry proc~restart_macro_residual_path->proc~mpi_get_rank_size proc~restart_rng_state_path->proc~mpi_get_rank_size proc~resolve_parallel_rank_size~2 resolve_parallel_rank_size proc~seed_particles_from_config->proc~resolve_parallel_rank_size~2 proc~seed_rng seed_rng proc~seed_particles_from_config->proc~seed_rng proc~init_mesh init_mesh proc~apply_obj_transform->proc~init_mesh proc~append_mesh_ids append_mesh_ids proc~build_template_mesh->proc~append_mesh_ids proc~append_triangles append_triangles proc~build_template_mesh->proc~append_triangles proc~build_one_template build_one_template proc~build_template_mesh->proc~build_one_template proc~build_template_mesh->proc~init_mesh proc~build_mesh_from_indexed build_mesh_from_indexed proc~load_obj_mesh->proc~build_mesh_from_indexed proc~parse_obj parse_obj proc~load_obj_mesh->proc~parse_obj proc~scan_obj scan_obj proc~load_obj_mesh->proc~scan_obj proc~load_toml_config->proc~lower_ascii interface~resolve_batch_duration resolve_batch_duration proc~load_toml_config->interface~resolve_batch_duration interface~strip_comment strip_comment proc~load_toml_config->interface~strip_comment interface~validate_photo_raycast_species validate_photo_raycast_species proc~load_toml_config->interface~validate_photo_raycast_species interface~validate_reservoir_species validate_reservoir_species proc~load_toml_config->interface~validate_reservoir_species proc~apply_mesh_kv apply_mesh_kv proc~load_toml_config->proc~apply_mesh_kv proc~apply_output_kv apply_output_kv proc~load_toml_config->proc~apply_output_kv proc~apply_particles_kv apply_particles_kv proc~load_toml_config->proc~apply_particles_kv proc~apply_particles_species_kv apply_particles_species_kv proc~load_toml_config->proc~apply_particles_species_kv proc~apply_sim_kv apply_sim_kv proc~load_toml_config->proc~apply_sim_kv proc~apply_template_kv apply_template_kv proc~load_toml_config->proc~apply_template_kv proc~ensure_particle_species_capacity ensure_particle_species_capacity proc~load_toml_config->proc~ensure_particle_species_capacity proc~ensure_template_capacity ensure_template_capacity proc~load_toml_config->proc~ensure_template_capacity proc~species_from_defaults species_from_defaults proc~load_toml_config->proc~species_from_defaults proc~resolve_parallel_rank_size~2->proc~mpi_get_rank_size proc~resolve_periodic2_collision_config->proc~lower_ascii proc~build_collision_grid build_collision_grid proc~update_mesh_geometry->proc~build_collision_grid proc~cross cross proc~update_mesh_geometry->proc~cross interface~parse_real parse_real proc~apply_mesh_kv->interface~parse_real interface~parse_real3 parse_real3 proc~apply_mesh_kv->interface~parse_real3 interface~parse_string parse_string proc~apply_mesh_kv->interface~parse_string interface~split_key_value split_key_value proc~apply_mesh_kv->interface~split_key_value interface~parse_int parse_int proc~apply_output_kv->interface~parse_int interface~parse_logical parse_logical proc~apply_output_kv->interface~parse_logical proc~apply_output_kv->interface~parse_string proc~apply_output_kv->interface~split_key_value proc~apply_particles_kv->interface~split_key_value proc~apply_particles_species_kv->proc~lower_ascii proc~apply_particles_species_kv->interface~parse_int proc~apply_particles_species_kv->interface~parse_logical proc~apply_particles_species_kv->interface~parse_real proc~apply_particles_species_kv->interface~parse_real3 proc~apply_particles_species_kv->interface~parse_string proc~apply_particles_species_kv->interface~split_key_value proc~apply_sim_kv->proc~lower_ascii interface~parse_boundary_mode parse_boundary_mode proc~apply_sim_kv->interface~parse_boundary_mode proc~apply_sim_kv->interface~parse_int proc~apply_sim_kv->interface~parse_logical proc~apply_sim_kv->interface~parse_real proc~apply_sim_kv->interface~parse_real3 proc~apply_sim_kv->interface~parse_string proc~apply_sim_kv->interface~split_key_value proc~apply_template_kv->interface~parse_int proc~apply_template_kv->interface~parse_logical proc~apply_template_kv->interface~parse_real proc~apply_template_kv->interface~parse_real3 proc~apply_template_kv->interface~parse_string proc~apply_template_kv->interface~split_key_value proc~cell_id cell_id proc~build_collision_grid->proc~cell_id proc~coord_to_cell coord_to_cell proc~build_collision_grid->proc~coord_to_cell proc~build_mesh_from_indexed->proc~init_mesh proc~build_one_template->proc~lower_ascii proc~make_annulus make_annulus proc~build_one_template->proc~make_annulus proc~make_box make_box proc~build_one_template->proc~make_box proc~make_cylinder make_cylinder proc~build_one_template->proc~make_cylinder proc~make_disk make_disk proc~build_one_template->proc~make_disk proc~make_plane make_plane proc~build_one_template->proc~make_plane proc~make_plate_hole make_plate_hole proc~build_one_template->proc~make_plate_hole proc~make_sphere make_sphere proc~build_one_template->proc~make_sphere proc~init_mesh->proc~update_mesh_geometry proc~is_face_line is_face_line proc~parse_obj->proc~is_face_line proc~is_vertex_line is_vertex_line proc~parse_obj->proc~is_vertex_line proc~parse_face_line parse_face_line proc~parse_obj->proc~parse_face_line proc~parse_vertex_line parse_vertex_line proc~parse_obj->proc~parse_vertex_line proc~strip_cr strip_cr proc~parse_obj->proc~strip_cr proc~count_face_tokens count_face_tokens proc~scan_obj->proc~count_face_tokens proc~scan_obj->proc~is_face_line proc~scan_obj->proc~is_vertex_line proc~scan_obj->proc~strip_cr proc~make_annulus->proc~init_mesh proc~push_tri push_tri proc~make_annulus->proc~push_tri proc~make_box->proc~init_mesh proc~make_box->proc~push_tri proc~make_cylinder->proc~init_mesh proc~make_cylinder->proc~push_tri proc~make_disk->proc~make_annulus proc~make_plane->proc~init_mesh proc~make_plate_hole->proc~init_mesh proc~make_plate_hole->proc~push_tri proc~ray_to_rectangle ray_to_rectangle proc~make_plate_hole->proc~ray_to_rectangle proc~transition_corner_count transition_corner_count proc~make_plate_hole->proc~transition_corner_count proc~transition_corners transition_corners proc~make_plate_hole->proc~transition_corners proc~make_sphere->proc~init_mesh proc~make_sphere->proc~push_tri proc~sph sph proc~make_sphere->proc~sph proc~edge_order_index edge_order_index proc~transition_corner_count->proc~edge_order_index proc~transition_corners->proc~transition_corner_count proc~corner_between corner_between proc~transition_corners->proc~corner_between proc~edge_next_ccw edge_next_ccw proc~transition_corners->proc~edge_next_ccw

Variables

Type Attributes Name Initial
type(mesh_type) :: mesh
type(app_config) :: app
type(sim_stats) :: stats
type(sim_stats) :: initial_stats
type(injection_state) :: inject_state
type(mpi_context) :: mpi
integer :: history_unit
integer :: potential_history_unit
logical :: history_opened
logical :: potential_history_opened
logical :: resumed
real(kind=dp) :: perf_t0
real(kind=dp) :: perf_program_t0
real(kind=dp), allocatable :: mesh_potential_v(:)

Subroutines

subroutine load_or_init_run_state(app, mesh, initial_stats, inject_state, resumed, mpi)

設定読込・メッシュ構築・再開判定・乱数初期化をまとめて行う。

Arguments

Type IntentOptional Attributes Name
type(app_config), intent(out) :: app

読み込み・既定値適用後のアプリ設定。

type(mesh_type), intent(out) :: mesh

構築した三角形メッシュ。

type(sim_stats), intent(out) :: initial_stats

再開時に引き継ぐ初期統計(新規実行時はゼロ)。

type(injection_state), intent(out) :: inject_state

種別ごとの注入残差状態。

logical, intent(out) :: resumed

チェックポイントから再開した場合に .true.

type(mpi_context), intent(in) :: mpi

subroutine resolve_config_path(path, found)

実行時設定ファイルの読み込みパスを決定する。

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(out) :: path

読み込む設定ファイルパス(未検出時は空文字)。

logical, intent(out) :: found

設定ファイルが解決できた場合に .true.

subroutine initialize_injection_state(state, n_species)

種数に合わせて注入状態をゼロ初期化する。

Arguments

Type IntentOptional Attributes Name
type(injection_state), intent(out) :: state

初期化する注入状態。

integer, intent(in) :: n_species

粒子種数(macro_residual 配列長)。

Source Code

program main
  use bem_kinds, only: dp, i32
  use bem_types, only: sim_stats, mesh_type, injection_state
  use bem_mpi, only: mpi_context, mpi_initialize, mpi_shutdown, mpi_is_root, mpi_world_size
  use bem_performance_profile, only: perf_configure_from_env, perf_set_output_context, perf_region_begin, &
                                     perf_region_end, perf_write_outputs, perf_region_program_total, perf_region_load_or_init, &
                                     perf_region_history_open, perf_region_write_results, perf_region_write_checkpoint
  use bem_simulator, only: run_absorption_insulator
  use bem_restart, only: load_restart_checkpoint, write_rng_state_file, write_macro_residuals_file
  use bem_output_writer, only: open_history_writer, open_potential_history_writer, print_run_summary, write_result_files, &
                               ensure_output_dir
  use bem_app_config, only: app_config, default_app_config, load_app_config, build_mesh_from_config, &
                            seed_particles_from_config
  use bem_mesh, only: prepare_periodic2_collision_mesh
  implicit none

  type(mesh_type) :: mesh
  type(app_config) :: app
  type(sim_stats) :: stats
  type(sim_stats) :: initial_stats
  type(injection_state) :: inject_state
  type(mpi_context) :: mpi
  integer :: history_unit
  integer :: potential_history_unit
  logical :: history_opened, potential_history_opened, resumed
  real(dp) :: perf_t0, perf_program_t0
  real(dp), allocatable :: mesh_potential_v(:)

  call perf_configure_from_env()
  call perf_region_begin(perf_region_program_total, perf_program_t0)
  call mpi_initialize(mpi)
  call perf_region_begin(perf_region_load_or_init, perf_t0)
  call load_or_init_run_state(app, mesh, initial_stats, inject_state, resumed, mpi)
  call perf_region_end(perf_region_load_or_init, perf_t0)
  call perf_set_output_context(trim(app%output_dir), app%write_output)
  if (mpi_is_root(mpi)) then
    call perf_region_begin(perf_region_history_open, perf_t0)
    call open_history_writer(app, resumed, history_opened, history_unit)
    call open_potential_history_writer(app, resumed, potential_history_opened, potential_history_unit)
    call perf_region_end(perf_region_history_open, perf_t0)
  else
    history_opened = .false.
    history_unit = -1
    potential_history_opened = .false.
    potential_history_unit = -1
  end if

  if (history_opened) then
    if (app%write_mesh_potential) then
      if (potential_history_opened) then
        call run_absorption_insulator( &
          mesh, app, stats, history_unit=history_unit, history_stride=app%history_stride, initial_stats=initial_stats, &
          inject_state=inject_state, mpi=mpi, mesh_potential_v=mesh_potential_v, &
          potential_history_unit=potential_history_unit &
          )
      else
        call run_absorption_insulator( &
          mesh, app, stats, history_unit=history_unit, history_stride=app%history_stride, initial_stats=initial_stats, &
          inject_state=inject_state, mpi=mpi, mesh_potential_v=mesh_potential_v &
          )
      end if
    else
      if (potential_history_opened) then
        call run_absorption_insulator( &
          mesh, app, stats, history_unit=history_unit, history_stride=app%history_stride, initial_stats=initial_stats, &
          inject_state=inject_state, mpi=mpi, &
          potential_history_unit=potential_history_unit &
          )
      else
        call run_absorption_insulator( &
          mesh, app, stats, history_unit=history_unit, history_stride=app%history_stride, initial_stats=initial_stats, &
          inject_state=inject_state, mpi=mpi &
          )
      end if
    end if
    close (history_unit)
  else
    if (app%write_mesh_potential) then
      if (potential_history_opened) then
        call run_absorption_insulator( &
          mesh, app, stats, initial_stats=initial_stats, inject_state=inject_state, mpi=mpi, &
          mesh_potential_v=mesh_potential_v, &
          potential_history_unit=potential_history_unit &
          )
      else
        call run_absorption_insulator( &
          mesh, app, stats, initial_stats=initial_stats, inject_state=inject_state, mpi=mpi, &
          mesh_potential_v=mesh_potential_v &
          )
      end if
    else
      if (potential_history_opened) then
        call run_absorption_insulator( &
          mesh, app, stats, initial_stats=initial_stats, inject_state=inject_state, mpi=mpi, &
          potential_history_unit=potential_history_unit &
          )
      else
        call run_absorption_insulator(mesh, app, stats, initial_stats=initial_stats, inject_state=inject_state, mpi=mpi)
      end if
    end if
  end if
  if (potential_history_opened) close (potential_history_unit)

  if (mpi_is_root(mpi)) call print_run_summary(mesh, stats)

  if (app%write_output) then
    call ensure_output_dir(app%output_dir)
    if (mpi_is_root(mpi)) then
      call perf_region_begin(perf_region_write_results, perf_t0)
      if (allocated(mesh_potential_v)) then
        call write_result_files( &
          trim(app%output_dir), mesh, stats, app, mpi_world_size=mpi_world_size(mpi), mesh_potential_v=mesh_potential_v &
          )
      else
        call write_result_files(trim(app%output_dir), mesh, stats, app, mpi_world_size=mpi_world_size(mpi))
      end if
      call perf_region_end(perf_region_write_results, perf_t0)
    end if
    call perf_region_begin(perf_region_write_checkpoint, perf_t0)
    call write_rng_state_file(trim(app%output_dir), mpi=mpi)
    call write_macro_residuals_file(trim(app%output_dir), inject_state, mpi=mpi)
    call perf_region_end(perf_region_write_checkpoint, perf_t0)
    if (mpi_is_root(mpi)) print '(a,a)', 'results written to ', trim(app%output_dir)
  end if
  call perf_region_end(perf_region_program_total, perf_program_t0)
  call perf_write_outputs(mpi)
  call mpi_shutdown(mpi)

contains

  !> 設定読込・メッシュ構築・再開判定・乱数初期化をまとめて行う。
  !! @param[out] app 読み込み・既定値適用後のアプリ設定。
  !! @param[out] mesh 構築した三角形メッシュ。
  !! @param[out] initial_stats 再開時に引き継ぐ初期統計(新規実行時はゼロ)。
  !! @param[out] inject_state 種別ごとの注入残差状態。
  !! @param[out] resumed チェックポイントから再開した場合に `.true.`。
  subroutine load_or_init_run_state(app, mesh, initial_stats, inject_state, resumed, mpi)
    type(app_config), intent(out) :: app
    type(mesh_type), intent(out) :: mesh
    type(sim_stats), intent(out) :: initial_stats
    type(injection_state), intent(out) :: inject_state
    logical, intent(out) :: resumed
    type(mpi_context), intent(in) :: mpi
    character(len=256) :: cfg_path
    logical :: has_config

    call default_app_config(app)
    call resolve_config_path(cfg_path, has_config)
    if (has_config) then
      call load_app_config(trim(cfg_path), app)
    end if

    call build_mesh_from_config(app, mesh)
    call prepare_periodic2_collision_mesh(mesh, app%sim)
    call initialize_injection_state(inject_state, app%n_particle_species)
    initial_stats = sim_stats()
    resumed = .false.
    if (app%resume_output) then
      if (.not. app%write_output) error stop 'output.resume requires output.write_files = true.'
      call load_restart_checkpoint( &
        trim(app%output_dir), mesh, initial_stats, resumed, inject_state, mpi=mpi &
        )
    end if

    if (resumed) then
      if (mpi_is_root(mpi)) then
        print '(a,i0)', 'resuming_from_batches=', initial_stats%batches
        print '(a,i0)', 'resuming_from_processed_particles=', initial_stats%processed_particles
      end if
    else
      call seed_particles_from_config(app, mpi=mpi)
    end if
  end subroutine load_or_init_run_state

  !> 実行時設定ファイルの読み込みパスを決定する。
  !! @param[out] path 読み込む設定ファイルパス(未検出時は空文字)。
  !! @param[out] found 設定ファイルが解決できた場合に `.true.`。
  subroutine resolve_config_path(path, found)
    character(len=*), intent(out) :: path
    logical, intent(out) :: found
    logical :: has_primary
    character(len=*), parameter :: primary_config = 'beach.toml'

    path = ''
    found = .false.
    if (command_argument_count() >= 1) then
      call get_command_argument(1, path)
      if (len_trim(path) == 0) error stop 'Config path argument is empty.'
      found = .true.
      return
    end if

    inquire (file=primary_config, exist=has_primary)
    if (has_primary) then
      path = primary_config
      found = .true.
    end if
  end subroutine resolve_config_path

  !> 種数に合わせて注入状態をゼロ初期化する。
  !! @param[out] state 初期化する注入状態。
  !! @param[in] n_species 粒子種数(`macro_residual` 配列長)。
  subroutine initialize_injection_state(state, n_species)
    type(injection_state), intent(out) :: state
    integer, intent(in) :: n_species

    allocate (state%macro_residual(n_species))
    state%macro_residual = 0.0d0
  end subroutine initialize_injection_state

end program main