Skip to content

Comments

feat: add get and create methods and **kwarg support for create#4866

Open
Gobot1234 wants to merge 18 commits intomainfrom
jhilton/create-and-find
Open

feat: add get and create methods and **kwarg support for create#4866
Gobot1234 wants to merge 18 commits intomainfrom
jhilton/create-and-find

Conversation

@Gobot1234
Copy link
Collaborator

@Gobot1234 Gobot1234 commented Jan 27, 2026

The API surface now looks like which is fairly ORM like

AxisBoundary.get(solver, name='boundary')
AxisBoundary.all(solver)
AxisBoundary.create(solver, name='foo', other='parameters that', are='just values')

Context

Fixes #4863
And adds create and get methods as more clear methods for using name and new_instance_name to the object oriented API

Scope

This PR affects all of the object oriented API, and some of the settings API related code surrounding child creation

This is due to the following changes:

This needs settings api changes that I haven't figured out
Copilot AI review requested due to automatic review settings January 27, 2026 20:52
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds get and create class methods to the object-oriented API for clearer alternatives to using name and new_instance_name parameters. The implementation refactors initialization logic into a shared mixin class and enables passing additional kwargs when creating objects.

Changes:

  • Refactored initialization logic into _SettingsInitializerMixin base class
  • Added get class method to _SingletonSetting for retrieving singleton instances
  • Added create class method to _CreatableNamedObjectSetting for creating new instances with optional attributes

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/ansys/fluent/core/solver/settings_builtin_bases.py Adds mixin class for shared initialization logic and implements get/create class methods
src/ansys/fluent/core/solver/flobject.py Updates _create_child_object and __getitem__ to accept and pass kwargs, enabling attribute setting during object creation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings January 28, 2026 02:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings January 28, 2026 02:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@seanpearsonuk
Copy link
Collaborator

@Gobot1234 Please can we define the scope of the current work. E.g.:

  • Adding create() and get() methods to the settings classes
  • Allowing creation there without need for a name to be specified?
  • Adding keyword arguments to the settings classes (at creation?)

If we know the scope, it makes it easier to understand the changes. Thanks.

@Gobot1234 Gobot1234 changed the title Add get and create methods to objects feat: get and create methods and **kwarg support for create Jan 28, 2026
@Gobot1234 Gobot1234 changed the title feat: get and create methods and **kwarg support for create feat: add get and create methods and **kwarg support for create Jan 28, 2026
Copilot AI review requested due to automatic review settings January 28, 2026 10:12
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings January 28, 2026 10:59
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


@classmethod
def get(cls, settings_source: SettingsBase | Solver | None = None, /, *, name: str) -> Self:
"""Get and return the singleton instance of this object in Fluent.
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says 'singleton instance' but this method can be used on non-singleton classes like _CreatableNamedObjectSetting. The description should be more generic, such as 'Get and return an instance of this object in Fluent' to accurately reflect its usage across different setting types.

Suggested change
"""Get and return the singleton instance of this object in Fluent.
"""Get and return an instance of this object in Fluent.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings January 28, 2026 12:43
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

*,
name: str,
) -> Self:
"""Get and return the singleton instance of this object in Fluent.
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring describes this method as returning a 'singleton instance', but this is misleading for a named object that can have multiple instances. Consider changing to 'Get and return the named instance of this object in Fluent.'

Copilot uses AI. Check for mistakes.
@Gobot1234
Copy link
Collaborator Author

Generated object oriented code looks like this now, which type checks far better, there's a per version split though.

class SolidMaterials(
    _SingletonSetting,
    type(settings_root_261.setup.materials.solid),
):
    create = settings_root_261.setup.materials.solid.create

class SolidMaterial(
    _CreatableNamedObjectSetting,
    type(settings_root_261.setup.materials.solid.child_object_type),
):
    create = settings_root_261.setup.materials.solid.create

create parameter's are also fully typed

class manage_criteria(NamedObject[manage_criteria_child]):
    _version: str
    fluent_name: str
    _python_name: str
    command_names: list[str]
    def create(self, *, name: str = ..., menu: str = ..., sync: bool = ..., index: int = ..., offset: int = ..., manual: bool = ..., active: bool = ..., frequency: int = ..., skip_until: int = ..., anisotropic: bool = ..., refinement_layers: int = ..., predicates: list[str] = ...):
        """
        Create a new instance of the current object type.
        
        Parameters
        ----------
            name : str
                Object name.
        """

