Skip to content

Conversation

henrikjacobsenfys
Copy link
Member

@henrikjacobsenfys henrikjacobsenfys commented Oct 17, 2025

Adding SampleModel which can contain ModelComponents and calculate the model at given x, with and without detailed balance.

@henrikjacobsenfys henrikjacobsenfys added [scope] enhancement Adds/improves features (major.MINOR.patch) [priority] high Should be prioritized soon labels Oct 17, 2025
@henrikjacobsenfys henrikjacobsenfys added this to the First release milestone Oct 17, 2025
@henrikjacobsenfys
Copy link
Member Author

henrikjacobsenfys commented Oct 17, 2025

Code coverage is not 100%, but I think all the essentials are there, and I'd rather get feedback on the important things than polish tests that may need to change anyway :)

Edit: apparently codecov is upset about the missing tests, so I added tests and discovered a minor bug in the process.

Copy link

codecov bot commented Oct 17, 2025

Codecov Report

❌ Patch coverage is 95.40230% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.10%. Comparing base (95175e5) to head (ce49f05).

Files with missing lines Patch % Lines
src/easydynamics/sample_model/sample_model.py 95.37% 8 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop      #56      +/-   ##
===========================================
+ Coverage    95.02%   95.10%   +0.07%     
===========================================
  Files           11       12       +1     
  Lines          663      837     +174     
===========================================
+ Hits           630      796     +166     
- Misses          33       41       +8     
Flag Coverage Δ
unittests 95.10% <95.40%> (+0.07%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@henrikjacobsenfys henrikjacobsenfys changed the title A Sample Model Oct 17, 2025
Copy link
Member

@rozyczko rozyczko left a comment

Choose a reason for hiding this comment

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

part 1 after a quick look

import scipp as sc
from easyscience.base_classes import ObjBase
from easyscience.variable import Parameter
from scipp import UnitError
Copy link
Member

Choose a reason for hiding this comment

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

the order of imports should be fixed and consistent (run ruff)

Copy link
Member Author

Choose a reason for hiding this comment

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

I am running ruff, this is the order it decided on - perhaps I need to change some settings?

Numeric = Union[float, int]


class SampleModel(ObjBase, MutableMapping):
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't you inherit from TheoreticalModelBase or at least from BaseObjectCollection?

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably? I'm not exactly sure

self,
name: str = "MySampleModel",
temperature: Optional[Union[Numeric, None]] = None,
temperature_unit: Optional[str] = "K",
Copy link
Member

Choose a reason for hiding this comment

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

maybe you should accept scipp's Variable with unit as Temperature?

def __init__(
self,
name: str = "MySampleModel",
temperature: Optional[Union[Numeric, None]] = None,
Copy link
Member

Choose a reason for hiding this comment

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

Optional[Union[Numeric, None]] is redundant. Should be Optional[Numeric]

##########################################################

@property
def temperature(self) -> Union[Parameter, None]:
Copy link
Member

Choose a reason for hiding this comment

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

Should be Optional[Parameter]

(in other typehints as well!)

params = []
for comp in self.components.values():
params.extend(comp.get_parameters())
return params
Copy link
Member

Choose a reason for hiding this comment

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

or just

    from itertools import chain
    
    # Create generator for temperature parameter
    temp_params = (self._temperature,) if self._temperature is not None else ()
    
    # Create generator for component parameters
    comp_params = (param for comp in self.components.values() 
                   for param in comp.get_parameters())
    
    # Chain them together and return as list
    return list(chain(temp_params, comp_params))

(slight teaching aspect for itertools - a VERY important python module)

if is_not_fixed and is_independent:
fit_parameters.append(parameter)

return fit_parameters
Copy link
Member

Choose a reason for hiding this comment

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

Again, not a bad loop (easy to see what's being done) but if you want to make it more "modular" you could use list comprehension, e.g.

    def is_fit_parameter(param: Parameter) -> bool:
        """Check if a parameter can be used for fitting."""
        return (not getattr(param, "fixed", False) and 
                getattr(param, "_independent", True))
    
    return [param for param in self.get_parameters() if is_fit_parameter(param)]

)

if self._temperature:
new_model.use_detailed_balance = self.use_detailed_balance
Copy link
Member

Choose a reason for hiding this comment

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

did you intend not to copy _normalize_detailed_balance?

Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops.

new_model = SampleModel(
name=name,
temperature=self._temperature.value if self._temperature else None,
)
Copy link
Member

Choose a reason for hiding this comment

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

no unit in constructor??

Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops. Unit incoming.

##############################################

def __getitem__(self, key: str) -> ModelComponent:
"""
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't all those dunder methods be available in BaseObjectCollection already?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but TheoreticalModelBase doesn't inherit from BaseObjectCollection, so I'm a bit confused - can we chat on Monday?

@henrikjacobsenfys
Copy link
Member Author

@rozyczko Would you have another look when you have time? I'm working on other things the rest of the day and am away tomorrow, so no rush.

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

Labels

[priority] high Should be prioritized soon [scope] enhancement Adds/improves features (major.MINOR.patch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants