-
Notifications
You must be signed in to change notification settings - Fork 25
Sheshuk/flavor transformations with flavor matrices #351
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
Sheshuk/flavor transformations with flavor matrices #351
Conversation
|
Comment from @Sheshuk (2024-08-13): some classes are implementing all flavor transformations. Others are handling transformations only in certain environments, e.g., in Earth or in vacuum. As a result, the matrix outputs are not consistent or equivalent across all classes (e.g., flavor-to-flavor, flavor-to-mass, mass-to-mass, etc.). This needs to be checked carefully in the code to ensure that mixing operations between the classes is not allowed if physically the transformations should not be mixed. All classes inherit from |
|
A dumb observation @Sheshuk: while approving PR #350, I noticed the While I could live with it, I don't love having a generic "utils" module that is destined to be a dumping ground for random calculations. Can we move the function into |
|
@Sheshuk, I added support for
|
|
Finally I think this is ready for review. What I did
Next steps (to be done in separate PRs):
|
|
One more thing: I had to swap the formulas for NMO and IMO for NeutrinoDecay in the tests: 09735b5 I'm not sure this is correct. we need a cross-check |
|
Thanks a lot for this work @Sheshuk! Sorry it’s taking me a while to review this; I’m still looking through the code but wanted to get at least some high level comments to you now:
This class structure looks really good to me. Just two comments:
This is really nice for readability!
At first glance, that makes sense to me; but happy to revisit it later.
That makes sense; I’ll largely skip that during my review. And a first comment on the code: snewpy/python/snewpy/flavor_transformation/transforms.py Lines 116 to 119 in d8664a6
The TransformationChain instances are currently saving the transforms in two different places; can we simplify that?
|
JostMigenda
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Individual comments are at the appropriate line in the code.
Regarding the code arrangement in flavor_transformation/:
In addition to the in_* submodules (which makes sense), there are three fairly generic submodules (__init__.py, base.py and transforms.py) and it’s not obvious to me what code goes where. My intuition would probably be to drop transforms.py by putting NoTransformation/CompleteExchange/ThreeFlavorDecoherence into __init__.py (alongside the backwards-compatible transformation chains) and moving FlavorTransformation and TransformationChain into base.py?
Finally, I added a commit cleaning up unused imports.
|
@JostMigenda thanks a lot for your review!
Yes, it is correct. I also don't like it being asymmetric, so if you have any idea of making it more clear for the user, please tell.
I agree, But probably we should do the renaming in a separate PR, it will be rather straightforward.
Sure. I can make
That makes sense, thank you, I'll do that! The only classes standing out here are |
On second thought, this makes a circular import: |
Hm … good point; I can see it being very confusing if
Yep, let’s do that! I’ve created #366 to keep track of it.
It would certainly eliminate any risk of bugs where we change e.g. >>> from collections import namedtuple
>>> Transformations = namedtuple('Transformations', 'in_sn, in_vacuum, in_earth')
>>> transforms = Transformations('AdiabaticMSW', 'NoVacuumTransformation', 'NoEarthMatter')
>>> for t in transforms:
... print(t)
...
AdiabaticMSW
NoVacuumTransformation
NoEarthMatter
>>> transforms.in_vacuum
'NoVacuumTransformation'In particular, the current design doesn’t make it clear that iterating over
I guess they are more of a mix-in than a base class; but I’m fine with that.
Oh, good point! After thinking about it for a while, I think the main issue is the
The first of these should probably be |
Good idea, that's perfect use case for that. I also like I'll do it now. |
I'm glad you caught this. I checked the formulas, they are now correct. I must have mixed up NMO and IMO. |
From a physics perspective there is no symmetry between the three parts of a transformation chain: the neutrinos have to pass through the supernova but they don't have to do anything in the vacuum (except decohere), or pass through the Earth. I would prefer that we not provide a in_sn.NoSNTransformation prescription so that the user cannot create an empty TransformationChain object that they might think would be equivalent to NoTransformation. But if you insist, the only way I can see how to do it would be to check that all three parts of the chain are None and if so, use NoTransformation.
AdvancedMSW is fine with me, or we could adopt NumericalMSW |
|
I noticed that ThreeFlavorDecoherence is described as Phenomenological transformation and placed in init.py. It really should be a SN transformation. It's true that Earth matter can't change the decoherence but vacuum transformations could e.g. one of the mass states decays. |
I just wanted to make sure that this asymmetry is deliberate and not an oversight.
My impression was that However, if you think that any users might want to build a |
| def __call__(self, mixing_params): | ||
| """Convenience method: update mixing parameters and return self""" | ||
| self.set_mixing_params(mixing_params) | ||
| return self | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| def __call__(self, mixing_params): | |
| """Convenience method: update mixing parameters and return self""" | |
| self.set_mixing_params(mixing_params) | |
| return self | |
Hm … changing the existing transformation in place and returning it at the same time seems like a potentially confusing design to me.
I’d contrast this with list.sort() (which sorts in place and deliberately doesn’t return the list) vs. sorted(list) (which returns a new list but does not modify its argument).
For example, I’d be tempted to write something like
from snewpy.flavor_transformation import AdiabaticMSW
from snewpy.neutrino import MixingParameters
msw_nufit = AdiabaticMSW(MixingParameters(version="NuFIT6.0"))
msw_pdg = AdiabaticMSW(MixingParameters(version="PDG2024"))but this wouldn’t work at all. (Instead, it would result in msw_nufit == msw_pdg == AdiabaticMSW.)
I’m not even sure what __call__ should do in general. In the example above, it feels intuitive to have it return a deep copy (with modified mixing params) and leave the original object unmodified; but in other contexts having it as a convenience method as it is now might make more sense?
For this PR, I think we should stick with explicit use of set_mixing_params() (which makes it clear that the current object is modified), remove __call__ and maybe open a separate issue if we want to enable use cases like in the example above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this suggestion. My presumption when reading a code is that member functions modify the existing class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also don't like this __call__ method - it is here only to mimic the previous behaviour, as in __init__.py we define the aliases:
snewpy/python/snewpy/flavor_transformation/__init__.py
Lines 44 to 56 in 6ffae09
| # define default values for backward compatibility | |
| AdiabaticMSW = TransformationChain(in_sn.AdiabaticMSW()) | |
| NonAdiabaticMSWH = TransformationChain(in_sn.NonAdiabaticMSWH()) | |
| AdiabaticMSWes = TransformationChain(in_sn.AdiabaticMSWes()) | |
| NonAdiabaticMSWes = TransformationChain(in_sn.NonAdiabaticMSWes()) | |
| TwoFlavorDecoherence = TransformationChain(in_sn.TwoFlavorDecoherence()) | |
| NeutrinoDecay = TransformationChain(in_sn.AdiabaticMSW(), in_vacuum.NeutrinoDecay()) | |
| QuantumDecoherence = TransformationChain(in_sn.AdiabaticMSW(), in_vacuum.QuantumDecoherence()) | |
| EarthMatter = lambda mixing_params,AltAz: TransformationChain( | |
| in_sn.AdiabaticMSW(), | |
| in_earth=in_earth.EarthMatter(SNAltAz=AltAz), | |
| mixing_params=mixing_params | |
| ) |
so that previous code like AdiabaticMSW(MixingParams) would work.
I'm not sure about this whole interface - so I just tried to make a minimal adapter layer, to conform to the old interface. There are many other ways to do them, but I don't see any of them as simple and elegant. Make specific classmethod constructors, and use them as aliases in init?
Maybe you have an idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but this wouldn’t work at all. (Instead, it would result in
msw_nufit == msw_pdg == AdiabaticMSW.)
Could you explain, why? I thought, that in each line we create an instance of AdiabaticMSW, and then set the _mixing_params attribute for each of them specifically, so that msw_pdg and msw_pdg should be AdiabaticMSW instances with corresponding mixing parameters? Or am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought, that in each line we create an instance of
AdiabaticMSW
Nope—we’ve turned AdiabaticMSW from a class into an instance of a TransformationChain. So AdiabaticMSW(mix_pars) no longer creates new instances; it’s just syntactic sugar for TransformationChain.__call__(AdiabaticMSW, mix_pars), which passes AdiabaticMSW in as the self argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so that previous code like AdiabaticMSW(MixingParams) would work.
That wasn’t quite the previous interface, though. Previously, the function signature was def __init__(self, mix_angles=None, mh=MassHierarchy.NORMAL): and we’re breaking this deliberately to pass in a MixingParameters instead.
So … I’d be okay with breaking that “even more” (at least for now—maybe we can discuss at the hackathon next month how an intuitive interface for that should look like?) and deleting the __call__ for now before it causes any further confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note from Zoom call: Replace “singletons” with lambdas
Will do.
The use case you describe for TFD is a useful one so my suggestion is to have a Phenomenological Transformation in init.py called e.g. Equilibrate which implements this case, and to have ThreeFlavorDecoherence as prescription in in_sn.py. The ThreeFlavorDecoherence is useful for describing the effect of strong turbulence in supernovae; the TwoFlavorDecoherence describes the effect of weak turbulence. |
|
Opened #384 for the TFD issue. Splitting it up like you suggested sounds good to me; I’d just like to do that in a follow-up PR later, since this one is already more unwieldy than I’d like. |
|
Which branch should I start from? We seem to have a couple running in parallel. |
|
Probably from the branch of this PR here? ( Does that mean you’re done reviewing this PR and happy with it? If so, then I think the only remaining issue before we can merge this PR is the |
I agree with your suggestion to remove the definition of call as currently written. I would make it equivalent to the apply_to method. |
402345c to
6ffae09
Compare
|
I’ve moved Jim’s commits to a separate branch (and separate PR #385). Hopefully, keeping that discussion separate will help us get this PR here merged sooner |
|
Did we decide on the new name for MSWEffect? |
|
|
@jpkneller, about your commit: the implementation of get_transformed_spectra is already done in #363 |
Sorry, I just saw the error and didn't check. Is there anything left to do with this PR? |
jpkneller
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's merge this branch and #363 and then deal with the remaining small issues.
51fb540 to
27cc176
Compare
No, I think we're good. I've just reverted your commit to avoid any kind of merge conflicts in the future |


To be merged after #350
In this PR I do the following:
FlavorMatrixclass everywhere inget_probabilitiesThree/FourFlavorTransformation.Pmf_HighDensityLimitmethods toThree/FourFlavorMixingParameters.Pmf_HighDensityLimit- I think they belong next there, next toVacuumMixingMatrixEarthMattertransform (based on BEMEWS)MSWEffecttransform (based on SNOSHEWS)