diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc
index e1f822f..42c8602 100644
--- a/FixedOrderGen/src/PhaseSpacePoint.cc
+++ b/FixedOrderGen/src/PhaseSpacePoint.cc
@@ -1,715 +1,719 @@
 /**
  *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "PhaseSpacePoint.hh"
 
 #include <algorithm>
 
 #include "CLHEP/Vector/LorentzVector.h"
 
 #include "HEJ/Constants.hh"
 #include "HEJ/exceptions.hh"
 #include "HEJ/kinematics.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/utility.hh"
 
 #include "Process.hh"
 #include "Subleading.hh"
 
 using namespace HEJ;
 
 namespace HEJFOG{
 
   static_assert(
       std::numeric_limits<double>::has_quiet_NaN,
       "no quiet NaN for double"
   );
   constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
 
   HEJ::Event::EventData to_EventData(PhaseSpacePoint const & psp){
     HEJ::Event::EventData result;
     result.incoming = psp.incoming();
     assert(result.incoming.size() == 2);
     result.outgoing=psp.outgoing();
       // technically Event::EventData doesn't have to be sorted,
       // but PhaseSpacePoint should be anyway
     assert(
         std::is_sorted(
             begin(result.outgoing), end(result.outgoing),
             HEJ::rapidity_less{}
         )
     );
     assert(result.outgoing.size() >= 2);
     result.decays=psp.decays();
     result.parameters.central= {NaN, NaN, psp.weight() };
     return result;
   }
 
   namespace{
     bool can_swap_to_uno(
         HEJ::Particle const & p1, HEJ::Particle const & p2
     ){
       return is_parton(p1)
         && p1.type != pid::gluon
         && p2.type == pid::gluon;
     }
 
     size_t count_gluons(std::vector<Particle>::const_iterator first,
       std::vector<Particle>::const_iterator last){
       return std::count_if(first, last, [](Particle const & p)
         {return p.type == pid::gluon;});
     }
 
     /** assumes FKL configurations between first and last,
      * else there can be a quark in a non-extreme position
      * e.g. uno configuration gqg would pass
      */
     bool can_change_to_qqx(
       std::vector<Particle>::const_iterator first,
       std::vector<Particle>::const_iterator last){
       return 1 < count_gluons(first,last);
     }
     bool is_AWZ_proccess(Process const & proc){
       return proc.boson && is_AWZ_boson(*proc.boson);
     }
 
     bool is_up_type(Particle const & part){
       return HEJ::is_anyquark(part) && !(abs(part.type)%2);
     }
     bool is_down_type(Particle const & part){
       return HEJ::is_anyquark(part) && abs(part.type)%2;
     }
     bool can_couple_to_W(Particle const & part, pid::ParticleID const W_id){
       const int W_charge = W_id>0?1:-1;
       return abs(part.type)<5
         && ( (W_charge*part.type > 0 && is_up_type(part))
           || (W_charge*part.type < 0 && is_down_type(part)) );
     }
   }
 
   void PhaseSpacePoint::maybe_turn_to_subl(
       double chance,
       unsigned int const channels,
       Process const & proc,
       HEJ::RNG & ran
   ){
     if(proc.njets <= 2) return;
     assert(outgoing_.size() >= 2);
 
     // decide what kind of subleading process is allowed
     bool allow_uno = false;
     bool allow_strange = true;
     const size_t nout = outgoing_.size();
     const bool can_be_uno_backward = (channels&Subleading::uno)
       && can_swap_to_uno(outgoing_[0], outgoing_[1]);
     const bool can_be_uno_forward = (channels&Subleading::uno)
       && can_swap_to_uno(outgoing_[nout-1], outgoing_[nout-2]);
     allow_uno = can_be_uno_backward || can_be_uno_forward;
 
     bool allow_qqx = false;
     if(is_AWZ_proccess(proc)) {
       allow_qqx = (channels&Subleading::qqx)
         && can_change_to_qqx(outgoing_.cbegin(), outgoing_.cend());
       if(std::none_of(outgoing_.cbegin(), outgoing_.cend(),
           [&proc](Particle const & p){ return can_couple_to_W(p, *proc.boson);})) {
         // enforce qqx if A/W/Z can't couple somewhere else
         assert(allow_qqx);
         allow_uno = false;
         chance = 1.;
         // strange not allowed for W
         if(abs(*proc.boson)== pid::Wp) allow_strange = false;
       }
     }
 
     if(!allow_uno && !allow_qqx) return;
     if(ran.flat() < chance){
       weight_ /= chance;
       if(allow_uno && !allow_qqx){
           turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran);
       } else if (!allow_uno && allow_qqx) {
         turn_to_qqx(allow_strange, ran);
       } else {
         assert( allow_uno && allow_qqx);
         if(ran.flat() < 0.5) turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran);
         else turn_to_qqx(allow_strange, ran);
         weight_ *= 2.;
       }
     } else weight_ /= 1 - chance;
 
   }
 
   void PhaseSpacePoint::turn_to_uno(
       const bool can_be_uno_backward, const bool can_be_uno_forward,
       HEJ::RNG & ran
   ){
     if(!can_be_uno_backward && !can_be_uno_forward) return;
     const size_t nout = outgoing_.size();
     if(can_be_uno_backward && can_be_uno_forward){
       if(ran.flat() < 0.5){
         std::swap(outgoing_[0].type, outgoing_[1].type);
       } else {
         std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type);
       }
       weight_ *= 2.;
     } else if(can_be_uno_backward){
       std::swap(outgoing_[0].type, outgoing_[1].type);
     } else {
       assert(can_be_uno_forward);
       std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type);
     }
   }
 
   void PhaseSpacePoint::turn_to_qqx(const bool allow_strange, HEJ::RNG & ran){
     /// find first and last gluon in FKL chain
     auto first = std::find_if(outgoing_.begin(), outgoing_.end(),
       [](Particle const & p){return p.type == pid::gluon;});
     std::vector<Particle*> FKL_gluons;
     for(auto p = first; p!=outgoing_.end(); ++p){
       if(p->type == pid::gluon) FKL_gluons.push_back(&*p);
       else if(is_anyquark(*p)) break;
     }
     const size_t ng = FKL_gluons.size();
     if(ng < 2)
       throw std::logic_error("not enough gluons to create qqx");
     // select flavour of quark
     const double r1 = 2.*ran.flat()-1.;
     const double max_flavour = allow_strange?n_f:n_f-1;
     weight_ *= max_flavour*2;
     int flavour = pid::down + std::floor(std::abs(r1)*max_flavour);
     flavour*=r1<0.?-1:1;
     // select gluon for switch
     const size_t idx = floor((ng-1) * ran.flat());
     weight_ *= (ng-1);
     FKL_gluons[idx]->type = ParticleID(flavour);
     FKL_gluons[idx+1]->type = ParticleID(-flavour);
   }
 
   template<class ParticleMomenta>
   fastjet::PseudoJet PhaseSpacePoint::gen_last_momentum(
       ParticleMomenta const & other_momenta,
       const double mass_square, const double y
   ) const {
     std::array<double,2> pt{0.,0.};
     for (auto const & p: other_momenta) {
       pt[0]-= p.px();
       pt[1]-= p.py();
     }
 
     const double mperp = sqrt(pt[0]*pt[0]+pt[1]*pt[1]+mass_square);
     const double pz=mperp*sinh(y);
     const double E=mperp*cosh(y);
 
     return {pt[0], pt[1], pz, E};
   }
 
   namespace {
     //! adds a particle to target (in correct rapidity ordering)
     //! @returns    positon of insertion
     auto insert_particle(std::vector<HEJ::Particle> & target,
       HEJ::Particle && particle
     ){
       const auto pos = std::upper_bound(
           begin(target),end(target),particle,rapidity_less{}
       );
       target.insert(pos, std::move(particle));
       return pos;
     }
   }
 
   namespace {
     HEJ::ParticleID reconstruct_boson_id(HEJ::ParticleID type1,
                                          HEJ::ParticleID type2
     ){
       const int pidsum = type1 + type2;
       if(pidsum == +1) {
         assert(is_antilepton(type1));
         if(is_antineutrino(type1)) {
           throw HEJ::not_implemented{"lepton-flavour violating final state"};
         }
         assert(is_neutrino(type2));
         // charged antilepton + neutrino means we had a W+
         return HEJ::pid::Wp;
       }
       if(pidsum == -1) {
         assert(is_antilepton(type1));
         if(is_neutrino(type2)) {
           throw HEJ::not_implemented{"lepton-flavour violating final state"};
         }
         assert(is_antineutrino(type1));
         // charged lepton + antineutrino means we had a W-
         return HEJ::pid::Wm;
       }
       throw HEJ::not_implemented{
         "final state with leptons "+HEJ::name(type1)+" and "+HEJ::name(type2)
       };
     }
   }
 
   PhaseSpacePoint::PhaseSpacePoint(
     Process const & proc_in,
     JetParameters const & jet_param,
     HEJ::PDF & pdf, double E_beam,
     double const subl_chance,
     unsigned int const subl_channels,
-    ParticlesPropMap const & particles_properties,
+    ParticlesPropMap const & particles_properties_in,
     HEJ::RNG & ran
   )
   {
     auto proc{ proc_in };
+    auto particles_properties{ particles_properties_in };
     assert(proc.njets >= 2);
     if(proc.boson
       && particles_properties.find(*(proc.boson))
         == particles_properties.end())
       throw HEJ::missing_option("Boson "
         +std::to_string(*(proc.boson))+" can't be generated: missing properties");
     status_ = good;
     weight_ = 1;
 
     const int nout = proc.njets + (proc.boson?1:0) + proc.leptons.size();
     outgoing_.reserve(nout);
 
     // generate parton momenta
     const bool is_pure_jets = (nout == proc.njets);
     auto partons = gen_LO_partons(
         proc.njets, is_pure_jets, jet_param, E_beam, ran
     );
     // pre fill flavour with gluons
     for(auto&& p_out: partons) {
       outgoing_.emplace_back(Particle{pid::gluon, std::move(p_out), {}});
     }
     if(status_ != good) return;
 
     // create phase space point for (lepton,neutrino)-pair of W+/W-
     // @TODO this have massive overlap with the "create boson" section
     if(proc.leptons.size()==2){
+      /// @TODO changes to the runcard should be set inside config.cc
       proc.boson = reconstruct_boson_id(proc.leptons[0], proc.leptons[1]);
+      if(particles_properties[*proc.boson].decays.size() == 0)
+        particles_properties[*proc.boson].decays.push_back({proc.leptons, 1});
       weight_*=M_PI; // Divide by 8, *2, see WPhaseSpace.tex @TODO where?!?
     }
     if(proc.boson){ // decay boson
       const auto & boson_prop = particles_properties.at(*proc.boson);
       auto boson{ gen_boson(*proc.boson, boson_prop.mass, boson_prop.width, ran) };
       const auto pos{insert_particle(outgoing_, std::move(boson))};
       if(! boson_prop.decays.empty()){
         const size_t boson_idx = std::distance(begin(outgoing_), pos);
         decays_.emplace(
             boson_idx,
             decay_boson(outgoing_[boson_idx], boson_prop.decays, ran)
         );
       }
     }
     // normalisation of momentum-conserving delta function
     weight_ *= pow(2*M_PI, 4);
 
     /** @TODO
      *  uf (jet_param.min_pt) doesn't correspond to our final scale choice.
      *  The HEJ scale generators currently expect a full event as input,
      *  so fixing this is not completely trivial
      */
     reconstruct_incoming(proc, subl_channels, pdf, E_beam, jet_param.min_pt, ran);
     if(status_ != good) return;
     // set outgoing states
     most_backward_FKL(outgoing_).type = incoming_[0].type;
     most_forward_FKL(outgoing_).type = incoming_[1].type;
 
     maybe_turn_to_subl(subl_chance, subl_channels, proc, ran);
 
     if(proc.boson) couple_boson(*proc.boson, ran);
   }
 
   double PhaseSpacePoint::gen_hard_pt(
       int np , double ptmin, double ptmax, double y,
       HEJ::RNG & ran
   ) {
     // heuristic parameters for pt sampling
     const double ptpar = ptmin + np/5.;
     const double arg_small_y = atan((ptmax - ptmin)/ptpar);
     const double y_cut = 3.;
 
     const double r1 = ran.flat();
     if(y < y_cut){
       const double pt = ptmin + ptpar*tan(r1*arg_small_y);
       const double temp = cos(r1*arg_small_y);
       weight_ *= pt*ptpar*arg_small_y/(temp*temp);
       return pt;
     }
 
     const double ptpar2 = ptpar/(1 + 5*(y-y_cut));
     const double temp = 1. - std::exp((ptmin-ptmax)/ptpar2);
     const double pt = ptmin - ptpar2*std::log(1-r1*temp);
     weight_ *= pt*ptpar2*temp/(1-r1*temp);
     return pt;
   }
 
   double PhaseSpacePoint::gen_soft_pt(int np, double max_pt, HEJ::RNG & ran) {
     constexpr double ptpar = 4.;
 
     const double r = ran.flat();
     const double pt = max_pt + ptpar/np*std::log(r);
     weight_ *= pt*ptpar/(np*r);
     return pt;
   }
 
   double PhaseSpacePoint::gen_parton_pt(
       int count, JetParameters const & jet_param, double max_pt, double y,
       HEJ::RNG & ran
   ) {
     constexpr double p_small_pt = 0.02;
 
     if(! jet_param.peak_pt) {
        return gen_hard_pt(count, jet_param.min_pt, max_pt, y, ran);
     }
     const double r = ran.flat();
     if(r > p_small_pt) {
       weight_ /= 1. - p_small_pt;
       return gen_hard_pt(count, *jet_param.peak_pt, max_pt, y, ran);
     }
     weight_ /= p_small_pt;
     const double pt =  gen_soft_pt(count, *jet_param.peak_pt, ran);
     if(pt < jet_param.min_pt) {
       weight_=0.0;
       status_ = not_enough_jets;
       return jet_param.min_pt;
     }
     return pt;
   }
 
   std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_LO_partons(
       int np, bool is_pure_jets,
       JetParameters const & jet_param,
       double max_pt,
       HEJ::RNG & ran
   ){
     if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"};
 
     weight_ /= pow(16.*pow(M_PI,3),np);
     weight_ /= std::tgamma(np+1); //remove rapidity ordering
     std::vector<fastjet::PseudoJet> partons;
     partons.reserve(np);
     for(int i = 0; i < np; ++i){
       const double y = -jet_param.max_y + 2*jet_param.max_y*ran.flat();
       weight_ *= 2*jet_param.max_y;
 
       const bool is_last_parton = i+1 == np;
       if(is_pure_jets && is_last_parton) {
         constexpr double parton_mass_sq = 0.;
         partons.emplace_back(gen_last_momentum(partons, parton_mass_sq, y));
         break;
       }
 
       const double phi = 2*M_PI*ran.flat();
       weight_ *= 2.0*M_PI;
 
       const double pt = gen_parton_pt(np, jet_param, max_pt, y, ran);
       if(weight_ == 0.0) return {};
 
       partons.emplace_back(fastjet::PtYPhiM(pt, y, phi));
       assert(jet_param.min_pt <= partons[i].pt());
       assert(partons[i].pt() <= max_pt+1e-5);
     }
     // Need to check that at LO, the number of jets = number of partons;
     fastjet::ClusterSequence cs(partons, jet_param.def);
     auto cluster_jets=cs.inclusive_jets(jet_param.min_pt);
     if (cluster_jets.size()!=unsigned(np)){
       weight_=0.0;
       status_ = not_enough_jets;
       return {};
     }
 
     std::sort(begin(partons), end(partons), rapidity_less{});
 
     return partons;
   }
 
   Particle PhaseSpacePoint::gen_boson(
       HEJ::ParticleID bosonid, double mass, double width,
       HEJ::RNG & ran
   ){
     // Usual phase space measure
     weight_ /= 16.*pow(M_PI, 3);
 
     // Generate a y Gaussian distributed around 0
     /// @TODO check magic numbers for different boson Higgs
     /// @TODO better sampling for W
     double stddev_y = abs(bosonid)==pid::Wp?0.7:1.6;
     const double y = random_normal(stddev_y, ran);
     const double r1 = ran.flat();
     const double s_boson = mass*(
         mass + width*tan(M_PI/2.*r1 + (r1-1.)*atan(mass/width))
     );
     // off-shell s_boson sampling
     /// @TODO use a flag instead
     /// @TODO is this correct?
     if(abs(bosonid) == pid::Wp){
       static double temp=atan(mass/width);
       weight_*=(width*mass*(M_PI+2.*temp))/(1.+cos(M_PI*r1+2.*(-1.+r1)*temp));
     }
 
     auto p = gen_last_momentum(outgoing_, s_boson, y);
 
     return Particle{bosonid, std::move(p), {}};
   }
 
   Particle const & PhaseSpacePoint::most_backward_FKL(
       std::vector<Particle> const & partons
   ) const{
     if(!HEJ::is_parton(partons[0])) return partons[1];
     return partons[0];
   }
 
   Particle const & PhaseSpacePoint::most_forward_FKL(
       std::vector<Particle> const & partons
   ) const{
     const size_t last_idx = partons.size() - 1;
     if(!HEJ::is_parton(partons[last_idx])) return partons[last_idx-1];
     return partons[last_idx];
   }
 
   Particle & PhaseSpacePoint::most_backward_FKL(
       std::vector<Particle> & partons
   ) const{
     if(!HEJ::is_parton(partons[0])) return partons[1];
     return partons[0];
   }
 
   Particle & PhaseSpacePoint::most_forward_FKL(
       std::vector<Particle> & partons
   ) const{
     const size_t last_idx = partons.size() - 1;
     if(!HEJ::is_parton(partons[last_idx])) return partons[last_idx-1];
     return partons[last_idx];
   }
 
   namespace {
     /// partons are ordered: even = anti, 0 = gluon
     ParticleID index_to_pid(size_t i){
       if(!i) return pid::gluon;
       return static_cast<ParticleID>(i%2?(i+1)/2:-i/2);
     }
 
     /// partons are ordered: even = anti, 0 = gluon
     size_t pid_to_index(ParticleID id){
       if(id==pid::gluon) return 0;
       return id>0?id*2-1:abs(id)*2;
     }
 
     std::bitset<11> init_allowed(ParticleID const id){
       if(abs(id) == pid::proton)
         return ~0;
       std::bitset<11> out = 0;
       if(is_parton(id))
         out[pid_to_index(id)] = 1;
       return out;
     }
 
     /// decides which "index" (see index_to_pid) are allowed for process
     std::bitset<11> allowed_quarks(ParticleID const boson){
       std::bitset<11> allowed = ~0;
       if(abs(boson) == pid::Wp){
         // special case W:
         // Wp: anti-down or up-type quark, no b/t -> 0001100110(1) = 205
         // Wm: down or anti-up-type quark, no b/t -> 0010011001(1) = 307
         allowed = boson>0?205:307;
       }
       return allowed;
     }
   }
 
   /**
    * checks which partons are allowed as initial state:
    * 1. only allow what is given in the Runcard (p -> all)
    * 2. A/W/Z require something to couple to
    *  a) no qqx => no incoming gluon
    *  b) 2j     => no incoming gluon
    *  c) 3j     => can couple OR is gluon => 2 gluons become qqx later
    */
   std::array<std::bitset<11>,2> PhaseSpacePoint::filter_partons(
       Process const & proc, unsigned int const subl_channels, HEJ::RNG & ran
   ){
     std::array<std::bitset<11>,2> allowed_partons{
         init_allowed(proc.incoming[0]),
         init_allowed(proc.incoming[1])
       };
     bool const allow_qqx = subl_channels&Subleading::qqx;
     // special case A/W/Z
     if(is_AWZ_proccess(proc) && ((proc.njets < 4) || !allow_qqx)){
       // all possible incoming states
       auto allowed(allowed_quarks(*proc.boson));
       if(proc.njets == 2 || !allow_qqx) allowed[0]=0;
 
       // possible states per leg
       std::array<std::bitset<11>,2> const maybe_partons{
         allowed_partons[0]&allowed, allowed_partons[1]&allowed};
 
       if(maybe_partons[0].any() && maybe_partons[1].any()){
         // two options to get allowed initial state => choose one at random
         const size_t idx = ran.flat() < 0.5;
         allowed_partons[idx] = maybe_partons[idx];
         // else choose the possible
       } else if(maybe_partons[0].any()) {
         allowed_partons[0] = maybe_partons[0];
       } else if(maybe_partons[1].any()) {
         allowed_partons[1] = maybe_partons[1];
       } else{
         throw std::invalid_argument{"Incoming state not allowed."};
       }
     }
     return allowed_partons;
   }
 
   void PhaseSpacePoint::reconstruct_incoming(
       Process const & proc, unsigned int const subl_channels,
       HEJ::PDF & pdf, double E_beam,
       double uf,
       HEJ::RNG & ran
   ){
     std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_);
     // calculate xa, xb
     const double sqrts=2*E_beam;
     const double xa=(incoming_[0].p.e()-incoming_[0].p.pz())/sqrts;
     const double xb=(incoming_[1].p.e()+incoming_[1].p.pz())/sqrts;
     // abort if phase space point is outside of collider energy reach
     if (xa>1. || xb>1.){
       weight_=0;
       status_ = too_much_energy;
       return;
     }
     auto const & ids = proc.incoming;
     std::array<std::bitset<11>,2> allowed_partons(
       filter_partons(proc, subl_channels, ran));
     for(size_t i = 0; i < 2; ++i){
       if(ids[i] == pid::proton || ids[i] == pid::p_bar){
       // pick ids according to pdfs
         incoming_[i].type =
           generate_incoming_id(i, i?xb:xa, uf, pdf, allowed_partons[i], ran);
       } else {
         assert(allowed_partons[i][pid_to_index(ids[i])]);
         incoming_[i].type = ids[i];
       }
     }
     assert(momentum_conserved(1e-7));
   }
 
   HEJ::ParticleID PhaseSpacePoint::generate_incoming_id(
       size_t const beam_idx, double const x, double const uf,
       HEJ::PDF & pdf, std::bitset<11> allowed_partons, HEJ::RNG & ran
   ){
     std::array<double,11> pdf_wt;
     pdf_wt[0] = allowed_partons[0]?fabs(pdf.pdfpt(beam_idx,x,uf,pid::gluon)):0.;
     double pdftot = pdf_wt[0];
     for(size_t i = 1; i < pdf_wt.size(); ++i){
       pdf_wt[i] = allowed_partons[i]?4./9.*fabs(pdf.pdfpt(beam_idx,x,uf,index_to_pid(i))):0;
       pdftot += pdf_wt[i];
     }
     const double r1 = pdftot * ran.flat();
     double sum = 0;
     for(size_t i=0; i < pdf_wt.size(); ++i){
       if (r1 < (sum+=pdf_wt[i])){
         weight_*= pdftot/pdf_wt[i];
         return index_to_pid(i);
       }
     }
     std::cerr << "Error in choosing incoming parton: "<<x<<" "<<uf<<" "
       <<sum<<" "<<pdftot<<" "<<r1<<std::endl;
     throw std::logic_error{"Failed to choose parton flavour"};
   }
 
   void PhaseSpacePoint::couple_boson(
     HEJ::ParticleID const boson, HEJ::RNG & ran
   ){
     if(abs(boson) != pid::Wp) return; // only matters for W
     /// @TODO this could be use to sanity check gamma and Z
 
     // find all possible quarks
     std::vector<Particle*> allowed_parts;
     for(auto & part: outgoing_){
       // Wp -> up OR anti-down, Wm -> anti-up OR down, no bottom
       if ( can_couple_to_W(part, boson) )
         allowed_parts.push_back(&part);
     }
     if(allowed_parts.size() == 0){
       throw std::logic_error{"Found no parton for coupling with boson"};
     }
 
     // select one and flip it
     size_t idx = 0;
     if(allowed_parts.size() > 1){
       /// @TODO more efficient sampling
       ///       old code: probability[i] = exp(parton[i].y - W.y)
       idx = floor(ran.flat()*allowed_parts.size());
       weight_ *= allowed_parts.size();
     }
     const int W_charge = boson>0?1:-1;
     allowed_parts[idx]->type =
       static_cast<ParticleID>( allowed_parts[idx]->type - W_charge );
   }
 
   double PhaseSpacePoint::random_normal(
       double stddev,
       HEJ::RNG & ran
   ){
     const double r1 = ran.flat();
     const double r2 = ran.flat();
     const double lninvr1 = -log(r1);
     const double result = stddev*sqrt(2.*lninvr1)*cos(2.*M_PI*r2);
     weight_ *= exp(result*result/(2*stddev*stddev))*sqrt(2.*M_PI)*stddev;
     return result;
   }
 
   bool PhaseSpacePoint::momentum_conserved(double ep) const{
     fastjet::PseudoJet diff;
     for(auto const & in: incoming()) diff += in.p;
     for(auto const & out: outgoing()) diff -= out.p;
     return nearby_ep(diff, fastjet::PseudoJet{}, ep);
   }
 
   Decay PhaseSpacePoint::select_decay_channel(
       std::vector<Decay> const & decays,
       HEJ::RNG & ran
   ){
     double br_total = 0.;
     for(auto const & decay: decays) br_total += decay.branching_ratio;
     // adjust weight
     // this is given by (channel branching ratio)/(chance to pick channel)
     // where (chance to pick channel) =
     //       (channel branching ratio)/(total branching ratio)
     weight_ *= br_total;
     const double r1 = br_total*ran.flat();
     double br_sum = 0.;
     for(auto const & decay: decays){
       br_sum += decay.branching_ratio;
       if(r1 < br_sum) return decay;
     }
     throw std::logic_error{"unreachable"};
   }
 
   std::vector<Particle> PhaseSpacePoint::decay_boson(
       HEJ::Particle const & parent,
       std::vector<Decay> const & decays,
       HEJ::RNG & ran
   ){
     const auto channel = select_decay_channel(decays, ran);
     if(channel.products.size() != 2){
       throw HEJ::not_implemented{
         "only decays into two particles are implemented"
       };
     }
     std::vector<Particle> decay_products(channel.products.size());
     for(size_t i = 0; i < channel.products.size(); ++i){
       decay_products[i].type = channel.products[i];
     }
     // choose polar and azimuth angle in parent rest frame
     const double E = parent.m()/2;
     const double theta = 2.*M_PI*ran.flat();
     const double cos_phi = 2.*ran.flat()-1.;
     const double sin_phi = sqrt(1. - cos_phi*cos_phi);  // Know 0 < phi < pi
     const double px = E*cos(theta)*sin_phi;
     const double py = E*sin(theta)*sin_phi;
     const double pz = E*cos_phi;
     decay_products[0].p.reset(px, py, pz, E);
     decay_products[1].p.reset(-px, -py, -pz, E);
     for(auto & particle: decay_products) particle.p.boost(parent.p);
     return decay_products;
   }
 }