Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue with mutable default attributes #759

Merged
merged 8 commits into from
Dec 8, 2023

Conversation

paulf81
Copy link
Collaborator

@paulf81 paulf81 commented Dec 7, 2023

Fix issue with mutable default attributes

This pull request address the fact that if you use a list, dict, array or other mutable object as a default parameter in attrs (or more generally in function defaults), all instances of the declared object will point to the same list/dict/array, possibly generating confusing bugs. See Issue #758 for more detail on the problem and proposed solution.

The approach used in this pull request was determined in Issue #758 and is also identified as the correct solution in the attrs documentation (https://www.attrs.org/en/stable/api-attr.html#attr.ib) (can see also a quick explanation here: https://refex.readthedocs.io/en/latest/guide/fixers/attrib_default.html), specifically to use a factory to generate a new list/dict/array, rather declaring a single instance. The resulting changes all take the form of converting:

power: NDArrayFloat = field(default=[], converter=floris_array_converter)

to:

power: NDArrayFloat = field(factory=list, converter=floris_array_converter)

For empty lists, while using a lambda for empty numpy arrays:

w_initial_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([]))

Related issue

Closes #758

Impacted areas of the software

farm.py
turbine.py
flow_field.py
wake.py
empirical_gauss.py

Test results, if applicable

@paulf81 paulf81 added bug Something isn't working v3 Label to denote focus on v3 floris.simulation in-progress Work is actively in progress labels Dec 7, 2023
@paulf81 paulf81 self-assigned this Dec 7, 2023
@rafmudaf rafmudaf added this to the v3.6 milestone Dec 7, 2023
@rafmudaf rafmudaf removed the in-progress Work is actively in progress label Dec 7, 2023
Copy link
Collaborator

@RHammond2 RHammond2 left a comment

Choose a reason for hiding this comment

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

I thinks is great! My only requested change is to pick one approach for the empty object creation process, like I mentioned in my comment on turbine.py. I'm not really picky (even if I've demonstrated a preference in the commecnts) about which it ends up being, so long as it's consistent (which is really focused on future maintenance).

turbine_fCts: Dict[str, interp1d] | List[interp1d] = field(init=False, default=[])
turbine_fCts_sorted: NDArrayFloat = field(init=False, default=[])
turbine_fCts: Dict[str, interp1d] | List[interp1d] = field(init=False, factory=list)
turbine_fCts_sorted: NDArrayFloat = field(init=False, factory=list)
Copy link
Collaborator

Choose a reason for hiding this comment

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

For instances like this below, I would recommend modifying slightly to ensure the typing is valid (even if unchecked currently) to use the following paragdigm:

x: NDArrayFloat = field(init=False, factory=list, converter=floris_array_converter)

Essentially what will happen if typing is implemented, without converting to the array type, it will fail. Additionally it creates some confusion (though some already exists tbc) to create a list for an object indicated to be an array.

The only place this will cause issues is with non-float arrays, in which case I'd recommend having two variations of floris_array_converter such as float_array_converter and object_array_converter. I don't know that it needs to have floris_ at the front at this point, and might be a lingering (but dated) idea from early in the design process.

Because this is a suggested course correction somewhat late in the game, i could see this being a separate issue/PR since that usage is pretty common.

u: NDArrayFloat = field(init=False, default=np.array([]))
v: NDArrayFloat = field(init=False, default=np.array([]))
w: NDArrayFloat = field(init=False, default=np.array([]))
u_initial_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([]))
Copy link
Collaborator

Choose a reason for hiding this comment

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

My comment from farm.py stands for this as well since that would have the same effect without a lambda for an empty object, but is also not a required change.

power: NDArrayFloat = field(default=[], converter=floris_array_converter)
thrust: NDArrayFloat = field(default=[], converter=floris_array_converter)
wind_speed: NDArrayFloat = field(default=[], converter=floris_array_converter)
power: NDArrayFloat = field(factory=list, converter=floris_array_converter)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess I'd say we should have a unified approach now that I see it's a separate approach for the same results in each of the files so far (love Python for this!), at least to make it easier for future developers.

@paulf81
Copy link
Collaborator Author

paulf81 commented Dec 8, 2023

Hi @RHammond2 , thank you for your review! I opened a new issue (#761) to capture your request, would you mind to both check if that issue captures the problem/solution well? And then also would you mind to re-review this pull request and let us know if we can merge? Thank you!

@RHammond2
Copy link
Collaborator

@paulf81, I think I see where this is going, which is to simply translate to a version that isn't causing memory issues under certain use cases, and then to decide on the FLORIS standard later. In this case, the code is approved, and a decision can be made later about the best approach for FLORIS.

@paulf81
Copy link
Collaborator Author

paulf81 commented Dec 8, 2023

Thank you @RHammond2 !

@rafmudaf rafmudaf merged commit 64ae678 into NREL:develop Dec 8, 2023
8 checks passed
@paulf81 paulf81 deleted the feature/fix_mutable_attrs_types branch December 8, 2023 21:22
@misi9170 misi9170 mentioned this pull request Apr 5, 2024
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working floris.simulation v3 Label to denote focus on v3
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants