impy-project / chromo

Hadronic Interaction Model interface in PYthon
Other
30 stars 7 forks source link

Pythia8 afterburner #167

Closed jncots closed 1 year ago

jncots commented 1 year ago

This should fix #87 mentioned in #165 and some other problems releated to issue that some event generators cannot decay some particles.

jncots commented 1 year ago

About execution time:

DecayAfterburner.__call__(event) typically adds the same execution time as it is required for event generation (~ 1 ms), i.e. the time increases twice. For DpmJet:

image

37% of the time takes decay itself (forceHadronLevel(), _decay on picture), 31 % takes extraction of event stack from Pythia8 (_extract_result on picture), 10% takes filling of Pythia8 stack (_fill), 6% np.copy and np.where, 15 % - copying initial stack to final one.

image

jncots commented 1 year ago

Problems noticed during testing:

jncots commented 1 year ago

I started to integrate DecayAutoburner with models. I changed interface to provide more flexible settings for decaying/stable particles. "theonly_decaying_pids" sets all particles as stable, except the one that in the list. "theonly_stable_pids" sets all particles as unstable, except the one that in the list. The arguments are convenient to set an initial state of stable/unstable in Pythia. "extra_decaying/stable_pids" toggle this state further.

I noticed that no model has expicit lists of stable/unstable particles. Also, there is no common interface to inquire event generators what they consider stable/unstable. Currently, I set:

self._afterburner = DecayAfterburner(
                seed=seed, theonly_stable_pids=long_lived
            )

,i.e. all other particles that is not in long_lived list will decay by DecayAfterburner.

I guess, it is just enough to keep final_state_particles list for each instance and implicitly assume that all other should decay. Then set_stable should modify this list. final_state_particles could be a property which call set_stable if a new list is assigned to it.

The easiest way to switch on DecayAfterburner for a model is to provide a common method switch_on_autoburner, which will call:

self._afterburner = DecayAfterburner(
                seed=seed, theonly_stable_pids=self.final_state_particles
            )
HDembinski commented 1 year ago

I am unhappy with the naming.

1) I would prefer a more serious name than "DecayAfterburner". I am open for suggestions. Since Pythia8 is doing the work, should we call it one these?

2) The keyword theonly_stable_pids breaks rules regarding underscores. Either the_only_stable_pids or just stable_pids. Keywords shouldn't be too long and don't have to be self-explanatory. You have to write documentation anyway to explain what the keyword does.

Pythia8Decay is primarily a component for us the library developers. It should be used automatically internally by some model that cannot internally decay all particles. It should only be run if the user requests that particles should decay which the model cannot decay by itself. All this should happen without any special user input. The user just requests which particles should be stable/decay with set_stable.

I noticed that no model has expicit lists of stable/unstable particles. Also, there is no common interface to inquire event generators what they consider stable/unstable. Currently, I set:

We already have an interface to set particles stable with set_stable, but as you say we don't have a way to query which particles are currently set as stable in the model.

I like the idea of replacing this interface with a property on the model called final_state_particles which returns a list of pids that are considered final state by the model. Assigning a list to final_state_particles should be allowed and should set all those particles stable and decay all others. We can then deprecate set_stable which is a bit of a misnomer.

jncots commented 1 year ago

I added the functionality discussed above. By default DecayAfterburner is not used. To switch on/off one should explicitly call switch_afterburner(on=True/False).

_final_state_particles is the list containing list of the pdg ids which are considered as final. They can be obtained, but not directly assigned. set_stable is used to do this. The default value is long_lived.

gen = chromo.models.DpmjetIII193(evt_kin)
gen.switch_afterburner()
for event in gen(10):
     ...

# switch off afterburner
gen.switch_afterburner(on=False)
jncots commented 1 year ago

I am unhappy with the naming.

Ok, I like Pythia8DecayHandler. stable_pids is fine.

It should only be run if the user requests that particles should decay which the model cannot decay by itself.

Ideally, yes. Is there any good way to get those particles?

Assigning a list to final_state_particles should be allowed and should set all those particles stable and decay all others.

Basically, it is done in _update_final_state_particles but currently it is private method. I find it more covenient to be a method instead of property setter because it allows add and remove particles from the list via second parameter (stable/unstable).

HDembinski commented 1 year ago

Ideally, yes. Is there any good way to get those particles?

It is model specific. To my knowledge, only QGSJet does not decay anything. I learned from your comments that there are also some issues with DPMJet. Most of the other models do not need this functionality.

I don't like the current design.

I find it more covenient to be a method instead of property setter because it allows add and remove particles from the list via second parameter (stable/unstable).

Allowing assignment to final_state_particles is pure convenience, but it is Pythonic. We need this propery anyway to have an interface to get the list of particles. It is then natural/pythonic to also support assignment to this property. We don't need another method to add/remove individual particles, we have set_stable for that.

jncots commented 1 year ago

Pythia8DecayHandler should not be called by MCRun. It is not a generic component that all models need.

I checked all models what they decay and what not. It turns out that only EposLHC decays everything that could be passed to it as unstable particles. Models that use Pythia6 (Sibyll, Sophia) and Pythia6 itself cannot decay neutron. Phojet, Dpmjet - just not decay what set as unstable. UrQMD and QGSJet just don't decay particles.

So, I would say we need Pythia8DecayHandler for pretty much everything.

I removed the possibility for switching on/off Pythia8DecayHandler. Pythia8DecayHandler will not be used where Pythia8 is not available (like on Windows). In other cases, the event.pid is checked, and if there are particles which are not in final_state_particles Pythia8DecayHandler will be used.

In all models _decaying_pids first set to unstable, and then final_state_particles are set for stable. In such case we are sure that there are no unstable particles, except that we are set. _decaying_pids are model-specific because different models know different types of particles.

Even by setting stable particles as above, most of the models ignores some particles to decay:

Only EposLHC decays all that is set as decaying.

Thus, currently Pythia8DecayHandler will be called only if the model produces particles which are not in final_state_particles. For a standard long_lived particles (30 psec) Pythia8DecayHandler will be used by QGSJet, Phojet, Dpmjet, and UrQMD. It probably could be used sometimes by Sibyll because I noticed it happens from time to time that stack contains undecayed particles. Sibyll21 will use Pythia8DecayHandler when one of [-13, 13, 130, 310] is unstable.

jncots commented 1 year ago

I notice one unpleasant side effect of using Pythia8 as DecayHandler: it looks like one can set stable/decay only for the pair of particle-antiparticle. I.e. one cannot set pi+ as stable and pi- as unstable.

jncots commented 1 year ago

_validate_decay function is added which checks whether all particles that should decay have been decayed. If not, it uses decay_handler if it is switched on, or suggest to do that if decay_handler is switched off.

afedynitch commented 1 year ago

@jncots, could you pull from main before preparing for merge? I forgot that SIBYLL* has internal decays and it works differently compared to normal SIBYLL. We won't get short lived particles out of it.

At very least it must XFAIL on test_final_state.py.

jncots commented 1 year ago

@jncots, could you pull from main before preparing for merge?

Because the next merge is for #170, I've done it there.

jncots commented 1 year ago

After @afedynitch 's review: