Skip to main content

FieldManager

C++: phynexis::fields::FieldManager Python: phynexis.fields.FieldManager Header: src/fields/core/manager.hpp

Description

FieldManager provides centralized lifecycle management for a collection of fields. It maintains a KeyedObjectPool of FieldBase* pointers, tracks a FieldLayout, and ensures all managed fields share the same size. Use it to create, resize, subset, and persist groups of fields together.

Constructors

FieldManager()

Creates an empty manager with no fields and size 0.

Example:

import phynexis
fm = phynexis.fields.FieldManager()
print(fm.field_size(), fm.get_field_count())

Output:

0 0

Methods

create_field(meta)

Creates a single field from metadata and registers it.

Parameters:

ParameterTypeDescription
metaFieldMetaMetadata for the new field

Returns: FieldHandle — handle to the created field

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
meta = F.FieldMeta("pressure", F.FieldType.Scalar, F.ValueType.Double)
h = fm.create_field(meta)
print(h)
fm.resize_fields(10)
print(fm.field_size())

Output:

FieldHandle(index=0, epoch=0)
10

create_fields(metas)

Creates multiple fields from a FieldLayout (or list of FieldMeta).

Parameters:

ParameterTypeDescription
metasFieldLayout or list[FieldMeta]Layout describing the fields to create

Returns: list[FieldHandle]

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
layout = F.FieldLayout([
F.FieldMeta("x", F.FieldType.Scalar, F.ValueType.Double),
F.FieldMeta("v", F.FieldType.VecX, F.ValueType.Double),
])
handles = fm.create_fields(layout)
print([str(h) for h in handles])

Output:

['FieldHandle(index=0, epoch=0)', 'FieldHandle(index=1, epoch=0)']

ensure_fields(layout) / ensure_fields(metas) / ensure_fields(schema)

Ensures all fields described by the layout/schema exist; creates any that are missing. Existing fields are left untouched.

Parameters:

ParameterTypeDescription
layoutFieldLayoutLayout to ensure
metaslist[FieldMeta]List of metadata (auto-converted to layout)
schemaFieldSchemaSchema to ensure

Returns: boolTrue if any new field was created

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
fm.create_field(F.FieldMeta("x", F.FieldType.Scalar, F.ValueType.Double))
fm.resize_fields(5)

# "x" already exists, "y" will be created
fm.ensure_fields([
F.FieldMeta("x", F.FieldType.Scalar, F.ValueType.Double),
F.FieldMeta("y", F.FieldType.Scalar, F.ValueType.Double),
])
print(fm.get_field_count())

Output:

2

remove_field(name) / remove_field(handle)

Removes a field by name or by handle.

Parameters:

ParameterTypeDescription
namestrField name
handleFieldHandleField handle

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
fm.create_field(F.FieldMeta("x", F.FieldType.Scalar, F.ValueType.Double))
fm.remove_field("x")
print(fm.has_field("x"))

Output:

False

has_field(name)

Returns True if a field with the given name exists.

Parameters:

ParameterTypeDescription
namestrField name to check

Returns: bool

valid_field_handle(handle)

Checks if a handle is still valid (index in range and epoch matches).

Parameters:

ParameterTypeDescription
handleFieldHandleHandle to validate

Returns: bool

get_field(handle)

Retrieves the concrete field instance for a handle. The runtime type is resolved via RTTI (e.g. ScalarField, VecXdField).

Parameters:

ParameterTypeDescription
handleFieldHandleField handle

Returns: concrete Field subclass (e.g. ScalarField)

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
h = fm.create_field(F.FieldMeta("p", F.FieldType.Scalar, F.ValueType.Double))
fm.resize_fields(5)
f = fm.get_field(h)
print(type(f).__name__)

Output:

ScalarField

find_field(name)

Finds a field by name and returns its handle.

Parameters:

ParameterTypeDescription
namestrField name

Returns: FieldHandle

field_size()

Returns the shared element count of all managed fields.

Returns: int

resize_fields(new_size)

Resizes all managed fields to new_size.

Parameters:

ParameterTypeDescription
new_sizeintNew element count for all fields

reserve_fields(capacity)

Reserves capacity for all managed fields without changing their sizes.

Parameters:

ParameterTypeDescription
capacityintCapacity to reserve

clear_fields()

Clears all elements from all fields (size becomes 0). Fields themselves remain registered.

clear()

Removes all fields and resets the manager to its initial empty state.

subset_fields(mask)

Extracts a subset of elements where mask[i] is True, returning a new FieldManager.

Parameters:

ParameterTypeDescription
maskBoolFieldBoolean mask field

Returns: FieldManager — new manager containing the subset

Known Issue: The returned manager may have field_size() == 0 even when the subset is non-empty. This appears to be a C++ source behavior; the field count and names are correct.

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
fm.create_field(F.FieldMeta("x", F.FieldType.Scalar, F.ValueType.Double))
fm.resize_fields(5)

