Skip to content

Implement detailed balance settings#162

Merged
henrikjacobsenfys merged 7 commits intodevelopfrom
detailed-balance-settings
Apr 22, 2026
Merged

Implement detailed balance settings#162
henrikjacobsenfys merged 7 commits intodevelopfrom
detailed-balance-settings

Conversation

@henrikjacobsenfys
Copy link
Copy Markdown
Member

Fixes #160

No new tests yet. Will need to test that Analysis uses detailed balancing when convolution is not used.

@henrikjacobsenfys henrikjacobsenfys added [scope] enhancement Adds/improves features (major.MINOR.patch) [priority] high Should be prioritized soon labels Apr 20, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.02%. Comparing base (5b06f1e) to head (bf00ee2).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #162      +/-   ##
===========================================
+ Coverage    97.93%   98.02%   +0.09%     
===========================================
  Files           43       45       +2     
  Lines         2715     2790      +75     
  Branches       472      484      +12     
===========================================
+ Hits          2659     2735      +76     
  Misses          33       33              
+ Partials        23       22       -1     
Flag Coverage Δ
integration 49.31% <53.44%> (+0.29%) ⬆️
unittests 98.02% <100.00%> (+0.09%) ⬆️

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.

Copy link
Copy Markdown

@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.

+1 for standardizing to "normalize" instead of "divide by temperature" and use it consistently :P
Looks good to me, just a few small things and a potential improvement / learning opportunity.

self,
use_detailed_balance: bool = True,
normalize_detailed_balance: bool = True,
display_name: str = 'MySampleModel',
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You probably don't want this default display name :P

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I definitely did not copy/paste anything. Nope, no copy paste at all.

display_name : str, default='MySampleModel'
Display name of the model.
unique_name : str | None, default=None
Unique name of the model. If None, a unique name will be generated.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your docstring also seems wrong.

from easydynamics.base_classes.easydynamics_base import EasyDynamicsBase


class DetailedBalanceSettings(EasyDynamicsBase):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This could probably be a dataclass. Seems ideal for it, to avoid writing boiler-plate code :)

@rozyczko
Copy link
Copy Markdown
Member

I played with running some scripts I had before and one is not giving me the same results. As expected or something more sinister?

import numpy as np
import scipp as sc

from easydynamics.analysis.analysis1d import Analysis1d
from easydynamics.experiment import Experiment
from easydynamics.sample_model import InstrumentModel, SampleModel
from easydynamics.sample_model.components.gaussian import Gaussian

Q = sc.array(dims=['Q'], values=[1.0], unit='1/Angstrom')
energy = sc.array(dims=['energy'], values=np.linspace(-1.0, 1.0, 5), unit='meV')
data = sc.array(
    dims=['Q', 'energy'],
    values=[[1.0, 1.0, 1.0, 1.0, 1.0]],
    variances=[[1.0, 1.0, 1.0, 1.0, 1.0]],
)
experiment = Experiment(data=sc.DataArray(data=data, coords={'Q': Q, 'energy': energy}))

sample_model = SampleModel(
    components=Gaussian(area=1.0, center=0.0, width=0.2),
    temperature=100.0,
)

instrument_model = InstrumentModel()  # empty resolution model

analysis = Analysis1d(
    experiment=experiment,
    sample_model=sample_model,
    instrument_model=instrument_model,
    Q_index=0,
)

print("About to call analysis.calculate()")
print(analysis.calculate())

develop gives me

About to call analysis.calculate()
[7.43359757e-06 8.76415025e-02 1.99471140e+00 8.76415025e-02
 7.43359757e-06]

The current branch fails with

About to call analysis.calculate()
Traceback (most recent call last):
  File "c:\projects\easy\dynamics-lib\a.py", line 33, in <module>
    print(analysis.calculate())
          ^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 153, in calculate
    return self._calculate(energy=energy)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 173, in _calculate
    sample_intensity = self._evaluate_sample(energy=energy)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 579, in _evaluate_sample
    return self._evaluate_components(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 535, in _evaluate_components
    energy_unit=self.unit,
                ^^^^^^^^^
AttributeError: 'Analysis1d' object has no attribute 'unit'

@henrikjacobsenfys
Copy link
Copy Markdown
Member Author

henrikjacobsenfys commented Apr 21, 2026

I played with running some scripts I had before and one is not giving me the same results. As expected or something more sinister?

import numpy as np
import scipp as sc

from easydynamics.analysis.analysis1d import Analysis1d
from easydynamics.experiment import Experiment
from easydynamics.sample_model import InstrumentModel, SampleModel
from easydynamics.sample_model.components.gaussian import Gaussian

Q = sc.array(dims=['Q'], values=[1.0], unit='1/Angstrom')
energy = sc.array(dims=['energy'], values=np.linspace(-1.0, 1.0, 5), unit='meV')
data = sc.array(
    dims=['Q', 'energy'],
    values=[[1.0, 1.0, 1.0, 1.0, 1.0]],
    variances=[[1.0, 1.0, 1.0, 1.0, 1.0]],
)
experiment = Experiment(data=sc.DataArray(data=data, coords={'Q': Q, 'energy': energy}))

sample_model = SampleModel(
    components=Gaussian(area=1.0, center=0.0, width=0.2),
    temperature=100.0,
)

instrument_model = InstrumentModel()  # empty resolution model

analysis = Analysis1d(
    experiment=experiment,
    sample_model=sample_model,
    instrument_model=instrument_model,
    Q_index=0,
)

print("About to call analysis.calculate()")
print(analysis.calculate())

develop gives me

About to call analysis.calculate()
[7.43359757e-06 8.76415025e-02 1.99471140e+00 8.76415025e-02
 7.43359757e-06]

The current branch fails with

About to call analysis.calculate()
Traceback (most recent call last):
  File "c:\projects\easy\dynamics-lib\a.py", line 33, in <module>
    print(analysis.calculate())
          ^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 153, in calculate
    return self._calculate(energy=energy)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 173, in _calculate
    sample_intensity = self._evaluate_sample(energy=energy)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 579, in _evaluate_sample
    return self._evaluate_components(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\easy\dynamics-lib\src\easydynamics\analysis\analysis1d.py", line 535, in _evaluate_components
    energy_unit=self.unit,
                ^^^^^^^^^
AttributeError: 'Analysis1d' object has no attribute 'unit'
image

(I don't know why, but in this branch AnalysisBase inherited directly from the easyscience base classes instead of my own. Should be fixed now. Also, I'm expecting the results to be different now, with calculate giving a higher value at positive energy than negative energy because of detailed balancing.)

@henrikjacobsenfys
Copy link
Copy Markdown
Member Author

I decided to make a settings folder, since I'll some time soon^TM introduce fit settings as well, and it seems nicer to have all settings in one place - perhaps even a settings class? (or dataclass). I might also one day do default plot settings or other types of settings.

@henrikjacobsenfys henrikjacobsenfys linked an issue Apr 21, 2026 that may be closed by this pull request
@henrikjacobsenfys henrikjacobsenfys merged commit e0e2aea into develop Apr 22, 2026
34 checks passed
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.

Detailed balancing is not implemented correctly

3 participants