@Gobot1234
Copy link
Collaborator Author

@deprecated("Use SolidCellZone.all() instead")
class SolidCellZones(
    _SingletonSetting,
    type(settings_root_261.setup.cell_zone_conditions.solid),
):
    create = settings_root_261.setup.cell_zone_conditions.solid.create

class SolidCellZone(
    _CreatableNamedObjectSetting,
    type(settings_root_261.setup.cell_zone_conditions.solid.child_object_type),
):
    create = settings_root_261.setup.cell_zone_conditions.solid.create

@deprecated("Use BoundaryCondition.all() instead")
class BoundaryConditions(
    _SingletonSetting,
    type(settings_root_261.setup.boundary_conditions),
):
    ...

class BoundaryCondition(
    _NonCreatableNamedObjectSetting,
    type(settings_root_261.setup.boundary_conditions.child_object_type),
):
    ...

@deprecated("Use AxisBoundary.all() instead")
class AxisBoundaries(
    _SingletonSetting,
    type(settings_root_261.setup.boundary_conditions.axis),
):
    create = settings_root_261.setup.boundary_conditions.axis.create

class AxisBoundary(
    _CreatableNamedObjectSetting,
    type(settings_root_261.setup.boundary_conditions.axis.child_object_type),
):
    create = settings_root_261.setup.boundary_conditions.axis.create

after talking with Sean we agreed it best to remove the plural versions of the classes in favour of using something which I would describe as more ORM like which feels much more familiar to me at least.

API surface now looks like

AxisBoundary.get(solver, name='boundary')
AxisBoundary.all(solver)
AxisBoundary.create(solver, name='foo', other='parameters that', are='just values')

Copilot AI review requested due to automatic review settings January 28, 2026 23:31
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +523 to +524
"PlaneSurfaces": ("Singleton", "results.surfaces.plane_surface", None),
"PlaneSurface": ("NamedObject", "results.surfaces.plane_surface", None),
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent reciprocal relationships: PlaneSurfaces and PlaneSurface have None as their reciprocal instead of referencing each other like other surface pairs (e.g., LineSurfaces/LineSurface on lines 503-511). This breaks the symmetry in the data structure.

Suggested change
"PlaneSurfaces": ("Singleton", "results.surfaces.plane_surface", None),
"PlaneSurface": ("NamedObject", "results.surfaces.plane_surface", None),
"PlaneSurfaces": (
"Singleton",
"results.surfaces.plane_surface",
"PlaneSurface",
),
"PlaneSurface": (
"NamedObject",
"results.surfaces.plane_surface",
"PlaneSurfaces",
),

Copilot uses AI. Check for mistakes.
Comment on lines 656 to 659
"Contours": ("Singleton", "results.graphics.contour", None),
"Contour": ("NamedObject", "results.graphics.contour", None),
"Vectors": ("Singleton", "results.graphics.vector", None),
"Vector": ("NamedObject", "results.graphics.vector", None),
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple graphics objects (Contours/Contour, Vectors/Vector, Pathlines/Pathline, LICs/LIC) have None reciprocals, unlike most other named object pairs. Verify whether these should reference each other for consistency with the new ORM-like API.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings January 28, 2026 23:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings January 29, 2026 09:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"ReadCase": ("Command", "file.read_case"),
"ReadData": ("Command", "file.read_data"),
"ReadCaseData": ("Command", "file.read_case_data"),
"SimulationReports",
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'SimulationReports' entry has 'SimulationReports' as its reciprocal, creating a self-reference. This should likely be None for a singleton container or have a proper singular counterpart like 'SimulationReport' if one exists.

Suggested change
"SimulationReports",
None,

Copilot uses AI. Check for mistakes.
Comment on lines 656 to 657
"Contours": ("Singleton", "results.graphics.contour", None),
"Contour": ("NamedObject", "results.graphics.contour", None),
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contours and Contour lack reciprocal relationships. For consistency with the API pattern shown in other entries, set Contours' reciprocal to 'Contour' and Contour's reciprocal to 'Contours'.