mask = F.BoolField("mask", 5, True)
mask[0] = False
mask[2] = False

fm2 = fm.subset_fields(mask)
print(fm2.get_field_count(), fm2.get_field_names())

Output:

1 ['x']

subset_fields_inplace(mask)

Same as subset_fields but modifies the current manager in-place.

Parameters:

ParameterTypeDescription
maskBoolFieldBoolean mask field

check_size_consistency()

Returns True if all fields have the same size.

Returns: bool

get_inconsistent_fields()

Returns the names of fields whose size differs from the majority.

Returns: list[str]

reset_field_values(index)

Resets all field values at index to their type defaults.

Parameters:

ParameterTypeDescription
indexintElement index to reset

copy_field_values(src_index, dst_index)

Copies all field values from src_index to dst_index.

Parameters:

ParameterTypeDescription
src_indexintSource index
dst_indexintDestination index

set_field_value(index, name, value)

Sets a single field value by name at the given index.

Parameters:

ParameterTypeDescription
indexintElement index
namestrField name
valuefloat / int / boolValue to set

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
fm.create_field(F.FieldMeta("p", F.FieldType.Scalar, F.ValueType.Double))
fm.resize_fields(3)
fm.set_field_value(1, "p", 99.5)
ok, val = fm.get_field_value(1, "p")
print(val)

Output:

{'p': 99.5}

get_field_value(index, name)

Reads a single field value by name at the given index.

Parameters:

ParameterTypeDescription
indexintElement index
namestrField name

Returns: tuple[bool, dict](success, {name: value})

set_field_values(index, values)

Sets multiple field values at an index from a JSON-like dict.

Parameters:

ParameterTypeDescription
indexintElement index
valuesdict{field_name: value, ...}

get_field_values(index)

Reads all field values at an index as a dict.

Parameters:

ParameterTypeDescription
indexintElement index

Returns: dict{field_name: value, ...}

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
fm.create_field(F.FieldMeta("p", F.FieldType.Scalar, F.ValueType.Double))
fm.create_field(F.FieldMeta("t", F.FieldType.Scalar, F.ValueType.Double))
fm.resize_fields(2)
fm.set_field_value(0, "p", 1.0)
fm.set_field_value(0, "t", 300.0)
print(fm.get_field_values(0))

Output:

{'p': 1.0, 't': 300.0}

save_to(path, file, opt=None)

Saves all fields to a binary file.

Parameters:

ParameterTypeDefaultDescription
pathstrParent directory
filestrFilename
optSaveOptionsSaveOptions()Save options

load_from(path, file, opt=None)

Loads fields from a binary file.

Parameters:

ParameterTypeDefaultDescription
pathstrParent directory
filestrFilename
optLoadOptionsLoadOptions()Load options

Example:

import phynexis, tempfile, os
F = phynexis.fields

fm = F.FieldManager()
fm.create_field(F.FieldMeta("p", F.FieldType.Scalar, F.ValueType.Double))
fm.resize_fields(5)

with tempfile.TemporaryDirectory() as td:
fm.save_to(td, "snapshot.dat")
fm2 = F.FieldManager()
fm2.load_from(td, "snapshot.dat")
print(fm2.get_field_count(), fm2.field_size())

Output:

1 5

inspect(filepath)

Reads metadata from a snapshot file without loading data.

Parameters:

ParameterTypeDescription
filepathstrFull path to the snapshot file

get_field_layout()

Returns the current FieldLayout describing all registered fields.

Returns: FieldLayout

get_field_count()

Returns the number of registered fields.

Returns: int

get_field_names()

Returns the names of all registered fields.

Returns: list[str]

get_field_ptrs()

Returns a list of concrete field instances. Each element is the runtime-resolved type (e.g. ScalarField, VecXdField).

Returns: list[Field]

Prints a summary of the manager to stdout.

Example:

import phynexis
F = phynexis.fields

fm = F.FieldManager()
fm.create_field(F.FieldMeta("p", F.FieldType.Scalar, F.ValueType.Double))
fm.resize_fields(5)
fm.print_info()

Output:

=== FieldManager ===
field count: 1
field size: 5
- p (double)

Unexposed C++ API

The following C++ APIs are not exposed via pybind:

  • remove_fields(handles) / remove_fields(names) — batch remove by VecX<FieldHandle> or VecX<String>

Known Issues

subset_fields returns manager with field_size() == 0

When calling subset_fields(mask), the returned FieldManager correctly contains the subset fields (names and count are accurate), but field_size() reports 0. This appears to be a C++ source-side behavior rather than a binding issue.

Workaround: Use subset_fields_inplace(mask) if in-place mutation is acceptable, or verify field sizes with get_field_count() and per-field size().

Status: noted — source issue, not binding bug