load_toml_config Subroutine

public subroutine load_toml_config(path, cfg)

最小限の TOML セクションと key = value を解釈して設定へ反映する。 現在は sim / mesh / output / [[mesh.templates]] / [[particles.species]] を扱う。

Arguments

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

読み込むTOMLファイルパス。

type(app_config), intent(inout) :: cfg

読み込み結果で更新するアプリ設定。


Calls

proc~~load_toml_config~~CallsGraph proc~load_toml_config load_toml_config 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~lower_ascii lower_ascii proc~load_toml_config->proc~lower_ascii proc~species_from_defaults species_from_defaults proc~load_toml_config->proc~species_from_defaults 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

Called by

proc~~load_toml_config~~CalledByGraph proc~load_toml_config load_toml_config proc~load_app_config load_app_config proc~load_app_config->proc~load_toml_config proc~load_or_init_run_state load_or_init_run_state proc~load_or_init_run_state->proc~load_app_config program~main main program~main->proc~load_or_init_run_state

Source Code

  subroutine load_toml_config(path, cfg)
    character(len=*), intent(in) :: path
    type(app_config), intent(inout) :: cfg
    integer :: u, ios, i, axis, t_idx, s_idx
    integer(i32) :: per_batch_particles
    integer(i32) :: n_periodic_axes
    logical :: has_dynamic_source_species
    character(len=512) :: raw, line, section

    t_idx = 0
    s_idx = 0
    section = ''
    if (.not. allocated(cfg%templates)) then
      allocate (cfg%templates(max_templates))
      cfg%n_templates = 0_i32
    end if
    if (.not. allocated(cfg%particle_species)) then
      allocate (cfg%particle_species(max_particle_species))
      cfg%particle_species = particle_species_spec()
      cfg%n_particle_species = 0_i32
    end if

    open (newunit=u, file=trim(path), status='old', action='read', iostat=ios)
    if (ios /= 0) error stop 'Could not open TOML file.'

    do
      read (u, '(A)', iostat=ios) raw
      if (ios /= 0) exit
      line = strip_comment(trim(raw))
      if (len_trim(line) == 0) cycle

      if (line(1:1) == '[') then
        ! 配列テーブルは専用カウンタを進める。
        if (trim(line) == '[[mesh.templates]]') then
          t_idx = t_idx + 1
          call ensure_template_capacity(cfg, t_idx)
          if (int(t_idx, i32) > cfg%n_templates) cfg%n_templates = int(t_idx, i32)
          cfg%templates(t_idx)%enabled = .true.
          section = 'mesh.template'
        else if (trim(line) == '[[particles.species]]') then
          s_idx = s_idx + 1
          call ensure_particle_species_capacity(cfg, s_idx)
          if (int(s_idx, i32) > cfg%n_particle_species) cfg%n_particle_species = int(s_idx, i32)
          cfg%particle_species(s_idx) = species_from_defaults()
          cfg%particle_species(s_idx)%enabled = .true.
          section = 'particles.species'
        else
          section = lower_ascii(trim(adjustl(line(2:len_trim(line) - 1))))
        end if
        cycle
      end if

      select case (trim(section))
      case ('')
        error stop 'Found key-value pair before any TOML section.'
      case ('sim')
        call apply_sim_kv(cfg, line)
      case ('particles')
        call apply_particles_kv(line)
      case ('particles.species')
        call apply_particles_species_kv(cfg%particle_species(s_idx), line)
      case ('mesh')
        call apply_mesh_kv(cfg, line)
      case ('mesh.template')
        call apply_template_kv(cfg%templates(t_idx), line)
      case ('output')
        call apply_output_kv(cfg, line)
      case default
        error stop 'Unknown TOML section: ['//trim(section)//']'
      end select
    end do
    close (u)

    if (cfg%sim%batch_count <= 0_i32) error stop 'sim.batch_count must be > 0.'
    if (s_idx <= 0) error stop 'At least one [[particles.species]] entry is required.'
    if (t_idx > 0) cfg%n_templates = int(t_idx, i32)

    cfg%n_particle_species = int(s_idx, i32)
    cfg%sim%field_solver = lower_ascii(trim(cfg%sim%field_solver))
    select case (trim(cfg%sim%field_solver))
    case ('direct', 'treecode', 'fmm', 'auto')
      continue
    case default
      error stop 'sim.field_solver must be "direct", "treecode", "fmm", or "auto".'
    end select
    cfg%sim%field_bc_mode = lower_ascii(trim(cfg%sim%field_bc_mode))
    select case (trim(cfg%sim%field_bc_mode))
    case ('free', 'periodic2')
      continue
    case default
      error stop 'sim.field_bc_mode must be "free" or "periodic2".'
    end select
    cfg%sim%field_periodic_far_correction = lower_ascii(trim(cfg%sim%field_periodic_far_correction))
    select case (trim(cfg%sim%field_periodic_far_correction))
    case ('auto')
      continue
    case ('none')
      continue
    case ('m2l_root_oracle')
      continue
    case default
      error stop 'sim.field_periodic_far_correction must be "auto", "none", '// &
        'or "m2l_root_oracle".'
    end select
    if (trim(cfg%sim%field_periodic_far_correction) == 'm2l_root_oracle') then
      if (trim(cfg%sim%field_solver) /= 'fmm' .or. trim(cfg%sim%field_bc_mode) /= 'periodic2') then
        error stop 'sim.field_periodic_far_correction requires sim.field_solver="fmm" and sim.field_bc_mode="periodic2".'
      end if
      if (cfg%sim%field_periodic_ewald_layers < 1_i32) then
        error stop 'sim.field_periodic_ewald_layers must be >= 1 when far correction is enabled.'
      end if
    end if
    if (cfg%sim%field_periodic_image_layers < 0_i32) then
      error stop 'sim.field_periodic_image_layers must be >= 0.'
    end if
    if (.not. ieee_is_finite(cfg%sim%field_periodic_ewald_alpha) .or. cfg%sim%field_periodic_ewald_alpha < 0.0d0) then
      error stop 'sim.field_periodic_ewald_alpha must be finite and >= 0.'
    end if
    if (cfg%sim%field_periodic_ewald_layers < 0_i32) then
      error stop 'sim.field_periodic_ewald_layers must be >= 0.'
    end if
    select case (trim(cfg%sim%field_solver))
    case ('direct', 'treecode', 'auto')
      if (trim(cfg%sim%field_bc_mode) /= 'free') then
        error stop 'sim.field_bc_mode must be "free" when sim.field_solver is "direct", "treecode", or "auto".'
      end if
    case ('fmm')
      if (trim(cfg%sim%field_bc_mode) == 'periodic2') then
        if (.not. cfg%sim%use_box) then
          error stop 'sim.field_bc_mode="periodic2" requires sim.use_box=true.'
        end if
        n_periodic_axes = 0_i32
        do axis = 1, 3
          if ((cfg%sim%bc_low(axis) == bc_periodic) .neqv. (cfg%sim%bc_high(axis) == bc_periodic)) then
            error stop 'periodic2 requires bc_low(axis)=bc_high(axis)=periodic for periodic axes.'
          end if
          if (cfg%sim%bc_low(axis) == bc_periodic) then
            n_periodic_axes = n_periodic_axes + 1_i32
            if (cfg%sim%box_max(axis) <= cfg%sim%box_min(axis)) then
              error stop 'periodic2 requires positive box length on periodic axes.'
            end if
          end if
        end do
        if (n_periodic_axes /= 2_i32) then
          error stop 'sim.field_bc_mode="periodic2" requires exactly two periodic axes.'
        end if
      end if
    end select
    if (.not. ieee_is_finite(cfg%sim%tree_theta) .or. cfg%sim%tree_theta <= 0.0d0 .or. cfg%sim%tree_theta > 1.0d0) then
      error stop 'sim.tree_theta must be finite and satisfy 0 < theta <= 1.'
    end if
    if (cfg%sim%tree_leaf_max < 1_i32) then
      error stop 'sim.tree_leaf_max must be >= 1.'
    end if
    if (cfg%sim%tree_min_nelem < 1_i32) then
      error stop 'sim.tree_min_nelem must be >= 1.'
    end if
    cfg%sim%reservoir_potential_model = lower_ascii(trim(cfg%sim%reservoir_potential_model))
    select case (trim(cfg%sim%reservoir_potential_model))
    case ('none', 'infinity_barrier')
      continue
    case default
      error stop 'sim.reservoir_potential_model must be "none" or "infinity_barrier".'
    end select
    if (cfg%sim%injection_face_phi_grid_n < 1_i32) then
      error stop 'sim.injection_face_phi_grid_n must be >= 1.'
    end if
    if (cfg%sim%raycast_max_bounce < 1_i32) then
      error stop 'sim.raycast_max_bounce must be >= 1.'
    end if
    if (.not. ieee_is_finite(cfg%sim%phi_infty)) then
      error stop 'sim.phi_infty must be finite.'
    end if
    cfg%sim%sheath_injection_model = lower_ascii(trim(cfg%sim%sheath_injection_model))
    select case (trim(cfg%sim%sheath_injection_model))
    case ('none', 'zhao_auto', 'zhao_a', 'zhao_b', 'zhao_c', 'floating_no_photo')
      continue
    case default
      error stop 'sim.sheath_injection_model must be "none", "zhao_auto", "zhao_a", "zhao_b", "zhao_c", or "floating_no_photo".'
    end select
    cfg%sim%sheath_electron_drift_mode = lower_ascii(trim(cfg%sim%sheath_electron_drift_mode))
    select case (trim(cfg%sim%sheath_electron_drift_mode))
    case ('normal', 'full')
      continue
    case default
      error stop 'sim.sheath_electron_drift_mode must be "normal" or "full".'
    end select
    cfg%sim%sheath_ion_drift_mode = lower_ascii(trim(cfg%sim%sheath_ion_drift_mode))
    select case (trim(cfg%sim%sheath_ion_drift_mode))
    case ('normal', 'full')
      continue
    case default
      error stop 'sim.sheath_ion_drift_mode must be "normal" or "full".'
    end select
    if (.not. ieee_is_finite(cfg%sim%sheath_alpha_deg) .or. cfg%sim%sheath_alpha_deg < 0.0d0 .or. &
        cfg%sim%sheath_alpha_deg > 90.0d0) then
      error stop 'sim.sheath_alpha_deg must be finite and satisfy 0 <= alpha <= 90.'
    end if
    if (index(trim(cfg%sim%sheath_injection_model), 'zhao_') == 1) then
      if (.not. ieee_is_finite(cfg%sim%sheath_photoelectron_ref_density_cm3) .or. &
          cfg%sim%sheath_photoelectron_ref_density_cm3 <= 0.0d0) then
        error stop 'sim.sheath_photoelectron_ref_density_cm3 must be > 0 for Zhao sheath injection.'
      end if
    end if
    if (cfg%sim%has_sheath_reference_coordinate) then
      if (.not. ieee_is_finite(cfg%sim%sheath_reference_coordinate)) then
        error stop 'sim.sheath_reference_coordinate must be finite.'
      end if
    end if
    if (trim(cfg%sim%sheath_injection_model) /= 'none' .and. trim(cfg%sim%reservoir_potential_model) /= 'none') then
      error stop 'sim.sheath_injection_model currently requires sim.reservoir_potential_model="none".'
    end if
    call resolve_batch_duration(cfg)
    per_batch_particles = 0_i32
    has_dynamic_source_species = .false.
    do i = 1, s_idx
      if (.not. cfg%particle_species(i)%enabled) cycle

      cfg%particle_species(i)%source_mode = lower_ascii(trim(cfg%particle_species(i)%source_mode))
      select case (trim(cfg%particle_species(i)%source_mode))
      case ('volume_seed')
        if (cfg%particle_species(i)%npcls_per_step < 0_i32) then
          error stop 'particles.species.npcls_per_step must be >= 0.'
        end if
        if (cfg%particle_species(i)%has_target_macro_particles_per_batch) then
          error stop 'target_macro_particles_per_batch is only valid for reservoir_face.'
        end if
        if (abs(cfg%particle_species(i)%emit_current_density_a_m2) > 0.0d0 .or. &
            cfg%particle_species(i)%rays_per_batch /= 0_i32 .or. cfg%particle_species(i)%has_ray_direction .or. &
            cfg%particle_species(i)%has_deposit_opposite_charge_on_emit) then
          error stop 'photo_raycast keys are only valid for source_mode="photo_raycast".'
        end if
        per_batch_particles = per_batch_particles + cfg%particle_species(i)%npcls_per_step
      case ('reservoir_face')
        has_dynamic_source_species = .true.
        call validate_reservoir_species(cfg, i)
      case ('photo_raycast')
        has_dynamic_source_species = .true.
        call validate_photo_raycast_species(cfg, i)
      case default
        error stop 'Unknown particles.species.source_mode.'
      end select
    end do

    if (per_batch_particles <= 0_i32 .and. .not. has_dynamic_source_species) then
      error stop 'At least one enabled [[particles.species]] entry must have npcls_per_step > 0.'
    end if
    cfg%n_particles = cfg%sim%batch_count*per_batch_particles
  end subroutine load_toml_config