Skip to content

Conversation

@rozyczko
Copy link
Member

@rozyczko rozyczko commented Dec 3, 2025

This PR implements the architecture decision from Discussion #160 to introduce a new stateless calculator factory pattern for physics calculators in EasyScience.

Key Components Added

Component File Purpose
CalculatorBase calculator_base.py Abstract base class for physics calculators that compute theoretical results
CalculatorFactoryBase calculator_factory.py Abstract factory for creating calculator instances
SimpleCalculatorFactory calculator_factory.py Concrete factory implementation using dictionary registry
ModelCollection model_collection.py Generic collection class for NewBase/ModelBase objects

Files Modified

File Change
map.py:70-76 Made _store private (__store) to enforce encapsulation
parameter.py:1025-1029 Updated to use public API instead of private _store access
collection_base.py:64-68 Extended type checking to include NewBase
init.py Exported new classes with backwards compatibility note for InterfaceFactoryTemplate
init.py Added ModelCollection export

@rozyczko rozyczko added [scope] enhancement Adds/improves features (major.MINOR.patch) [priority] medium Normal/default priority labels Dec 3, 2025
@codecov
Copy link

codecov bot commented Dec 3, 2025

Codecov Report

❌ Patch coverage is 91.28788% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.68%. Comparing base (bd10653) to head (d0833d0).

Files with missing lines Patch % Lines
src/easyscience/base_classes/model_collection.py 83.57% 15 Missing and 8 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #172      +/-   ##
===========================================
+ Coverage    81.15%   81.68%   +0.52%     
===========================================
  Files           52       55       +3     
  Lines         4267     4516     +249     
  Branches       740      785      +45     
===========================================
+ Hits          3463     3689     +226     
- Misses         624      639      +15     
- Partials       180      188       +8     
Flag Coverage Δ
unittests 81.68% <91.28%> (+0.52%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/easyscience/base_classes/__init__.py 100.00% <100.00%> (ø)
src/easyscience/base_classes/collection_base.py 91.26% <100.00%> (+0.06%) ⬆️
src/easyscience/fitting/calculators/__init__.py 100.00% <100.00%> (ø)
...easyscience/fitting/calculators/calculator_base.py 100.00% <100.00%> (ø)
...yscience/fitting/calculators/calculator_factory.py 100.00% <100.00%> (ø)
...syscience/fitting/calculators/interface_factory.py 94.59% <100.00%> (+0.09%) ⬆️
src/easyscience/global_object/map.py 96.92% <100.00%> (ø)
src/easyscience/variable/parameter.py 90.50% <100.00%> (+0.01%) ⬆️
src/easyscience/base_classes/model_collection.py 83.57% <83.57%> (ø)

@rozyczko rozyczko marked this pull request as ready for review December 13, 2025 10:55
Copy link
Member

@henrikjacobsenfys henrikjacobsenfys left a comment

Choose a reason for hiding this comment

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

Just a few questions, otherwise looks good to me

Copy link
Contributor

@damskii9992 damskii9992 left a comment

Choose a reason for hiding this comment

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

Can you please split this PR into two? I believe the ModelCollection needs its own PR and ADR discussion.


def __init__(
self,
model: NewBase,
Copy link
Contributor

Choose a reason for hiding this comment

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

The typehint should be ModelBase for now and should probably be SampleModelBase when that base class has been properly updated.

**kwargs : Any
Additional calculator-specific options.
"""
if model is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

why not if not isinstance(ModelBase)?

self._additional_kwargs = kwargs
# Register this calculator and model in the global object map
if hasattr(model, 'unique_name'):
self._global_object.map.add_edge(self, model)
Copy link
Contributor

Choose a reason for hiding this comment

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

So, if we know that the model is a ModelBase (or even NewBase), then we know it has a unique_name attribute and this check is redundant. But also, why add the edge? As far as I know, we don't use these "edges" in the map anywhere, and I hope to remove it entirely, so unless you have a reason to add it, please don't.

List[str]
Names of all calculators that can be created by this factory.
"""
...
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can do better than this in the base class. How about if the factory has an internal attribute _available_calculators which is an empty list and a generic method which attempts to import a package based on input, and if successful adds it to this list. That way we can have automatic granularity of installed calculators and even if a calculator for some reason fails on the users end we can still support the other calculators.

"""Find a parameter by its serializer_id from all parameters in the global map."""
for obj in self._global_object.map._store.values():
for key in self._global_object.map.vertices():
obj = self._global_object.map.get_item_by_key(key)
Copy link
Contributor

Choose a reason for hiding this comment

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

Haha, I knew I remembered seeing that you used the internal _store somewhere :D

Copy link
Member Author

Choose a reason for hiding this comment

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

shhh... you didn't SEE ANYTHING

@rozyczko
Copy link
Member Author

Can you please split this PR into two? I believe the ModelCollection needs its own PR and ADR discussion.

I can but the subsequent merge of these two would be ugly as the functionality is overlapping to some extent.

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

Labels

[priority] medium Normal/default priority [scope] enhancement Adds/improves features (major.MINOR.patch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants