最小限の TOML セクションと key = value を解釈して設定へ反映する。
現在は sim / mesh / output / [[mesh.templates]] / [[particles.species]] を扱う。
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | path |
読み込むTOMLファイルパス。 |
||
| type(app_config), | intent(inout) | :: | cfg |
読み込み結果で更新するアプリ設定。 |
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