Merge lp:~maddevelopers/mg5amcnlo/1.4.5 into lp:~madteam/mg5amcnlo/trunk

Proposed by Olivier Mattelaer
Status: Merged
Merged at revision: 210
Proposed branch: lp:~maddevelopers/mg5amcnlo/1.4.5
Merge into: lp:~madteam/mg5amcnlo/trunk
Diff against target: 1113 lines (+287/-179)
15 files modified
Template/Source/genps.inc (+1/-1)
Template/bin/internal/run_pythia (+2/-1)
Template/bin/madevent (+2/-0)
UpdateNotes.txt (+16/-1)
madgraph/VERSION (+2/-2)
madgraph/core/base_objects.py (+5/-1)
madgraph/interface/madevent_interface.py (+114/-127)
madgraph/interface/madgraph_interface.py (+7/-6)
madgraph/iolibs/template_files/matrix_madevent_group_v4.inc (+1/-1)
madgraph/various/cluster.py (+45/-7)
madgraph/various/gen_crossxhtml.py (+23/-29)
madgraph/various/misc.py (+61/-0)
madgraph/various/sum_html.py (+5/-1)
models/mssm/parameters.py (+2/-2)
tests/unit_tests/iolibs/test_export_v4.py (+1/-0)
To merge this branch: bzr merge lp:~maddevelopers/mg5amcnlo/1.4.5
Reviewer Review Type Date Requested Status
Olivier Mattelaer Approve
Johan Alwall (community) Approve
Review via email: mp+101662@code.launchpad.net

Description of the change

mainly cluster support.
This might be useful to limit the problem on the ucl site.
(mainly allow up to 5 minutes of unresponsive condor)
and wait up to 5 minutes if the files results.dat didn't exits.

Cheers,

Olivier

To post a comment you must log in.
Revision history for this message
Johan Alwall (johan-alwall) wrote :

Hello Olivier,

Very good. Comments/questions below:

> This might be useful to limit the problem on the ucl site.
> (mainly allow up to 5 minutes of unresponsive condor)
> and wait up to 5 minutes if the files results.dat didn't exits.
>
> === modified file 'UpdateNotes.txt'
> +1.4.5 (11/04/12)  OM: correct pointless bug #975647 (SLAH convention problem)
> +                      Thanks to Sho Iwamoto

Replace "pointless" with "minor".

> === modified file 'madgraph/various/cluster.py'
> --- madgraph/various/cluster.py 2012-03-20 19:55:40 +0000
> +++ madgraph/various/cluster.py 2012-04-11 23:06:24 +0000
> @@ -31,7 +31,7 @@
>  class NotImplemented(MadGraph5Error):
>     pass
>
> -def multiple_try(nb_try=5, sleep=1):
> +def multiple_try(nb_try=5, sleep=20):

This only affects waiting time if first attempt fails, right?

>     def deco_retry(f):
>         def deco_f_retry(*args, **opt):
> @@ -41,7 +41,7 @@
>                 except KeyboardInterrupt:
>                     raise
>                 except:
> -                    time.sleep(sleep)
> +                    time.sleep(sleep * nb_try)

Is this sleep * number of attempts already made, so that the time increases? That would make sense.

Cheers,
Johan

lp:~maddevelopers/mg5amcnlo/1.4.5 updated
212. By Olivier Mattelaer

make the waiting time increasing (thanks Johan to have pointed my mistake on that)
and modify the updateNote

213. By Olivier Mattelaer

Add waiting for results.dat if not present (up to one minut).
Add the possibility to clean to cluster in case of error. (done by default)
-refactoring the '-f' option to pass it to a global variable in madevent cmd interface

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Hi Johan,

>> === modified file 'madgraph/various/cluster.py'
>> --- madgraph/various/cluster.py 2012-03-20 19:55:40 +0000
>> +++ madgraph/various/cluster.py 2012-04-11 23:06:24 +0000
>> @@ -31,7 +31,7 @@
>> class NotImplemented(MadGraph5Error):
>> pass
>>
>> -def multiple_try(nb_try=5, sleep=1):
>> +def multiple_try(nb_try=5, sleep=20):
>
> This only affects waiting time if first attempt fails, right?

Yes.

>> def deco_retry(f):
>> def deco_f_retry(*args, **opt):
>> @@ -41,7 +41,7 @@
>> except KeyboardInterrupt:
>> raise
>> except:
>> - time.sleep(sleep)
>> + time.sleep(sleep * nb_try)
>
> Is this sleep * number of attempts already made, so that the time
> increases? That would make sense.
>

That was the idea, but looks like that I put the wrong variable.
Thanks to have point this.

So now the waiting time will be
20
40
60
80
100

So a total of 5 minuts.

I also note that one of my modification was not done.
It was adding a multiple try to the results reading since it looks
like that some time, they are filesystem problem
and that the file is not yet on disk.

I have also added that we propose the clean the queue is something
goes wrong.
(This is done by default on the cluster)
The reason not always doing it is that this might remove the reason of
the problem.

Cheers,

Olivier

> Cheers,
> Johan
> --
> https://code.launchpad.net/~maddevelopers/madgraph5/1.4.5/+merge/
> 101662
> You proposed lp:~maddevelopers/madgraph5/1.4.5 for merging.

lp:~maddevelopers/mg5amcnlo/1.4.5 updated
214. By Johan Alwall

Proper reading of cross section and number of events from Pythia log

215. By Johan Alwall

Proper reading of cross section and number of events from Pythia log

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Hi Johan,

I've fixed the error that you pointed to me.

I've also changed (again) the way to read the xsec for pythia. Just trying to factorize the
step for more clarity. I take an "old" code that I was using for MW. This reads a file line by line starting at the end of the file. Feel free to modify this class if you want.
(if you don't like the regexp line by line stuff for example).

I've also modified the function get_pythia_error
1) I've removed the argument cross which is pointless now (since you pass nacc)
2) I've changed all the call associate and fixed two of them where the argument were not
   corrected (n_acc = n_gen for two of those)

Otherwise, you change the formula for the computation of the pythia error. Why?
This one is obviously wrong.

Cheers,

Olivier

review: Needs Fixing
lp:~maddevelopers/mg5amcnlo/1.4.5 updated
216. By Olivier Mattelaer

fix few small bug
+ review Johan modifications

217. By Johan Alwall

Increased number of tries for non-zero helicities to 100 in genps.inc

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Hello Olivier,

