config_engine Module

Shared machinery for the per-dimension config modules (spec 2026-07-06-multidim-machinery-refactor-design.md, section 4). A dimension supplies DATA — a field_desc_t(:) table binding key/group/kind/bounds/ choices to pointers into its config derived type — and this engine supplies the behavior. Cross-field and conditional rules stay in the per-dimension validate routines; only unconditional per-field constraints (flagged check_in_validate) are enforced here.



Variables

Type Visibility Attributes Name Initial
integer, public, parameter :: cfg_kind_int = 1

Value-kind tags. Owned here; re-exported by config_schema / config_schema_2d so their ABI-facing constants keep working unchanged.

integer, public, parameter :: cfg_kind_real = 2
integer, public, parameter :: cfg_kind_logical = 3
integer, public, parameter :: cfg_kind_choice = 4
integer, public, parameter :: cfg_kind_string = 5
integer, public, parameter :: cfg_kind_real3 = 6
integer, public, parameter :: choice_msg_valid_list = 1

Generated choice-violation message styles (match the historical texts).

integer, public, parameter :: choice_msg_one_of = 2
integer, public, parameter :: choice_msg_bare = 3

Derived Types

type, public ::  field_desc_t

One configuration field: schema metadata + validation constraint + a typed pointer into a live config instance. Exactly one of the pointer components is associated, per value_kind.

Components

Type Visibility Attributes Name Initial
character(len=32), public :: key = ''
character(len=32), public :: group = ''
integer, public :: value_kind = 0
character(len=256), public :: help = ''
logical, public :: has_min = .false.
logical, public :: min_exclusive = .false.
real(kind=wp), public :: min_value = 0.0_wp
logical, public :: has_max = .false.
real(kind=wp), public :: max_value = 0.0_wp
character(len=64), public, allocatable :: choices(:)
integer, public :: choice_msg_style = choice_msg_valid_list
character(len=160), public :: msg_choice = ''
logical, public :: casefold = .false.
logical, public :: check_in_validate = .true.

.false. => published in the schema but NOT checked by validate_fields (conditional/cross-field rules stay in the per-dim validate routine).

character(len=160), public :: msg_min = ''
character(len=160), public :: msg_max = ''
integer, public, pointer :: iptr => null()
real(kind=wp), public, pointer :: rptr => null()
logical, public, pointer :: lptr => null()
character(len=:), public, pointer :: sptr => null()
real(kind=wp), public, pointer :: r3ptr(:) => null()

Functions

public pure function field_meta(key, group, value_kind, help, has_min, min_exclusive, min_value, msg_min, has_max, max_value, msg_max, choices, choice_msg_style, msg_choice, casefold, check_in_validate) result(d)

Metadata-only constructor. Callers associate the matching pointer component afterwards (build_field_table binds them to a target config).

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: key
character(len=*), intent(in) :: group
integer, intent(in) :: value_kind
character(len=*), intent(in) :: help
logical, intent(in), optional :: has_min
logical, intent(in), optional :: min_exclusive
real(kind=wp), intent(in), optional :: min_value
character(len=*), intent(in), optional :: msg_min
logical, intent(in), optional :: has_max
real(kind=wp), intent(in), optional :: max_value
character(len=*), intent(in), optional :: msg_max
character(len=*), intent(in), optional :: choices(:)
integer, intent(in), optional :: choice_msg_style
character(len=*), intent(in), optional :: msg_choice
logical, intent(in), optional :: casefold
logical, intent(in), optional :: check_in_validate

Return Value type(field_desc_t)

public function field_index(fields, key) result(index)

1-based index of key in the table (normalized compare); 0 when absent.

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(in) :: fields(:)
character(len=*), intent(in) :: key

Return Value integer

private function choice_violation_message(prefix, f) result(msg)

Compose the historical choice-violation text for one field.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: prefix
type(field_desc_t), intent(in) :: f

Return Value character(len=512)

public function fields_get_integer(fields, key, value) result(found)

Typed by-name read. .false. on unknown key OR kind mismatch; the caller composes its historical error text (they differ between 1D and 2D).

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(in) :: fields(:)
character(len=*), intent(in) :: key
integer, intent(out) :: value

Return Value logical

public function fields_get_real(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(in) :: fields(:)
character(len=*), intent(in) :: key
real(kind=wp), intent(out) :: value

Return Value logical

public function fields_get_logical(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(in) :: fields(:)
character(len=*), intent(in) :: key
logical, intent(out) :: value

Return Value logical

public function fields_get_string(fields, key, value) result(found)

Strings and choices are both readable as strings (historical behavior).

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(in) :: fields(:)
character(len=*), intent(in) :: key
character(len=*), intent(out) :: value

Return Value logical

public function fields_get_real3(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(in) :: fields(:)
character(len=*), intent(in) :: key
real(kind=wp), intent(out) :: value(3)

Return Value logical

public function fields_set_integer(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(inout) :: fields(:)
character(len=*), intent(in) :: key
integer, intent(in) :: value

Return Value logical

public function fields_set_real(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(inout) :: fields(:)
character(len=*), intent(in) :: key
real(kind=wp), intent(in) :: value

Return Value logical

public function fields_set_logical(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(inout) :: fields(:)
character(len=*), intent(in) :: key
logical, intent(in) :: value

Return Value logical

public function fields_set_string(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(inout) :: fields(:)
character(len=*), intent(in) :: key
character(len=*), intent(in) :: value

Return Value logical

public function fields_set_real3(fields, key, value) result(found)

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(inout) :: fields(:)
character(len=*), intent(in) :: key
real(kind=wp), intent(in) :: value(3)

Return Value logical


Subroutines

public subroutine normalize_fields(fields)

Lowercase every casefold-flagged string field in place (the table-driven replacement for the 1D normalize_config).

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(inout) :: fields(:)

public subroutine validate_fields(fields, prefix, ok, message)

Enforce every unconditional per-field constraint, in table order; first violation wins. prefix is the historical message prefix ('config' or 'config_2d'). Choice comparison casefolds the candidate via to_lower (length/blank-preserving), NOT lowercase_token (which adjustl's first): a leading-whitespace value like ' hllc' must be REJECTED here, matching the 2D to_lower-on-compare behavior; the 1D path already normalizes (adjustl+lowercase) via normalize_fields before this runs, so its stored values never carry leading blanks by the time they reach here.

Arguments

Type IntentOptional Attributes Name
type(field_desc_t), intent(in) :: fields(:)
character(len=*), intent(in) :: prefix
logical, intent(out) :: ok
character(len=*), intent(out) :: message