Suggested change
"Contours": ("Singleton", "results.graphics.contour", None),
"Contour": ("NamedObject", "results.graphics.contour", None),
"Contours": ("Singleton", "results.graphics.contour", "Contour"),
"Contour": ("NamedObject", "results.graphics.contour", "Contours"),

Copilot uses AI. Check for mistakes.
@seanpearsonuk
Copy link
Collaborator

seanpearsonuk commented Jan 29, 2026

@Gobot1234 In the code snippets in your description, an important use case is binding the solver
context at construction time rather than passing it into each accessor method (it’s fine to
support both). For example:

AxisBoundary(solver).get(name='boundary')
# or hold onto a solver-context-specific AxisBoundary object (which can be passed around)
axis = AxisBoundary(solver)
axis.all()
axis_foo = axis.create(name='foo', other='parameters that', are='just values')
axis_anon = axis.create(other='parameters that', are='just values')

This makes the interface more substitutable and the code more flexible, since consumers can
operate on a solver-bound facade without needing to thread the solver through every call.
From the client’s point of view, the solver argument can otherwise feel like an
implementation detail.

Perhaps both formats are already supported in what you have implemented?

@Gobot1234
Copy link
Collaborator Author

Gobot1234 commented Jan 29, 2026

@Gobot1234 In the code snippets in your description, an important use case is binding the solver context at construction time rather than passing it into each accessor method (it’s fine to support both). For example:

AxisBoundary(solver).get(name='boundary')
# or hold onto a solver-context-specific AxisBoundary object (which can be passed around)
axis = AxisBoundary(solver)
axis.all()
axis_foo = axis.create(name='foo', other='parameters that', are='just values')
axis_anon = axis.create(other='parameters that', are='just values')

This makes the interface more substitutable and the code more flexible, since consumers can operate on a solver-bound facade without needing to thread the solver through every call. From the client’s point of view, the solver argument can otherwise feel like an implementation detail.

Perhaps both formats are already supported in what you have implemented?

I think I can add support for this form though it'll require more changes. I was honestly thinking the best way to operate on this thing was using the following as it's scoped nicely and closes itself when required

with pyfluent.Solver.from_install() as solver, pyfluent.using(solver):
    AxisBoundary().get(name='boundary')

@seanpearsonuk
Copy link
Collaborator

@Gobot1234 In the code snippets in your description, an important use case is binding the solver context at construction time rather than passing it into each accessor method (it’s fine to support both). For example:

AxisBoundary(solver).get(name='boundary')
# or hold onto a solver-context-specific AxisBoundary object (which can be passed around)
axis = AxisBoundary(solver)
axis.all()
axis_foo = axis.create(name='foo', other='parameters that', are='just values')
axis_anon = axis.create(other='parameters that', are='just values')

This makes the interface more substitutable and the code more flexible, since consumers can operate on a solver-bound facade without needing to thread the solver through every call. From the client’s point of view, the solver argument can otherwise feel like an implementation detail.
Perhaps both formats are already supported in what you have implemented?

I think I can add support for this form though it'll require more changes. I was honestly thinking the best way to operate on this thing was using the following as it's scoped nicely and closes itself when required

with pyfluent.Solver.from_install() as solver, pyfluent.using(solver):
    AxisBoundary().get(name='boundary')

I agree that the context manager pattern is a good one, and it’s something we already encourage for solver lifetime and scoping.

However, I think that concern is largely orthogonal to how solver context is modeled in the object API itself.

In the pattern you show:

with pyfluent.Solver.from_install() as solver, pyfluent.using(solver):
    AxisBoundary().get(name='boundary')

AxisBoundary is effectively acting as a namespace whose methods depend on ambient state. By contrast, when the solver is bound at construction time, AxisBoundary(solver) becomes a solver-scoped facade that encapsulates its context. That keeps the public methods simpler, since they no longer need to expose solver as an argument that conceptually belongs to the object rather than the call.

Context managers mitigate some ergonomics, but they don’t quite provide the same expressive power as a context-aware object. That’s the reason I prefer constructor binding as the primary model (even if method-level solver passing remains supported).

@seanpearsonuk
Copy link
Collaborator

@deprecated("Use SolidCellZone.all() instead")
class SolidCellZones(
    _SingletonSetting,
    type(settings_root_261.setup.cell_zone_conditions.solid),
):
    create = settings_root_261.setup.cell_zone_conditions.solid.create