> I've also changed (again) the way to read the xsec for pythia. Just trying to
> factorize the
> step for more clarity. I take an "old" code that I was using for MW. This
> reads a file line by line starting at the end of the file. Feel free to modify
> this class if you want.
> (if you don't like the regexp line by line stuff for example).

Excellent.

> Otherwise, you change the formula for the computation of the pythia error.
> Why?
> This one is obviously wrong.

The other one was wrong when you ask for fewer events, as well as when you have low matching efficiency. For small number of events, the uncertainty should be Poissonian, and not related to the total xsec uncertainty. On the other hand, if we add the two uncertainties for 100% acceptance of events, we clearly get twice the actual uncertainty. So this was the best approximation I could think of, which gives a reasonable result in both limits. Please let me know if you have some other suggestion.

Thanks,
Johan

>
>
> Cheers,
>
> Olivier

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Hi Johan,

> The other one was wrong when you ask for fewer events, as well as
> when you have low matching efficiency. For small number of events,
> the uncertainty should be Poissonian, and not related to the total
> xsec uncertainty.

In general you have the following formula:
\sigma_m = \sigma * N_acc / N_gen
this means that the associated error should be (supposing full
correlation)
error_m = error * N_acc / N_gen + \sigma * error(N_acc) / N_gen

If we take poissonian error for N_acc we get the following formula:

error_m = error * N_acc / N_gen + \sigma * sqrt(N_acc) / N_gen

I don't see any reason to drop the first term if N_acc is small.

> On the other hand, if we add the two uncertainties for 100%
> acceptance of events, we clearly get twice the actual uncertainty.

That's assuming that error = \sigma / sqrt(N) which is the error that
we would like to have, but in principle we don't have any relation
between the two.
In principle, the above formula still stands. So we should have this
factor two (or sqrt(2) if we use uncorellated error) if the relation
that you pre-suppose between the error and sigma is valid.
The only solution to not have it, is to not use Poissonian
distribution...
So I think that indeed this factor 2 (or sqrt(2)) is in fact real.

Concerning the point of choosing between correlated error and
uncorrelated error. I'm starting to change my mind in favor of the
second one.

Cheers,

Olivier

On Apr 16, 2012, at 4:00 AM, Johan Alwall wrote:

> Hello Olivier,
>
>> I've also changed (again) the way to read the xsec for pythia. Just
>> trying to
>> factorize the
>> step for more clarity. I take an "old" code that I was using for
>> MW. This
>> reads a file line by line starting at the end of the file. Feel
>> free to modify
>> this class if you want.
>> (if you don't like the regexp line by line stuff for example).
>
> Excellent.
>
>> Otherwise, you change the formula for the computation of the pythia
>> error.
>> Why?
>> This one is obviously wrong.
>
> The other one was wrong when you ask for fewer events, as well as
> when you have low matching efficiency. For small number of events,
> the uncertainty should be Poissonian, and not related to the total
> xsec uncertainty. On the other hand, if we add the two uncertainties
> for 100% acceptance of events, we clearly get twice the actual
> uncertainty. So this was the best approximation I could think of,
> which gives a reasonable result in both limits. Please let me know
> if you have some other suggestion.
>
> Thanks,
> Johan
>
>>
>>
>> Cheers,
>>
>> Olivier
> --
> https://code.launchpad.net/~maddevelopers/madgraph5/1.4.5/+merge/
> 101662
> You proposed lp:~maddevelopers/madgraph5/1.4.5 for merging.

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Hi Olivier,

> In general you have the following formula:
> \sigma_m = \sigma * N_acc / N_gen
> this means that the associated error should be (supposing full
> correlation)
> error_m = error * N_acc / N_gen + \sigma * error(N_acc) / N_gen
>
> If we take poissonian error for N_acc we get the following formula:
>
> error_m = error * N_acc / N_gen + \sigma * sqrt(N_acc) / N_gen

This all sounds reasonable, except that it can't be correct. If N_acc is small and N_gen large, say e.g. N_acc = 100 and N_gen = 1M, you get
error_m = (small) + \sigma * 10 / 1M,
so an uncertainty in the cross section of 1e-5. However, if I pick events from 1M events with a probability p=1e-4 (which is what we are doing here), the Poisson uncertainty is 1/sqrt(\mu)=1/sqrt(100) = 10%.

On the other hand, if we have 10,000 events and all pass Pythia, the uncertainty on the cross section can never be different from the uncertainty from MadGraph.

In fact, Rikkert went through the details of this scenario (i.e., the uncertainty on the number of events picked from a finite pool) in a wiki page: http://cp3wks05.fynu.ucl.ac.be/twiki/bin/view/Main/NumberOfEvents

Please refer to the corresponding page in the new wiki. Let me know if you can't find it.

All the best,
Johan

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Hi Johan,

This is the link that you look for.
https://server06.fynu.ucl.ac.be/projects/madgraph/wiki/NumberOfEvents
(all the pages are transfered on this wiki now).
But I don't think that this is the same problem.

> This all sounds reasonable, except that it can't be correct. If
> N_acc is small and N_gen large, say e.g. N_acc = 100 and N_gen = 1M,
> you get
> error_m = (small) + \sigma * 10 / 1M,
> so an uncertainty in the cross section of 1e-5. However, if I pick
> events from 1M events with a probability p=1e-4 (which is what we
> are doing here), the Poisson uncertainty is 1/sqrt(\mu)=1/sqrt(100)
> = 10%.

if you compute the relative error, you have the following formula:

error_m / sigma_m = error/sigma + 1 / sqrt(Nacc)

and indeed you have a relative error of 10%. So this is correct. (I
think you were confuse between sigma and sigma_m)

Cheers,

Olivier

On 16-avr.-12, at 21:34, Johan Alwall wrote:

> Hi Olivier,
>
>> In general you have the following formula:
>> \sigma_m = \sigma * N_acc / N_gen
>> this means that the associated error should be (supposing full
>> correlation)
>> error_m = error * N_acc / N_gen + \sigma * error(N_acc) / N_gen
>>
>> If we take poissonian error for N_acc we get the following formula:
>>
>> error_m = error * N_acc / N_gen + \sigma * sqrt(N_acc) / N_gen
>
> This all sounds reasonable, except that it can't be correct. If
> N_acc is small and N_gen large, say e.g. N_acc = 100 and N_gen = 1M,
> you get
> error_m = (small) + \sigma * 10 / 1M,
> so an uncertainty in the cross section of 1e-5. However, if I pick
> events from 1M events with a probability p=1e-4 (which is what we
> are doing here), the Poisson uncertainty is 1/sqrt(\mu)=1/sqrt(100)
> = 10%.
>
> On the other hand, if we have 10,000 events and all pass Pythia, the
> uncertainty on the cross section can never be different from the
> uncertainty from MadGraph.
>
> In fact, Rikkert went through the details of this scenario (i.e.,
> the uncertainty on the number of events picked from a finite pool)
> in a wiki page: http://cp3wks05.fynu.ucl.ac.be/twiki/bin/view/Main/NumberOfEvents
>
> Please refer to the corresponding page in the new wiki. Let me know
> if you can't find it.
>
> All the best,
> Johan
> --
> https://code.launchpad.net/~maddevelopers/madgraph5/1.4.5/+merge/
> 101662
> You proposed lp:~maddevelopers/madgraph5/1.4.5 for merging.

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Hello Olivier,

> if you compute the relative error, you have the following formula:
>
> error_m / sigma_m = error/sigma + 1 / sqrt(Nacc)
>
> and indeed you have a relative error of 10%. So this is correct. (I
> think you were confuse between sigma and sigma_m)

Indeed, I was using sigma_m in my formula, since that's what's read from the Pythia log file. So that part of our formulas are identical. Note that in general sigma_m is NOT sigma*n/N_gen, since n can be chosen using niter=n in the pythia_card. In fact, sigma should only be used to calculate the relative error of the full sample.

> This is the link that you look for.
> https://server06.fynu.ucl.ac.be/projects/madgraph/wiki/NumberOfEvents
> (all the pages are transfered on this wiki now).
> But I don't think that this is the same problem.

It should be quite similar. The relative uncertainty must go from the MadGraph error (when all events are accepted) to 1/sqrt(N) (when few events are accepted). This is exactly what you get for the statistics described there. However, my formula is clearly wrong, since you get too large error when you ask for a subset of the events using the niter flag in the pythia_card. So in fact, both tried events and accepted events are needed to calculate the error (the total number of events should be the tried events rather than Ngen, which is not relevant in the niter < Ngen case).

Johan

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :
Download full text (3.8 KiB)

On 17-avr.-12, at 00:33, Johan Alwall wrote:

> Hello Olivier,
>
>> if you compute the relative error, you have the following formula:
>>
>> error_m / sigma_m = error/sigma + 1 / sqrt(Nacc)
>>
>> and indeed you have a relative error of 10%. So this is correct. (I
>> think you were confuse between sigma and sigma_m)
>
> Indeed, I was using sigma_m in my formula, since that's what's read
> from the Pythia log file. So that part of our formulas are
> identical. Note that in general sigma_m is NOT sigma*n/N_gen, since
> n can be chosen using niter=n in the pythia_card. In fact, sigma
> should only be used to calculate the relative error of the full
> sample.

Ok this is problematic (if you fix niter). And then the error depends
on how pythia calculates the cross-section.
Do you how how this is done?
is it sigma_m = n / n_gen
where this time n_gen is not fixed anymore (i.e. that's the number of
events required to have n accepted events)?

>> This is the link that you look for.
>> https://server06.fynu.ucl.ac.be/projects/madgraph/wiki/NumberOfEvents
>> (all the pages are transfered on this wiki now).
>> But I don't think that this is the same problem.
>
> It should be quite similar. The relative uncertainty must go from
> the MadGraph error (when all events are accepted) to 1/sqrt(N) (when
> few events are accepted). This is exactly what you get for the
> statistics described there. However, my formula is clearly wrong,
> since you get too large error when you ask for a subset of the
> events using the niter flag in the pythia_card. So in fact, both
> tried events and accepted events are needed to calculate the error
> (the total number of events should be the tried events rather than
> Ngen, which is not relevant in the niter < Ngen case).

In fact the real point, is that we don't have a poisson distribution
but a binomial distributions (which is the correct distribution for
sucess/failure experiment):
(from wikipedia: http://en.wikipedia.org/wiki/Binomial_distribution):
 >In probability theory and statistics, the binomial distribution is
the discrete probability distribution of the number of successes in a
sequence of n independent yes/no experiments, each of which yields
 >success with probability p.
and the variance square associate is n * p* (1-p)

and therefore error associate to it is (supposing niter=-1)
error(N_acc) = sqrt(N_gen*(N_acc/N_gen)*(1-N_acc/N_gen))

The binomial can be approximate to a Poisson if p <<1 but not in the
case where p is close to one.

Now the case where niter is not -1 requires a Negative binomial
distribution:
(from wikipedia : http://en.wikipedia.org/wiki/Negative_binomial_distribution)
 >In probability theory and statistics, the negative binomial
distribution is a discrete probability distribution of the number of
successes in a sequence of Bernoulli trials before a specified (non-
random) number >of failures (denoted r) occur.

I would say that the error on N_gen is the same of the number of
"suceesses" (which is here not passing the matching)
in that case the error on N_gen should be
error(N_gen) = sqrt( p * r / (1-p)**2)

i.e.

error(N_gen) = sqrt( (1- N_acc/...

Read more...

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Hello Olivier,

> Ok this is problematic (if you fix niter). And then the error depends
> on how pythia calculates the cross-section.
> Do you how how this is done?
> is it sigma_m = n / n_gen
> where this time n_gen is not fixed anymore (i.e. that's the number of
> events required to have n accepted events)?

That sounds right.

> In fact the real point, is that we don't have a poisson distribution
> but a binomial distributions (which is the correct distribution for
> sucess/failure experiment):
> (from wikipedia: http://en.wikipedia.org/wiki/Binomial_distribution):
> >In probability theory and statistics, the binomial distribution is
> the discrete probability distribution of the number of successes in a
> sequence of n independent yes/no experiments, each of which yields
> >success with probability p.
> and the variance square associate is n * p* (1-p)
>
> and therefore error associate to it is (supposing niter=-1)
> error(N_acc) = sqrt(N_gen*(N_acc/N_gen)*(1-N_acc/N_gen))

Exactly. This is exactly what Rikkert wrote about.

> So in the first case (niter=-1) the error is
>
> error * N_acc/N_gen + sigma * sqrt(N_gen*(N_acc/N_gen)*(1-N_acc/
> N_gen)) / N_gen

Exactly. To write it a bit shorter and easier to read:
error * N_acc/N_gen + sigma_m * sqrt((1-N_acc/N_gen)/N_acc)

> while in the second (niter=nacc) the error is
>
> error * N_acc/N_gen + sigma * N_acc / N_gen**2 * (N_gen*sqrt((1-N_acc/
> N_gen)/N_acc)
> i.e.
> error * N_acc/N_gen + sigma * N_acc / N_gen *sqrt((1-N_acc/N_gen)/N_acc)

I don't think this is completely correct however. Note that N_try (which you call "N_gen" above, if I understand you correctly) is here restricted by the actual number N_gen of events in the event file. This is not reflected in the formula.

I would suggest to simply use the first formula also for the second case. I don't think anybody expects us to have the perfect treatment in this case, and that should give a close enough approximation (it has the same limiting behavior when N_acc/N_try = 1 and N_acc/N_try -> 0).

> (or off course their uncorellated equivalent.)

That's right, the errors should be uncorrelated.

Cheers,
Johan

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :
Download full text (3.8 KiB)

Hi Johan,

>> while in the second (niter=nacc) the error is
>>
>> error * N_acc/N_gen + sigma * N_acc / N_gen**2 * (N_gen*sqrt((1-
>> N_acc/
>> N_gen)/N_acc)
>> i.e.
>> error * N_acc/N_gen + sigma * N_acc / N_gen *sqrt((1-N_acc/N_gen)/
>> N_acc)
>
> I don't think this is completely correct however. Note that N_try
> (which you call "N_gen" above, if I understand you correctly) is
> here restricted by the actual number N_gen of events in the event
> file. This is not reflected in the formula.

This formula is correct if niter=n_acc (i.e. if you reached the
requested number of events) Otherwise the previous formula is the
valid one.

> I would suggest to simply use the first formula also for the second
> case. I don't think anybody expects us to have the perfect treatment
> in this case, and that should give a close enough approximation (it
> has the same limiting behavior when N_acc/N_try = 1 and N_acc/N_try -
> > 0).

> I don't think anybody expects us to have the perfect treatment in
> this case

That's a wrong argument, off course that if we quote a number they
will expect that we give the correct number (always better to say
nothing that saying smtg wrong)
In fact, including it is not that difficult, since the information is
on the same line that we parse. The only point is that we need to
store the value of the matched error, which is fine.
I can do it if you want.

Cheers,

Olivier

On Apr 17, 2012, at 2:19 AM, Johan Alwall wrote:

> Hello Olivier,
>
>> Ok this is problematic (if you fix niter). And then the error depends
>> on how pythia calculates the cross-section.
>> Do you how how this is done?
>> is it sigma_m = n / n_gen
>> where this time n_gen is not fixed anymore (i.e. that's the number of
>> events required to have n accepted events)?
>
> That sounds right.
>
>> In fact the real point, is that we don't have a poisson distribution
>> but a binomial distributions (which is the correct distribution for
>> sucess/failure experiment):
>> (from wikipedia: http://en.wikipedia.org/wiki/
>> Binomial_distribution):
>>> In probability theory and statistics, the binomial distribution is
>> the discrete probability distribution of the number of successes in a
>> sequence of n independent yes/no experiments, each of which yields
>>> success with probability p.
>> and the variance square associate is n * p* (1-p)
>>
>> and therefore error associate to it is (supposing niter=-1)
>> error(N_acc) = sqrt(N_gen*(N_acc/N_gen)*(1-N_acc/N_gen))
>
> Exactly. This is exactly what Rikkert wrote about.
>
>> So in the first case (niter=-1) the error is
>>
>> error * N_acc/N_gen + sigma * sqrt(N_gen*(N_acc/N_gen)*(1-N_acc/
>> N_gen)) / N_gen
>
> Exactly. To write it a bit shorter and easier to read:
> error * N_acc/N_gen + sigma_m * sqrt((1-N_acc/N_gen)/N_acc)
>
>> while in the second (niter=nacc) the error is
>>
>> error * N_acc/N_gen + sigma * N_acc / N_gen**2 * (N_gen*sqrt((1-
>> N_acc/
>> N_gen)/N_acc)
>> i.e.
>> error * N_acc/N_gen + sigma * N_acc / N_gen *sqrt((1-N_acc/N_gen)/
>> N_acc)
>
> I don't think this is completely correct however. Note that N_try
> (which you call "N_gen" above, if I understand you corr...

Read more...

lp:~maddevelopers/mg5amcnlo/1.4.5 updated
218. By mattelaer-olivier

add a warning if the user specify a order larger than the one allowed in couplings_order

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Hello Olivier,

> >> while in the second (niter=nacc) the error is
> >>
> >> error * N_acc/N_gen + sigma * N_acc / N_gen**2 * (N_gen*sqrt((1-
> >> N_acc/
> >> N_gen)/N_acc)
> >> i.e.
> >> error * N_acc/N_gen + sigma * N_acc / N_gen *sqrt((1-N_acc/N_gen)/
> >> N_acc)
> >
> > I don't think this is completely correct however. Note that N_try
> > (which you call "N_gen" above, if I understand you correctly) is
> > here restricted by the actual number N_gen of events in the event
> > file. This is not reflected in the formula.
>
> This formula is correct if niter=n_acc (i.e. if you reached the
> requested number of events) Otherwise the previous formula is the
> valid one.
>
> > I would suggest to simply use the first formula also for the second
> > case. I don't think anybody expects us to have the perfect treatment
> > in this case, and that should give a close enough approximation (it
> > has the same limiting behavior when N_acc/N_try = 1 and N_acc/N_try -
> > > 0).
>
> > I don't think anybody expects us to have the perfect treatment in
> > this case
>
> That's a wrong argument, off course that if we quote a number they
> will expect that we give the correct number (always better to say
> nothing that saying smtg wrong)
> In fact, including it is not that difficult, since the information is
> on the same line that we parse. The only point is that we need to
> store the value of the matched error, which is fine.
> I can do it if you want.

Ok, sure. So, basically: If N_try = N_gen, we use the first formula. If N_try < N_gen, we use the second.

Excellent.

Johan

review: Approve
lp:~maddevelopers/mg5amcnlo/1.4.5 updated
219. By omatt

raise an error if condor_q is unresponsive

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Hi Johan,

> Ok, sure. So, basically: If N_try = N_gen, we use the first formula.
> If N_try < N_gen, we use the second.

Yes but the funny part is that the formula is the same (up to Ntry ->
Ngen):
# error for a Negative binomial distribution:
error_m = sqrt((error * Nacc/Ntry)**2 + sigma_m**2 *(1-Nacc/Ntry)/Nacc)

# error for a binomial distributions:
error_m = sqrt((error * Nacc/Ngen)**2 + sigma_m**2 *(1-Nacc/Ngen)/ Nacc)

even if they are compute via different formula...
So we don't have to split the formula at the end.

This makes me slightly uncomfortable but I think this makes some sense
if you take the case where in
order to have N_acc events you reach the points where Ntry=Ngen.
Having the same formula allows to have a perfectly smooth transition
between the two cases.

Do you think it makes sense?

Cheers,

Olivier

On Apr 18, 2012, at 12:03 AM, Johan Alwall wrote:

> Review: Approve
>
> Hello Olivier,
>
>>>> while in the second (niter=nacc) the error is
>>>>
>>>> error * N_acc/N_gen + sigma * N_acc / N_gen**2 * (N_gen*sqrt((1-
>>>> N_acc/
>>>> N_gen)/N_acc)
>>>> i.e.
>>>> error * N_acc/N_gen + sigma * N_acc / N_gen *sqrt((1-N_acc/N_gen)/
>>>> N_acc)
>>>
>>> I don't think this is completely correct however. Note that N_try
>>> (which you call "N_gen" above, if I understand you correctly) is
>>> here restricted by the actual number N_gen of events in the event
>>> file. This is not reflected in the formula.
>>
>> This formula is correct if niter=n_acc (i.e. if you reached the
>> requested number of events) Otherwise the previous formula is the
>> valid one.
>>
>>> I would suggest to simply use the first formula also for the second
>>> case. I don't think anybody expects us to have the perfect treatment
>>> in this case, and that should give a close enough approximation (it
>>> has the same limiting behavior when N_acc/N_try = 1 and N_acc/
>>> N_try -
>>>> 0).
>>
>>> I don't think anybody expects us to have the perfect treatment in
>>> this case
>>
>> That's a wrong argument, off course that if we quote a number they
>> will expect that we give the correct number (always better to say
>> nothing that saying smtg wrong)
>> In fact, including it is not that difficult, since the information is
>> on the same line that we parse. The only point is that we need to
>> store the value of the matched error, which is fine.
>> I can do it if you want.
>
> Ok, sure. So, basically: If N_try = N_gen, we use the first formula.
> If N_try < N_gen, we use the second.
>
> Excellent.
>
> Johan
> --
> https://code.launchpad.net/~maddevelopers/madgraph5/1.4.5/+merge/
> 101662
> You proposed lp:~maddevelopers/madgraph5/1.4.5 for merging.

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Very good. Since the limits should be the same, in fact I don't see how the formulas could differ. So this is just a confirmation that indeed the statistical frameworks are correct.

All the best,
Johan

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Ok Excellent,

I've just fixed a series of small bug.

And add the fact that multi-run ALWAYS change the seed between the run (so even if the run_card is for a fixed value)

I will release 1.4.5 tomorrow.

Cheers and thanks,

Olivier

review: Approve
lp:~maddevelopers/mg5amcnlo/1.4.5 updated
220. By mattelaer-olivier

change date

221. By mattelaer-olivier

put the correct formula for the pythia error

222. By mattelaer-olivier

call the function quit when MadEvent is call like ./bin/madevent cmd.cmd

223. By mattelaer-olivier

fix an html output problem (when launching generate_events RUN twice)

224. By mattelaer-olivier

make stdout/stderr different for pthia/pgs/Delphes
update the seed between the multi-run processes even if
the run_card has a fixed value.

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Hello Olivier,

I think all your modifs are ok, just remove any debug print statements.
Oh, also please change the following statement in the UpdateNotes (which can be confusing and worrying for a user):
                  OM+JA: Correct the way the events/error are calculated for matched
                      cross-sections.
to:
                  OM+JA: Correct the display of number of events and error for Pythia
                      in the html files.

Cheers,
Johan

lp:~maddevelopers/mg5amcnlo/1.4.5 updated
225. By Olivier Mattelaer

remove pointless print
update UpdateNotes
Fix a compilation problems for MadAnalysis

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Template/Source/genps.inc'
2--- Template/Source/genps.inc 2011-05-29 07:23:58 +0000
3+++ Template/Source/genps.inc 2012-04-20 05:15:24 +0000
4@@ -32,4 +32,4 @@
5 REAL*8 LIMHEL
6 PARAMETER(LIMHEL=1e-6)
7 INTEGER MAXTRIES
8- PARAMETER(MAXTRIES=10)
9+ PARAMETER(MAXTRIES=100)
10
11=== modified file 'Template/bin/internal/run_pythia'
12--- Template/bin/internal/run_pythia 2011-10-18 03:42:31 +0000
13+++ Template/bin/internal/run_pythia 2012-04-20 05:15:24 +0000
14@@ -19,4 +19,5 @@
15 echo " LHAPATH=$pydir/PDFsets" >> ../Cards/pythia_card.dat
16 export PDG_MASS_TBL=$pydir/mass_width_2004.mc
17
18-$pydir/pythia && touch pythia.done
19\ No newline at end of file
20+$pydir/pythia && touch pythia.done
21+
22
23=== modified file 'Template/bin/madevent'
24--- Template/bin/madevent 2012-02-08 05:31:40 +0000
25+++ Template/bin/madevent 2012-04-20 05:15:24 +0000
26@@ -126,11 +126,13 @@
27 cmd_line.debug_output = os.path.join(os.path.dirname(input_file),'generation.log')
28 cmd_line.use_rawinput = False
29 cmd_line.run_cmd('import command ' + input_file)
30+ cmd_line.run_cmd('quit')
31 sys.exit()
32 else:
33 cmd_line = cmd_interface.MadEventCmdShell()
34 cmd_line.use_rawinput = False
35 cmd_line.run_cmd('import command ' + input_file)
36+ cmd_line.run_cmd('quit')
37 sys.exit()
38 else:
39 # Interactive mode
40
41=== modified file 'UpdateNotes.txt'
42--- UpdateNotes.txt 2012-03-29 09:08:22 +0000
43+++ UpdateNotes.txt 2012-04-20 05:15:24 +0000
44@@ -1,7 +1,22 @@
45 Update notes for MadGraph 5 (in reverse time order)
46
47+1.4.5 (11/04/12) OM: Change the seed automatically in multi_run. (Even if the seed
48+ was set to a non automatic value in the card.)
49+ OM: correct a minor bug #975647 (SLAH convention problem)
50+ Thanks to Sho Iwamoto
51+ OM: Improve cluster support (more secure and complete version)
52+ JA: Increased the number of events tested for non-zero helicity
53+ configurations (needed for goldstino processes).
54+ OM: Add a command to remove the file RunWeb which were not always
55+ deleted correctly
56+ OM+JA: Correct the display of number of events and error for Pythia
57+ in the html files.
58+ OM: Changed the way the stdout/stderr are treated on the cluster
59+ since some cluster cann't support to have the same output file
60+ for both. (thanks abhishek)
61+
62 1.4.4 (29/03/12) OM: Added a command: "output aloha" which allows to creates a
63- subset (or all) of the aloha routines linked to the
64+ subset (or all) of the aloha routines linked to the
65 current model
66 OM: allow to choose the duration of the timer for the questions.
67 (via ./input/mg5_configuration.txt)
68
69=== modified file 'madgraph/VERSION'
70--- madgraph/VERSION 2012-03-29 09:08:22 +0000
71+++ madgraph/VERSION 2012-04-20 05:15:24 +0000
72@@ -1,3 +1,3 @@
73-version = 1.4.4
74-date = 2012-03-29
75+version = 1.4.5.beta1
76+date = 2012-04-18
77
78
79=== modified file 'madgraph/core/base_objects.py'
80--- madgraph/core/base_objects.py 2012-03-28 03:42:34 +0000
81+++ madgraph/core/base_objects.py 2012-04-20 05:15:24 +0000
82@@ -1989,7 +1989,11 @@
83 tmp = [(k,v) for (k,v) in expansion_orders.items() if 0 < v < 99]
84 for (k,v) in tmp:
85 if k in orders:
86- orders[k] = min(orders[k], v)
87+ if v < orders[k]:
88+ logger.warning('''The coupling order (%s=%s) specified is larger than the one allowed
89+ by the model builder. The maximal value allowed is %s.
90+ We set the %s order to this value''' % (k,orders[k],v,k))
91+ orders[k] = v
92 else:
93 orders[k] = v
94
95
96=== modified file 'madgraph/interface/madevent_interface.py'
97--- madgraph/interface/madevent_interface.py 2012-03-28 05:07:30 +0000
98+++ madgraph/interface/madevent_interface.py 2012-04-20 05:15:24 +0000
99@@ -108,10 +108,12 @@
100 InvalidCmd = InvalidCmd
101 ConfigurationError = MadGraph5Error
102
103-
104 def __init__(self, *arg, **opt):
105 """Init history and line continuation"""
106
107+ # Tag allowing/forbiding question
108+ self.force = False
109+
110 # If possible, build an info line with current version number
111 # and date, from the VERSION text file
112 info = misc.get_pkg_info()
113@@ -196,7 +198,10 @@
114
115 def postcmd(self, stop, line):
116 """ Update the status of the run for finishing interactive command """
117-
118+
119+ # relaxing the tag forbidding question
120+ self.force = False
121+
122 if not self.use_rawinput:
123 return stop
124
125@@ -247,7 +252,7 @@
126 """If a ME run is currently running add a link in the html output"""
127
128 self.add_error_log_in_html()
129- cmd.Cmd.nice_user_error(self, error, line)
130+ cmd.Cmd.nice_user_error(self, error, line)
131
132 def nice_config_error(self, error, line):
133 """If a ME run is currently running add a link in the html output"""
134@@ -585,11 +590,7 @@
135 def check_generate_events(self, args):
136 """check that the argument for generate_events are valid"""
137
138- force = False
139 run = None
140- if '-f' in args:
141- force = True
142- args.remove('-f')
143 if args and args[-1].startswith('--laststep='):
144 run = args[-1].split('=')[-1]
145 if run not in ['auto','parton', 'pythia', 'pgs', 'delphes']:
146@@ -607,19 +608,16 @@
147 self.help_generate_events()
148 raise self.InvalidCmd('Too many argument for generate_events command: %s' % cmd)
149
150- return force, run
151+ return run
152
153 def check_calculate_decay_widths(self, args):
154 """check that the argument for calculate_decay_widths are valid"""
155
156 if self.ninitial != 1:
157 raise self.InvalidCmd('Can only calculate decay widths for decay processes A > B C ...')
158- force = False
159+
160 accuracy = 0.01
161 run = None
162- if '-f' in args:
163- force = True
164- args.remove('-f')
165 if args and args[-1].startswith('--accuracy='):
166 try:
167 accuracy = float(args[-1].split('=')[-1])
168@@ -630,18 +628,14 @@
169 self.help_calculate_decay_widths()
170 raise self.InvalidCmd('Too many argument for calculate_decay_widths command: %s' % cmd)
171
172- return force, accuracy
173+ return accuracy
174
175
176
177 def check_multi_run(self, args):
178 """check that the argument for survey are valid"""
179
180- force = False
181 run = None
182- if '-f' in args:
183- force = True
184- args.remove('-f')
185
186 if not len(args):
187 self.help_multi_run()
188@@ -669,7 +663,7 @@
189 self.check_survey(args, cmd='multi_run')
190 args.insert(0, int(nb_run))
191
192- return force, run
193+ return run
194
195 def check_refine(self, args):
196 """check that the argument for survey are valid"""
197@@ -747,6 +741,20 @@
198 elif laststep:
199 raise self.InvalidCmd('only one laststep argument is allowed')
200
201+ # If not pythia-pgs path
202+ if not self.options['pythia-pgs_path']:
203+ logger.info('Retry to read configuration file to find pythia-pgs path')
204+ self.set_configuration()
205+
206+ if not self.options['pythia-pgs_path'] or not \
207+ os.path.exists(pjoin(self.options['pythia-pgs_path'],'src')):
208+ error_msg = 'No pythia-pgs path correctly set.'
209+ error_msg += 'Please use the set command to define the path and retry.'
210+ error_msg += 'You can also define it in the configuration file.'
211+ raise self.InvalidCmd(error_msg)
212+
213+
214+
215 tag = [a for a in args if a.startswith('--tag=')]
216 if tag:
217 args.remove(tag[0])
218@@ -775,19 +783,6 @@
219 input_file = pjoin(self.me_dir,'Events',self.run_name, 'unweighted_events.lhe')
220 output_file = pjoin(self.me_dir, 'Events', 'unweighted_events.lhe')
221 os.system('gunzip -c %s > %s' % (input_file, output_file))
222-
223-
224- # If not pythia-pgs path
225- if not self.options['pythia-pgs_path']:
226- logger.info('Retry to read configuration file to find pythia-pgs path')
227- self.set_configuration()
228-
229- if not self.options['pythia-pgs_path'] or not \
230- os.path.exists(pjoin(self.options['pythia-pgs_path'],'src')):
231- error_msg = 'No pythia-pgs path correctly set.'
232- error_msg += 'Please use the set command to define the path and retry.'
233- error_msg += 'You can also define it in the configuration file.'
234- raise self.InvalidCmd(error_msg)
235
236 args.append(mode)
237
238@@ -801,14 +796,6 @@
239 tag = tag[0]
240 tmp_args.remove('--tag=%s' % tag)
241
242- try:
243- tmp_args.remove('-f')
244- except:
245- pass
246- else:
247- if args[0] == '-f':
248- args.pop(0)
249- args.append('-f')
250
251 if len(tmp_args) == 0:
252 self.help_remove()
253@@ -817,7 +804,7 @@
254 return tmp_args[0], tag, ['all']
255 else:
256 for arg in tmp_args[1:]:
257- if arg not in self._clean_mode and arg != '-f':
258+ if arg not in self._clean_mode:
259 self.help_clean()
260 raise self.InvalidCmd('%s is not a valid options for clean command'\
261 % arg)
262@@ -855,10 +842,6 @@
263 args.append('all')
264 return
265
266- force = False
267- if '-f' in args:
268- args.remove('-f')
269- force = True
270
271 if args[0] not in self._plot_mode:
272 self.set_run_name(args[0], level='plot')
273@@ -874,9 +857,6 @@
274 self.help_plot()
275 raise self.InvalidCmd('unknown options %s' % arg)
276
277- if force:
278- args.append('-f')
279-
280
281 def check_pgs(self, arg):
282 """Check the argument for pythia command
283@@ -904,7 +884,7 @@
284
285 if len(arg) == 0 and not self.run_name:
286 if self.results.lastrun:
287- args.insert(0, self.results.lastrun)
288+ arg.insert(0, self.results.lastrun)
289 else:
290 raise self.InvalidCmd('No run name currently define. Please add this information.')
291
292@@ -1402,7 +1382,7 @@
293 elif arg == '-m':
294 self.cluster_mode = 2
295 elif arg == '-f':
296- continue
297+ self.force = True
298 elif not arg.startswith('--'):
299 if error:
300 raise self.InvalidCmd('%s argument cannot start with - symbol' % arg)
301@@ -1419,8 +1399,7 @@
302 self.web = True
303 self.cluster_mode = 1
304 self.results.def_web_mode(True)
305- if not '-f' in args:
306- args.append('-f')
307+ self.force = True
308 else:
309 continue
310 args.remove(arg)
311@@ -1544,11 +1523,7 @@
312
313 args = self.split_arg(line)
314 #check the validity of the arguments
315- self.check_banner_run(args)
316- if '-f' in args:
317- force = True
318- else:
319- force = False
320+ self.check_banner_run(args)
321
322 banner_mod.split_banner(args[0], self.me_dir, proc_card=False)
323
324@@ -1562,13 +1537,13 @@
325
326
327 # Check if we want to modify the run
328- if not force:
329+ if not self.force:
330 ans = self.ask('Do you want to modify the Cards?', 'n', ['y','n'])
331 if ans == 'n':
332- force = True
333+ self.force = True
334
335 # Call Generate events
336- self.exec_cmd('generate_events %s %s' % (self.run_name, force and '-f' or ''))
337+ self.exec_cmd('generate_events %s %s' % (self.run_name, self.force and '-f' or ''))
338
339
340
341@@ -1704,8 +1679,8 @@
342
343 args = self.split_arg(line)
344 # Check argument's validity
345- force, mode = self.check_generate_events(args)
346- self.ask_run_configuration(mode, force)
347+ mode = self.check_generate_events(args)
348+ self.ask_run_configuration(mode)
349 if not args:
350 # No run name assigned -> assigned one automaticaly
351 self.set_run_name(self.find_available_run_name(self.me_dir), None, 'parton')
352@@ -1748,14 +1723,13 @@
353 self.exec_cmd('refine %s' % nb_event, postcmd=False)
354 self.exec_cmd('refine %s' % nb_event, postcmd=False)
355 self.exec_cmd('combine_events', postcmd=False)
356+ self.print_results_in_shell(self.results.current)
357 self.create_plot('parton')
358 self.exec_cmd('store_events', postcmd=False)
359 self.exec_cmd('pythia --no_default', postcmd=False, printcmd=False)
360 # pythia launches pgs/delphes if needed
361 self.store_result()
362
363- self.print_results_in_shell(self.results.current)
364-
365
366 def print_results_in_shell(self, data):
367 """Have a nice results prints in the shell,
368@@ -1767,18 +1741,12 @@
369 else:
370 logger.info(" Cross-section : %.4g +- %.4g pb" % (data['cross'], data['error']))
371 logger.info(" Nb of events : %s" % data['nb_event'] )
372- if data['cross_pythia']:
373- error = data.get_pythia_error(data['cross'], data['error'],
374- data['cross_pythia'], data['nb_event'])
375- nb_event = 0
376- if data['cross']:
377- nb_event = int(0.5+(data['nb_event'] * data['cross_pythia'] /data['cross']))
378-
379+ if data['cross_pythia'] and data['nb_event_pythia']:
380 if self.ninitial == 1:
381- logger.info(" Matched Width : %.4g +- %.4g GeV" % (data['cross_pythia'], error))
382+ logger.info(" Matched Width : %.4g +- %.4g GeV" % (data['cross_pythia'], data['error_pythia']))
383 else:
384- logger.info(" Matched Cross-section : %.4g +- %.4g pb" % (data['cross'], error))
385- logger.info(" Nb of events after Matching : %s" % nb_event)
386+ logger.info(" Matched Cross-section : %.4g +- %.4g pb" % (data['cross_pythia'], data['error_pythia']))
387+ logger.info(" Nb of events after Matching : %s" % data['nb_event_pythia'])
388 logger.info(" " )
389
390
391@@ -1790,8 +1758,8 @@
392
393 args = self.split_arg(line)
394 # Check argument's validity
395- force, accuracy = self.check_calculate_decay_widths(args)
396- self.ask_run_configuration('parton', force)
397+ accuracy = self.check_calculate_decay_widths(args)
398+ self.ask_run_configuration('parton')
399 if not args:
400 # No run name assigned -> assigned one automaticaly
401 self.set_run_name(self.find_available_run_name(self.me_dir))
402@@ -1926,8 +1894,8 @@
403
404 args = self.split_arg(line)
405 # Check argument's validity
406- force, mode = self.check_multi_run(args)
407- self.ask_run_configuration(mode, force)
408+ mode = self.check_multi_run(args)
409+ self.ask_run_configuration(mode)
410 main_name = self.run_name
411 nb_run = args.pop(0)
412 crossoversig = 0
413@@ -1943,8 +1911,12 @@
414 crossoversig+=cross/error**2
415 inv_sq_err+=1.0/error**2
416 self.results[main_name][-1]['cross'] = crossoversig/inv_sq_err
417- self.results[main_name][-1]['error'] = math.sqrt(1.0/inv_sq_err)
418-
419+ self.results[main_name][-1]['error'] = math.sqrt(1.0/inv_sq_err)
420+ if self.run_card['iseed'] != 0:
421+ seed = self.random + 1
422+ text = open(pjoin(self.me_dir, 'Cards','run_card.dat')).read()
423+ (t,n) = re.subn(r'\s\d+\s*= iseed',' %s = iseed' % seed,text)
424+ open(pjoin(self.me_dir, 'Cards','run_card.dat'),'w').write(t)
425
426 self.run_name = main_name
427 self.results.def_current(main_name)
428@@ -2281,24 +2253,18 @@
429
430 # Check argument's validity
431 args = self.split_arg(line)
432- if '-f' in args:
433- force = True
434- args.remove('-f')
435- else:
436- force = False
437 if '--no_default' in args:
438 if not os.path.exists(pjoin(self.me_dir, 'Cards', 'pythia_card.dat')):
439 return
440- force = True
441 no_default = True
442 args.remove('--no_default')
443 else:
444 no_default = False
445
446 self.check_pythia(args)
447- # the args are modify and the last arg is always the mode
448-
449- self.ask_pythia_run_configuration(args[-1], force)
450+ # the args are modify and the last arg is always the mode
451+ if not no_default:
452+ self.ask_pythia_run_configuration(args[-1])
453
454 # Update the banner with the pythia card
455 if not self.banner:
456@@ -2307,15 +2273,6 @@
457 # initialize / remove lhapdf mode
458 self.configure_directory()
459
460- #if not force:
461- # if os.path.exists(pjoin(self.me_dir, 'Events', self.run_name, '%s_pythia.log' % tag)):
462- # question = 'Previous run of pythia detected. Do you want to remove it?'
463- # ans = self.ask(question, 'y', choices=['y','n'], timeout = 20)
464- # if ans == 'n':
465- # return
466-
467- #self.exec_cmd('remove %s pythia -f' % self.run_name)
468-
469 pythia_src = pjoin(self.options['pythia-pgs_path'],'src')
470
471 self.update_status('Running Pythia', 'pythia')
472@@ -2352,12 +2309,36 @@
473 self.to_store.append('pythia')
474
475 # Find the matched cross-section
476- if int(self.run_card['ickkw']):
477- pythia_log = open(pjoin(self.me_dir,'Events', self.run_name, '%s_pythia.log' % tag))
478- cs_info = misc.get_last_line(pythia_log)
479- # line should be of type: Cross section (pb): 1840.20000000006
480- cs_info = cs_info.split(':')[1]
481- self.results.add_detail('cross_pythia', cs_info)
482+ if int(self.run_card['ickkw']):
483+ # read the line from the bottom of the file
484+ pythia_log = misc.BackRead(pjoin(self.me_dir,'Events', self.run_name,
485+ '%s_pythia.log' % tag))
486+ pythiare = re.compile("\s*I\s+0 All included subprocesses\s+I\s+(?P<generated>\d+)\s+(?P<tried>\d+)\s+I\s+(?P<xsec>[\d\.D\-+]+)\s+I")
487+ for line in pythia_log:
488+ info = pythiare.search(line)
489+ if not info:
490+ continue
491+ try:
492+ # Pythia cross section in mb, we want pb
493+ sigma_m = float(info.group('xsec').replace('D','E')) *1e9
494+ Nacc = int(info.group('generated'))
495+ Ntry = int(info.group('tried'))
496+ except ValueError:
497+ # xsec is not float - this should not happen
498+ self.results.add_detail('cross_pythia', 0)
499+ self.results.add_detail('nb_event_pythia', 0)
500+ self.results.add_detail('error_pythia', 0)
501+ else:
502+ self.results.add_detail('cross_pythia', sigma_m)
503+ self.results.add_detail('nb_event_pythia', Nacc)
504+ #compute pythia error
505+ error = self.results[self.run_name].return_tag(self.run_tag)['error']
506+ error_m = math.sqrt((error * Nacc/Ntry)**2 + sigma_m**2 *(1-Nacc/Ntry)/Nacc)
507+ # works both for fixed number of generated events and fixed accepted events
508+ self.results.add_detail('error_pythia', error_m)
509+ break
510+
511+ pythia_log.close()
512
513 pydir = pjoin(self.options['pythia-pgs_path'], 'src')
514 eradir = self.options['exrootanalysis_path']
515@@ -2431,6 +2412,8 @@
516 self.exec_cmd('pgs --no_default', postcmd=False, printcmd=False)
517 if self.options['delphes_path']:
518 self.exec_cmd('delphes --no_default', postcmd=False, printcmd=False)
519+
520+ self.print_results_in_shell(self.results.current)
521
522 def get_available_tag(self):
523 """create automatically a tag"""
524@@ -2510,7 +2493,7 @@
525 to_suppress = [f for f in to_suppress if 'delphes' in f
526 or 'pgs' in f
527 or 'pythia' in f]
528- if '-f' not in args and len(to_suppress):
529+ if not self.force and len(to_suppress):
530 question = 'Do you want to suppress the following files?\n %s' % \
531 '\n '.join(to_suppress)
532 ans = self.ask(question, 'y', choices=['y','n'])
533@@ -2544,7 +2527,7 @@
534 to_suppress += glob.glob(pjoin(self.me_dir, 'SubProcesses', '*','%s*' % run))
535 to_suppress += glob.glob(pjoin(self.me_dir, 'SubProcesses', '*','*','%s*' % run))
536
537- if '-f' in args or len(to_suppress) == 0:
538+ if self.force or len(to_suppress) == 0:
539 ans = 'y'
540 else:
541 question = 'Do you want to suppress the following files?\n %s' % \
542@@ -2685,11 +2668,6 @@
543
544 args = self.split_arg(line)
545 # Check argument's validity
546- if '-f' in args:
547- force = True
548- args.remove('-f')
549- else:
550- force = False
551 if '--no_default' in args:
552 no_default = True
553 args.remove('--no_default')
554@@ -2712,7 +2690,7 @@
555 pjoin(self.me_dir, 'Cards', 'pgs_card.dat'))
556 logger.info('No pgs card found. Take the default one.')
557
558- if not (no_default or force):
559+ if not (no_default or self.force):
560 self.ask_edit_cards(['pgs'], args)
561
562 self.update_status('prepare PGS run', level=None)
563@@ -2807,11 +2785,6 @@
564
565 args = self.split_arg(line)
566 # Check argument's validity
567- if '-f' in args:
568- force = True
569- args.remove('-f')
570- else:
571- force = False
572 if '--no_default' in args:
573 no_default = True
574 args.remove('--no_default')
575@@ -2833,7 +2806,7 @@
576 if not os.path.exists(pjoin(self.me_dir, 'Cards', 'delphes_trigger.dat')):
577 files.cp(pjoin(self.me_dir, 'Cards', 'delphes_trigger_default.dat'),
578 pjoin(self.me_dir, 'Cards', 'delphes_trigger.dat'))
579- if not (no_default or force):
580+ if not (no_default or self.force):
581 self.ask_edit_cards(['delphes', 'trigger'], args)
582
583 self.update_status('Running Delphes', level=None)
584@@ -2984,8 +2957,19 @@
585 self.update_status((idle, run, finish, run_type), level=None)
586 else:
587 update_status = lambda idle, run, finish: None
588- self.cluster.wait(self.me_dir, update_status)
589-
590+ try:
591+ self.cluster.wait(self.me_dir, update_status)
592+ except Exception, error:
593+ logger.info(error)
594+ if not self.force:
595+ ans = self.ask('Cluster Error detected. Do you want to clean the queue?',
596+ default = 'y', answers=['y','n'])
597+ else:
598+ ans = 'y'
599+ if ans:
600+ self.cluster.remove()
601+ raise
602+
603 if mode == 2:
604 # Wait that all thread finish
605 if not self.control_thread[2]:
606@@ -3404,7 +3388,10 @@
607
608
609 if not os.path.exists(event_path):
610- raise self.InvalidCmd, 'Events file %s does not exits' % event_path
611+ if os.path.exists(event_path+'.gz'):
612+ os.system('gzip -f %s.gz ' % event_path)
613+ else:
614+ raise self.InvalidCmd, 'Events file %s does not exits' % event_path
615
616 self.update_status('Creating Plots for %s level' % mode, level = mode.lower())
617
618@@ -3462,7 +3449,7 @@
619
620
621 ############################################################################
622- def ask_run_configuration(self, mode=None, force=False):
623+ def ask_run_configuration(self, mode=None):
624 """Ask the question when launching generate_events/multi_run"""
625
626 available_mode = ['0', '1']
627@@ -3487,7 +3474,7 @@
628 if '4' in available_mode:
629 question += """ 4 / delphes : MadEvent + Pythia + Delphes.\n"""
630
631- if not force:
632+ if not self.force:
633 if not mode:
634 mode = self.ask(question, '0', options)
635 elif not mode:
636@@ -3524,7 +3511,7 @@
637 self.add_card_to_run('trigger')
638 cards.append('delphes_card.dat')
639
640- if force:
641+ if self.force:
642 return
643
644 def get_question(mode):
645@@ -3599,7 +3586,7 @@
646
647
648 ############################################################################
649- def ask_pythia_run_configuration(self, mode=None, force=False):
650+ def ask_pythia_run_configuration(self, mode=None):
651 """Ask the question when launching pythia"""
652
653 available_mode = ['0', '1', '2']
654@@ -3614,7 +3601,7 @@
655 if '3' in available_mode:
656 question += """ 3 / delphes : Pythia + Delphes.\n"""
657
658- if not force:
659+ if not self.force:
660 if not mode:
661 mode = self.ask(question, '0', options)
662 elif not mode:
663@@ -3647,7 +3634,7 @@
664 self.add_card_to_run('trigger')
665 cards.append('delphes_card.dat')
666
667- if force:
668+ if self.force:
669 return mode
670
671 # Ask the user if he wants to edit any of the files
672@@ -3708,7 +3695,7 @@
673 def ask_edit_cards(self, cards, fct_args):
674 """Question for cards editions (used for pgs/delphes)"""
675
676- if '-f' in fct_args or '--no_default' in fct_args:
677+ if self.force or '--no_default' in fct_args:
678 return
679
680 card_name = {'pgs': 'pgs_card.dat',
681
682=== modified file 'madgraph/interface/madgraph_interface.py'
683--- madgraph/interface/madgraph_interface.py 2012-03-28 05:07:30 +0000
684+++ madgraph/interface/madgraph_interface.py 2012-04-20 05:15:24 +0000
685@@ -1896,7 +1896,6 @@
686 # check if a particle is asked more than once
687 if len(request_part) > len(set(request_part)):
688 for p in request_part:
689- print p, request_part.count(p),present_part.count(p)
690 if request_part.count(p) > present_part.count(p):
691 continue
692
693@@ -2820,14 +2819,16 @@
694 path = os.path.join(MG5DIR, 'pythia-pgs', 'src', 'make_opts')
695 text = open(path).read()
696 text = text.replace('FC=g77','FC=gfortran')
697- open(path, 'w').writelines(text)
698+ open(path, 'w').writelines(text)
699 elif compiler == 'gfortran' and args[0] == 'MadAnalysis':
700 path = os.path.join(MG5DIR, 'MadAnalysis', 'makefile')
701 text = open(path).read()
702- text = text.replace('F77 = g77','F77 = gfortran')
703- open(path, 'w').writelines(text)
704- if logger.level <= logging.INFO:
705- misc.call(['make', 'clean'], )
706+ text = text.replace('FC=g77','FC=gfortran')
707+ open(path, 'w').writelines(text)
708+
709+ if logger.level <= logging.INFO:
710+ devnull = open(os.devnull,'w')
711+ misc.call(['make', 'clean'], stdout=devnull, stderr=-2)
712 status = misc.call(['make'], cwd = os.path.join(MG5DIR, name))
713 else:
714 misc.compile(['clean'], mode='', cwd = os.path.join(MG5DIR, name))
715
716=== modified file 'madgraph/iolibs/template_files/matrix_madevent_group_v4.inc'
717--- madgraph/iolibs/template_files/matrix_madevent_group_v4.inc 2011-05-30 17:37:49 +0000
718+++ madgraph/iolibs/template_files/matrix_madevent_group_v4.inc 2012-04-20 05:15:24 +0000
719@@ -117,7 +117,7 @@
720 GOODHEL(I,IMIRROR)=.TRUE.
721 NGOOD(IMIRROR) = NGOOD(IMIRROR) +1
722 IGOOD(NGOOD(IMIRROR),IMIRROR) = I
723- PRINT *,'Added good helicity ',I,TS(I)*NCOMB/ANS
724+ PRINT *,'Added good helicity ',I,TS(I)*NCOMB/ANS,' in event ',NTRY(IMIRROR)
725 ENDIF
726 ENDDO
727 ENDIF
728
729=== modified file 'madgraph/various/cluster.py'
730--- madgraph/various/cluster.py 2012-03-20 19:55:40 +0000
731+++ madgraph/various/cluster.py 2012-04-20 05:15:24 +0000
732@@ -31,7 +31,7 @@
733 class NotImplemented(MadGraph5Error):
734 pass
735
736-def multiple_try(nb_try=5, sleep=1):
737+def multiple_try(nb_try=5, sleep=20):
738
739 def deco_retry(f):
740 def deco_f_retry(*args, **opt):
741@@ -41,7 +41,7 @@
742 except KeyboardInterrupt:
743 raise
744 except:
745- time.sleep(sleep)
746+ time.sleep(sleep * (i+1))
747 raise
748 return deco_f_retry
749 return deco_retry
750@@ -118,6 +118,12 @@
751 def launch_and_wait(self, prog, argument=[], cwd=None, stdout=None,
752 stderr=None, log=None):
753 """launch one job on the cluster and wait for it"""
754+
755+ special_output = False # tag for concatanate the error with the output.
756+ if stderr == -2 and stdout:
757+ #We are suppose to send the output to stdout
758+ special_output = True
759+ stderr = stdout + '.err'
760 id = self.submit(prog, argument, cwd, stdout, stderr, log)
761 while 1:
762 status = self.control_one_job(id)
763@@ -125,7 +131,25 @@
764 time.sleep(20) #security to ensure that the file are really written on the disk
765 break
766 time.sleep(30)
767-
768+
769+ if special_output:
770+ # combine the stdout and the stderr
771+ #wait up to 50 s to see if those files exists
772+ for i in range(5):
773+ if os.path.exists(stdout):
774+ if not os.path.exists(stderr):
775+ time.sleep(5)
776+ if os.path.exists(stderr):
777+ err_text = open(stderr).read()
778+ if not err_text:
779+ return
780+ logger.warning(err_txt)
781+ text = open(stdout).read()
782+ open(stdout,'w').write(text + err_txt)
783+ else:
784+ return
785+ time.sleep(10)
786+
787 def remove(self, *args):
788 """ """
789 logger.warning("""This cluster didn't support job removal,
790@@ -198,7 +222,12 @@
791 def control_one_job(self, id):
792 """ control the status of a single job with it's cluster id """
793 cmd = 'condor_q '+str(id)+" -format \'%-2s \\n\' \'ifThenElse(JobStatus==0,\"U\",ifThenElse(JobStatus==1,\"I\",ifThenElse(JobStatus==2,\"R\",ifThenElse(JobStatus==3,\"X\",ifThenElse(JobStatus==4,\"C\",ifThenElse(JobStatus==5,\"H\",ifThenElse(JobStatus==6,\"E\",string(JobStatus))))))))\'"
794- status = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
795+ status = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE,
796+ stderr=subprocess.PIPE)
797+ if status.returncode:
798+ raise ClusterManagmentError, 'condor_q returns error: %s' % \
799+ status.stderr.read()
800+
801 return status.stdout.readline().strip()
802
803 @check_interupt()
804@@ -210,16 +239,22 @@
805 return 0, 0, 0, 0
806
807 cmd = "condor_q " + ' '.join(self.submitted_ids) + " -format \'%-2s \\n\' \'ifThenElse(JobStatus==0,\"U\",ifThenElse(JobStatus==1,\"I\",ifThenElse(JobStatus==2,\"R\",ifThenElse(JobStatus==3,\"X\",ifThenElse(JobStatus==4,\"C\",ifThenElse(JobStatus==5,\"H\",ifThenElse(JobStatus==6,\"E\",string(JobStatus))))))))\'"
808- status = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
809+ status = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE,
810+ stderr=subprocess.PIPE)
811
812+ if status.returncode:
813+ raise ClusterManagmentError, 'condor_q returns error: %s' % \
814+ status.stderr.read()
815+
816+
817 idle, run, fail = 0, 0, 0
818 for line in status.stdout:
819 status = line.strip()
820- if status == 'I':
821+ if status in ['I','U']:
822 idle += 1
823 elif status == 'R':
824 run += 1
825- else:
826+ elif status != 'C':
827 fail += 1
828
829 return idle, run, self.submitted - (idle+run+fail), fail
830@@ -240,6 +275,7 @@
831 name = 'pbs'
832 idle_tag = ['Q']
833 running_tag = ['T','E','R']
834+ complete_tag = ['C']
835
836 @multiple_try()
837 def submit(self, prog, argument=[], cwd=None, stdout=None, stderr=None, log=None):
838@@ -324,6 +360,8 @@
839 idle += 1
840 elif status in self.running_tag:
841 run += 1
842+ elif status in self.complete_tag:
843+ continue
844 else:
845 fail += 1
846
847
848=== modified file 'madgraph/various/gen_crossxhtml.py'
849--- madgraph/various/gen_crossxhtml.py 2012-03-24 03:06:44 +0000
850+++ madgraph/various/gen_crossxhtml.py 2012-04-20 05:15:24 +0000
851@@ -278,7 +278,8 @@
852
853 def add_detail(self, name, value, run=None, tag=None):
854 """ add information to current run (cross/error/event)"""
855- assert name in ['cross', 'error', 'nb_event', 'cross_pythia']
856+ assert name in ['cross', 'error', 'nb_event', 'cross_pythia',
857+ 'nb_event_pythia','error_pythia']
858
859 if not run and not self.current:
860 return
861@@ -292,6 +293,8 @@
862 run['cross_pythia'] = float(value)
863 elif name == 'nb_event':
864 run[name] = int(value)
865+ elif name == 'nb_event_pythia':
866+ run[name] = int(value)
867 else:
868 run[name] = float(value)
869
870@@ -411,6 +414,13 @@
871 except:
872 self.web = False
873
874+ # check if more than one parton output
875+ parton = [r for r in self if r.parton]
876+ # clean wrong previous run link
877+ if len(parton)>1:
878+ for p in parton[:-1]:
879+ p.parton = []
880+
881 dico = self.info
882 dico['run_span'] = sum([tag.get_nb_line() for tag in self], 1) -1
883 dico['tag_data'] = '\n'.join([tag.get_html(self) for tag in self])
884@@ -465,11 +475,10 @@
885 output['cross'] = self[-2]['cross']
886 output['error'] = self[-2]['error']
887 elif (current.pgs or current.delphes) and not current['nb_event'] and len(self) > 1:
888- if self[-2]['cross_pythia']:
889+ if self[-2]['cross_pythia'] and self[-2]['nb_event_pythia']:
890 output['cross'] = self[-2]['cross_pythia']
891- output['nb_event'] = int(0.5+(self[-2]['nb_event'] * current['cross'] /self[-2]['cross']))
892- output['error'] = current.get_pythia_error(self[-2]['cross'],
893- self[-2]['error'], current['cross'], current['nb_event'])
894+ output['nb_event'] = self[-2]['nb_event_pythia']
895+ output['error'] = self[-2]['error_pythia']
896 else:
897 output['nb_event'] = self[-2]['nb_event']
898 output['cross'] = self[-2]['cross']
899@@ -514,6 +523,7 @@
900 self['nb_event'] = 0
901 self['cross'] = 0
902 self['cross_pythia'] = ''
903+ self['nb_event_pythia'] = 0
904 self['error'] = 0
905 self.parton = []
906 self.pythia = []
907@@ -638,20 +648,7 @@
908
909 return " <a id='%(id)s' href='%(link1)s' onClick=\"check_link('%(link1)s','%(link2)s','%(id)s')\">%(name)s</a>" \
910 % {'link1': link1, 'link2':link2, 'id': id, 'name':name}
911-
912- def get_pythia_error(self, cross, error, pythia_cross, nb_event):
913- """compute the error associate to pythie"""
914- # pythia_cross = cross * n_acc / n_gen
915- # error_pythia = error * n_acc /n_gen + cross * sqrt(n_acc) / n_gen
916
917- if cross and nb_event:
918- n_acc = int(0.5 + pythia_cross / cross * nb_event)
919- error_pythia = error * n_acc / nb_event
920- error_pythia += cross * math.sqrt(n_acc) / nb_event
921- else:
922- error_pythia = 0
923- return error_pythia
924-
925 def get_links(self, level):
926 """ Get the links for a given level"""
927
928@@ -776,9 +773,8 @@
929 elif (self.pgs or self.delphes) and not self['nb_event']:
930 if runresults[-2]['cross_pythia'] and runresults[-2]['cross']:
931 self['cross'] = runresults[-2]['cross_pythia']
932- self['nb_event'] = int(0.5+(runresults[-2]['nb_event'] * self['cross'] /runresults[-2]['cross']))
933- self['error'] = self.get_pythia_error(runresults[-2]['cross'],
934- runresults[-2]['error'], self['cross'], self['nb_event'])
935+ self['error'] = runresults[-2]['error_pythia']
936+ self['nb_event'] = runresults[-2]['nb_event_pythia']
937 else:
938 self['nb_event'] = runresults[-2]['nb_event']
939 self['cross'] = runresults[-2]['cross']
940@@ -807,13 +803,12 @@
941 local_dico['cross_span'] = nb_line -1
942 else:
943 local_dico['cross_span'] = nb_line
944- if self['cross']:
945- local_dico['nb_event'] = int(0.5+(self['nb_event'] * self['cross_pythia'] /self['cross']))
946+ if self['nb_event_pythia']:
947+ local_dico['nb_event'] = self['nb_event_pythia']
948 else:
949 local_dico['nb_event'] = 0
950 local_dico['cross'] = self['cross_pythia']
951- local_dico['err'] = self.get_pythia_error(self['cross'],
952- self['error'],self['cross_pythia'], self['nb_event'])
953+ local_dico['err'] = self['error_pythia']
954 else:
955 local_dico['cross_span'] = nb_line
956 local_dico['cross'] = self['cross']
957@@ -824,16 +819,15 @@
958 template = sub_part_template_parton
959 if self.parton:
960 local_dico['cross_span'] = nb_line - 1
961- if self['cross']:
962- local_dico['nb_event'] = int(0.5+(self['nb_event'] * self['cross_pythia'] /self['cross']))
963+ if self['nb_event_pythia']:
964+ local_dico['nb_event'] = self['nb_event_pythia']
965 else:
966 local_dico['nb_event'] = 0
967 else:
968 local_dico['cross_span'] = nb_line
969 local_dico['nb_event'] = self['nb_event']
970 local_dico['cross'] = self['cross_pythia']
971- local_dico['err'] = self.get_pythia_error(self['cross'],
972- self['error'],self['cross_pythia'], self['nb_event'])
973+ local_dico['err'] = self['error_pythia']
974 else:
975 template = sub_part_template_pgs
976
977
978=== modified file 'madgraph/various/misc.py'
979--- madgraph/various/misc.py 2012-03-20 07:16:18 +0000
980+++ madgraph/various/misc.py 2012-04-20 05:15:24 +0000
981@@ -261,6 +261,7 @@
982 if len(lines) >= to_read or pos == 0:
983 return lines[-to_read:offset and -offset or None]
984 avg_line_length *= 1.3
985+ avg_line_length = int(avg_line_length)
986
987 ################################################################################
988 # LAST LINE FUNCTION
989@@ -270,6 +271,66 @@
990
991 return tail(fsock, 1)[0]
992
993+class BackRead(file):
994+ """read a file returning the lines in reverse order for each call of readline()
995+This actually just reads blocks (4096 bytes by default) of data from the end of
996+the file and returns last line in an internal buffer."""
997+
998+
999+ def readline(self):
1000+ """ readline in a backward way """
1001+
1002+ while len(self.data) == 1 and ((self.blkcount * self.blksize) < self.size):
1003+ self.blkcount = self.blkcount + 1
1004+ line = self.data[0]
1005+ try:
1006+ self.seek(-self.blksize * self.blkcount, 2) # read from end of file
1007+ self.data = (self.read(self.blksize) + line).split('\n')
1008+ except IOError: # can't seek before the beginning of the file
1009+ self.seek(0)
1010+ self.data = string.split(self.read(self.size - (self.blksize * (self.blkcount-1))) + line, '\n')
1011+
1012+ if len(self.data) == 0:
1013+ return ""
1014+
1015+ line = self.data.pop()
1016+ return line + '\n'
1017+
1018+ def __init__(self, filepos, blksize=4096):
1019+ """initialize the internal structures"""
1020+
1021+ # get the file size
1022+ self.size = os.stat(filepos)[6]
1023+ # how big of a block to read from the file...
1024+ self.blksize = blksize
1025+ # how many blocks we've read
1026+ self.blkcount = 1
1027+ file.__init__(self, filepos, 'rb')
1028+ # if the file is smaller than the blocksize, read a block,
1029+ # otherwise, read the whole thing...
1030+ if self.size > self.blksize:
1031+ self.seek(-self.blksize * self.blkcount, 2) # read from end of file
1032+ self.data = self.read(self.blksize).split('\n')
1033+ # strip the last item if it's empty... a byproduct of the last line having
1034+ # a newline at the end of it
1035+ if not self.data[-1]:
1036+ self.data.pop()
1037+
1038+ def next(self):
1039+ line = self.readline()
1040+ if line:
1041+ return line
1042+ else:
1043+ raise StopIteration
1044+
1045+ def close(self):
1046+ """ close correctly file """
1047+ try:
1048+ self.close()
1049+ except:
1050+ pass
1051+
1052+
1053
1054
1055 #
1056
1057=== modified file 'madgraph/various/sum_html.py'
1058--- madgraph/various/sum_html.py 2012-02-27 22:22:51 +0000
1059+++ madgraph/various/sum_html.py 2012-04-20 05:15:24 +0000
1060@@ -20,7 +20,10 @@
1061 logger = logging.getLogger('madevent.stdout') # -> stdout
1062
1063 pjoin = os.path.join
1064-
1065+try:
1066+ import madgraph.various.cluster as cluster
1067+except:
1068+ import internal.cluster as cluster
1069
1070 class OneResult(object):
1071
1072@@ -41,6 +44,7 @@
1073 self.yerr_iter = []
1074 return
1075
1076+ @cluster.multiple_try(nb_try=4,sleep=5)
1077 def read_results(self, filepath):
1078 """read results.dat and fullfill information"""
1079
1080
1081=== modified file 'models/mssm/parameters.py'
1082--- models/mssm/parameters.py 2011-03-30 05:36:25 +0000
1083+++ models/mssm/parameters.py 2012-04-20 05:15:24 +0000
1084@@ -215,7 +215,7 @@
1085 value = 32337.4943,
1086 texname = 'm_{H_u}^2',
1087 lhablock = 'MSOFT',
1088- lhacode = [ 21 ])
1089+ lhacode = [ 22 ])
1090
1091 mHd2 = Parameter(name = 'mHd2',
1092 nature = 'external',
1093@@ -223,7 +223,7 @@
1094 value = -128800.134,
1095 texname = 'm_{H_d}^2',
1096 lhablock = 'MSOFT',
1097- lhacode = [ 22 ])
1098+ lhacode = [ 21 ])
1099
1100 RmQ211 = Parameter(name = 'RmQ211',
1101 nature = 'external',
1102
1103=== modified file 'tests/unit_tests/iolibs/test_export_v4.py'
1104--- tests/unit_tests/iolibs/test_export_v4.py 2012-03-28 04:08:18 +0000
1105+++ tests/unit_tests/iolibs/test_export_v4.py 2012-04-20 05:15:24 +0000
1106@@ -899,6 +899,7 @@
1107 NGOOD(IMIRROR) = NGOOD(IMIRROR) +1
1108 IGOOD(NGOOD(IMIRROR),IMIRROR) = I
1109 PRINT *,'Added good helicity ',I,TS(I)*NCOMB/ANS
1110+ $ ,' in event ',NTRY(IMIRROR)
1111 ENDIF
1112 ENDDO
1113 ENDIF

Subscribers

People subscribed via source and target branches