class SolidCellZone(
    _CreatableNamedObjectSetting,
    type(settings_root_261.setup.cell_zone_conditions.solid.child_object_type),
):
    create = settings_root_261.setup.cell_zone_conditions.solid.create

@deprecated("Use BoundaryCondition.all() instead")
class BoundaryConditions(
    _SingletonSetting,
    type(settings_root_261.setup.boundary_conditions),
):
    ...

class BoundaryCondition(
    _NonCreatableNamedObjectSetting,
    type(settings_root_261.setup.boundary_conditions.child_object_type),
):
    ...

@deprecated("Use AxisBoundary.all() instead")
class AxisBoundaries(
    _SingletonSetting,
    type(settings_root_261.setup.boundary_conditions.axis),
):
    create = settings_root_261.setup.boundary_conditions.axis.create

class AxisBoundary(
    _CreatableNamedObjectSetting,
    type(settings_root_261.setup.boundary_conditions.axis.child_object_type),
):
    create = settings_root_261.setup.boundary_conditions.axis.create

after talking with Sean we agreed it best to remove the plural versions of the classes in favour of using something which I would describe as more ORM like which feels much more familiar to me at least.

API surface now looks like

AxisBoundary.get(solver, name='boundary')
AxisBoundary.all(solver)
AxisBoundary.create(solver, name='foo', other='parameters that', are='just values')

@mkundu1 I'd like to get your feedback on this point too.

Copilot AI review requested due to automatic review settings February 3, 2026 23:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +397 to 402
report_file = ReportFile(solver)
report_file.create(name="report-file-1")
assert report_file == report_file.get(solver, name="report-file-1")
assert report_file.all() == list(solver.settings.setup.report_files["*"])


Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instance method create() is being called, but report_file has already been initialized as a ReportFile object. Since create() is a class method, this should be ReportFile.create(solver, name=\"report-file-1\"), or the variable should be assigned the result: report_file = ReportFile(solver).create(name=\"report-file-1\").

Suggested change
report_file = ReportFile(solver)
report_file.create(name="report-file-1")
assert report_file == report_file.get(solver, name="report-file-1")
assert report_file.all() == list(solver.settings.setup.report_files["*"])
report_file = ReportFile(solver).create(name="report-file-1")
assert report_file == report_file.get(solver, name="report-file-1")
assert report_file.all() == list(solver.settings.setup.report_files["*"])

Copilot uses AI. Check for mistakes.
report_file = ReportFile(solver)
report_file.create(name="report-file-1")
assert report_file == report_file.get(solver, name="report-file-1")
assert report_file.all() == list(solver.settings.setup.report_files["*"])
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The all() method is a class method, not an instance method. This should be ReportFile.all(solver) instead of report_file.all().

Suggested change
assert report_file.all() == list(solver.settings.setup.report_files["*"])
assert list(ReportFile.all(solver)) == list(solver.settings.setup.report_files["*"])

Copilot uses AI. Check for mistakes.
elif k in obj.argument_names:
newkwds[k] = v
else:
elif k not in obj.get_active_child_names():
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition may not correctly filter out child object parameters passed to create(). If a child name exists but is not 'active', valid kwargs might still be filtered out. Consider validating this logic or adding a comment explaining when child names are 'active'.

Copilot uses AI. Check for mistakes.
Comment on lines +243 to +244
# Generate for all available versions
versions = sorted([v.number for v in all_versions()])
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions 'all available versions' but the actual code in if __name__ == '__main__' generates for all versions. This might cause unexpected behavior if run directly vs imported, as the main generation flow uses a single version. Consider clarifying or consolidating the version selection logic.

Copilot uses AI. Check for mistakes.
@Gobot1234
Copy link
Collaborator Author

I'm curious as to whether it makes more sense to deprecate the singular or plurals, I think the plural methods make more sense grammatically to get() and all() for but less sense for create() but also interested to hear your opinion @mkundu1

@Gobot1234 Gobot1234 mentioned this pull request Feb 11, 2026
2 tasks
Parameters
----------
solver
Something with a ``settings`` attribute. If omitted the active session is assumed from the :func:`using` context manager.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't use assumed here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for kwargs that pass through to the instance properties in .create() and the object oriented types

3 participants