Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom photon fields / SOPHIA update #220

Open
wants to merge 1,387 commits into
base: master
Choose a base branch
from

Conversation

MHoerbe
Copy link
Contributor

@MHoerbe MHoerbe commented Jan 29, 2019

Dear all,

with this pull request I want to provide the first of three parts that I wish to contribute to CRPropa. This approach follows the comments made in #218

Content:

This extension is meant to introduce variable photon fields to CRPropa. Previously, there has only been the CMB and several IRB models. This photon field extension basically follows three concepts:

The photon field may

  1. have any shape, i.e. any dependence of photon energy and field density
  2. have any spacial and temporal behaviour (which is part of a future pull request)
  3. be modified even after CRPropa has been installed

Changelog (most severe changes):

~/src/PhotonBackground.cpp
~/include/PhotonBackground.h

There are now eight new slots PF1..PF8 that may be included in the simulation. A class has been added to draw a random photon from the photon field that takes the primary, the nucleon/gamma crossection and the photon field's shape into account. This method and all those that it depends on have been inspired by SOPHIA.

Photon fields are now contained in photon field files in ~/share/crpropa/Scaling/ . For detailed information on the format and use of these fields, there is a Wiki page in my forked repository.

~/libs/sophia/sophia_interface.f

The main point of these changes to SOPHIA is to run the code on other photon fields than the CMB and IRB Kneiske (as only these two are implemented so far in SOPHIA). SOPHIA samples photons from these fields with its own photon sampling routine which in this version has been removed and replaced by the sampling method mentioned in the section above. For this, the input and output parameters of SOPHIA changed from

sophiaevent(nature, Ein, OutPart, OutPartType, NbOutPart, z_particle, bgFlag, Zmax_IRB, idatamax, en_data, flux_data)

to

sophiaevent(nature, Ein, OutPart, OutPartType, NbOut, eps).

Furthermore, SOPHIA has recieved:

  • a removal of all parts of the program that are never used under any circumstances
  • a removal of all GOTO pointers which have been replaced by safer control structures
  • a unified indentation to ehance the readability of the code
  • a re-naming of several variables and methods from cryptic abbreviations to full identifiers
  • docstrings in most functions that as far as tracable describe their functions

~/src/module/PhotonPionProduction.cpp
~/include/module/PhotonPionProduction.h

The flag "haveRedshiftDependence" and all its associated functions have been removed as now all photon fields are represented on grids that are redshift-dependent. The signature of SOPHIA has been updated. The treatment of secondary particles being produced in SOPHIA has been rewritten in a more intuitively readable way. Also, this particle treatment did not allow for more than one hadron to be added among all secondaries produced by SOPHIA. This is valid for the CMB and IRB models as not eanough energy in the center of mass system would be present to produce nucleon/anti-nucleon pairs. However, with more energetic photon fields, these particles may occur. I would like to stress that no kind of anti-nucleon can be produced in the current implementation, even if it were produced.

Tests

As photon sampling is removed from SOPHIA and now provided within CRPropa, SOPHIA's sampling routines and its CRPropa counterparts are expected to produce similar results. Therefore, the following tests have been run according to their hierarchy in the procedure of sampling photons:

Hierarchy of sampling functions (highest to lowest):

sample_eps -> prob_eps -> get_photonDensity, gaussInt -> functs -> crossection

Reference simulation setup:

SimplePropagation(10 kpc, 100 kpc)
PhotoPionProduction(field=CMB, neutrinos=True)
Source @ (100 Mpc, 0, 0)										
Injected: 100.000 monoenergetic protons with 1e12 GeV
  1. crossection is the deepest layer to be tested. Note that the SOPHIA function that returns the nucleon-gamma xsection does not depend on the nucleon's energy itself but rather on the fact whether or not it deals with a proton or a neutron.
    The reference simulation called the crossection function roughly 500.000 times. The sampled photon's energies have been injected into the newly written C++ version of crossection producing another set of ca 500.000 crossection values. These two samples have been inserted into the scipy.stats.ks_2samp Kolmogorov-Smirnov test. A result of D=0.0 and p=1.0 corresponds to total identity in this implementation of the Kolmogorov-Smirnov test. The test achieved a result of D=0.0011, p=0.9183. Note that the functions Ef, Pl and breitwigner are only used by crossection.

  2. In an analogous way, the function gaussInt has been tested, which integrates the function functs over all center-of-mass energies. the function functs is only called by gaussint, whereas functs is the only function calling crossection. The KS test yielded a result of D=9.61e-5, p=0.9999999999 between the SOPHIA output and the C++ version.

  3. prob_eps calculates the non-normalized probability to encounter a photon of the given photon field. Normalization is achieved by dividing the integral of prob_eps over its entire photon energy range. prob_eps is the only function calling gaussInt and get_photonDensity. For better comparison, get_photonDensity has been replaced by an analytical expression returning the photon density of the CMB, i.e. a blackbody with a temprature of 2.73 K. The results of SOPHIA vs C++ yield D=9.61e-5, p=0.9999999999. Results on grid data (and runtimes) are discussed in a later section.

  4. Finally, sample_eps calls prob_eps to sample a photon. This happens in two steps: a) calculate the normalization and the photon energy with which one most likely gets an interaction. b) monte-carlo sample a photon from the field, use norm factor and max probability for this. For comparison, the SOPHIA output has been tested vs the C++ analytical expression for the photon density and vs the grid readout. The KS test yields:

SOPHIA vs C++ (analytic):     D=0.0224, p=0.0114
SOPHIA vs C++ (grid):         D=0.0225, p=0.0110
C++ (analytic) vs C++ (grid): D=0.0001, p=0.9999

Insights: the grid version and the analytic version are able to produce nearly identical outputs, however less similarity is achieved compared to SOPHIA. In return, SOPHIA operates a dedicated sampling routine that only works for the CMB this making several assumptions about the shape of the field in beforehand. The C++ version do not require any information that is not encoded in the photon field file. One might be interested in the fact that SOPHIA uses its own random number generator whereas the C++ routines have been run with std::srand().

  1. Totally finally, the sampling method is implemented into CRPropa and tested on observables. For this, the neutrino output (energies) of the reference simulation has been chosen. The KS test result of a neutrino the neutrino outputs acquired by SOPHIA vs C++ sampling have been tested in three ways fore comparison: a) using no grid i.e. the analytical density readout b) density readout of a CMB grid with 101 points of resolution c) density readout of a CMB grid with 1001 points of resolution
SOPHIA vs C++ (analytic):  1:57 | 2:20 @ D=0.0035, p=0.3514
SOPHIA vs C++ (Grid_101):  1:57 | 2:20 @ D=0.0037, p=0.2716
SOPHIA vs C++ (Grid_1001): 1:57 | 5:58 @ D=0.0035, p=0.3514

Insights: The C++ small grid / analytic versions perform roughly 20% slower than SOPHIA. A larger grid may reach the analytic version at the expense of runtime. All C++ versions perform similarly but do not reach identity with the SOPHIA version. The two points coming to my mind are:
a) the C++ version of sampling has been done on CRPropa's random number generator, SOPHIA uses its own one
b) SOPHIA operates a dedicated sampling method that only works on the CMB (yet another one with IRB_Kneiske) making assumptions about the CMB shape in beforehand. The C++ version does not rely on such information.

Important notes:

  1. all the tests have been run on the CMB as this allows for tests against analytical expressions of the photon field and SOPHIA. The other only photon field implemented in SOPHIA is IRB_Kneiske. First tests did not yield a correlation between SOPHIA and C++. Before going deeper here, I would like to invoke your feedback on the current implementation.
  2. The code does not tell CRPropa to build the photon field files stored in ~/share/crpropa/Scaling upon installation. Also, the tabulated files of the PhotoPionProduction have changed. Do you have any advice in how to include this?
  3. The photon field URB_Protheroe96 could not be converted into a photon field file as there is no sufficient photon field data in the CRPropa-data repository for this. Hence, this field is currently unavailable.

Thanks in advance and best wishes,
Mario

adundovi and others added 30 commits December 12, 2017 10:30
…ed from reference to pointer due to problems with swig.
… is an analytical solution for small distances from the symmetry axis (MagneticBottle) . Also added a magnetic field in which charged particles perform a gyration motion in a small and a big scale (GyroField). The right comment to these two classes is pending.
@MHoerbe
Copy link
Contributor Author

MHoerbe commented Mar 22, 2019

Now that step 0 is taken, there is the agreement to introduce a second version of SOPHIA that includes my modifications in parallel to its original version. This version is agreed to be toggled by some boolean switch in the PhotoPionProduction, defaulting to original SOPHIA. Yet I am having trouble to actually link this second version of SOPHIA to CRPropa and after going through all sorts of attempts I am asking for a few thoughts of yours.

Objective:
Create a parallel instance of SOPHIA, called SOPHIAMOD. Have it imported and used in the PhotoPionProduction.

State of progress
I can establish that second version of SOPIHA and also import it in the PhotoPionProduction. However, I can not use it.

How SOPHIA is implemented / linked
The version of CRPropa as listed in the master branch of the repository should contain all information necessary for the programme to install and link SOPHIA properly. Mentions of SOPHIA occur in the following places as retrieved by `grep -ri "sophia" (comments in which SOPHIA is mentioned are cut out here):

(main CMakeLists)
CRPropa3/CMakeLists.txt:# SOPHIA (provided)
CRPropa3/CMakeLists.txt:add_subdirectory(libs/sophia)
CRPropa3/CMakeLists.txt:list(APPEND CRPROPA_EXTRA_LIBRARIES sophia gfortran)
CRPropa3/CMakeLists.txt:list(APPEND CRPROPA_EXTRA_INCLUDES libs/sophia)

(sophia.h)
CRPropa3/libs/sophia/sophia.h:#ifndef _SOPHIA_H
CRPropa3/libs/sophia/sophia.h:#define SOPHIA_H
CRPropa3/libs/sophia/sophia.h:void sophiaevent
(int& channel, double& inputenergy, double momentum[][2000],

(sophia_interface.f)
CRPropa3/libs/sophia/sophia_interface.f: subroutine sophiaevent(nature,Ein,OutPart,OutPartType,NbOut

(SOPHIA CMakeLists)
CRPropa3/libs/sophia/CMakeLists.txt:add_library(sophia STATIC
CRPropa3/libs/sophia/CMakeLists.txt: sophia_interface.f
CRPropa3/libs/sophia/CMakeLists.txt:set_target_properties(sophia PROPERTIES COMPILE_FLAGS -fPIC)

(import / use in PPP)
CRPropa3/src/module/PhotoPionProduction.cpp:#include "sophia.h"
CRPropa3/src/module/PhotoPionProduction.cpp: sophiaevent_(nature, Ein, momentaList, particleList, nParticles, z, background, maxRedshift, dummy1, dummy2, dummy2);

in all these files I have implemented additional entries of the same syntax yet mentioning an object called SOPHIAMOD instead, including a new directory called sophiaMod in the /libs folder. I added an import in the PPP #include "sophiaMod.h" and up to this point the code compiles flawlessly.

Problem
If I want to call the modified version of SOPHIA it returns "undefined reference to sophiaeventMod_" during compilation (yet this function exists at that point).

Do you have any educated guesses what the solution could be? Otherwise I would be happy to provide a more detailed description of this problem. There is a branch in my repository that currently has these settings. Also, the PPP has got a version of sophiaeventMod (toggled out) that if enabled would produce the error described.

@adundovi
Copy link
Member

Give me one day - I'll write a CMake option for it from which we would be able to switch between sophia and sophiamod.

@adundovi
Copy link
Member

adundovi commented Apr 1, 2019

Sorry for the delay.

I somehow repeated the procedure you described and it worked for me.

Or to be more specific:

  1. copy libs/sophia to libs/sophiamod
  2. rename sophia.h to sophiamod.h in libs/sophiamod
  3. rename sophia to sophiamod in CMakeLists.txt located in libs/sophiamod
  4. copy PhotoPionProduction.h to PhotoPionProductionMod.h and PhotoPionProduction.cpp to PhotoPionProductionMod.cpp
  5. sed -i "s/PhotoPionProduction/PhotoPionProductionMod/g" and "s/PHOTOPIONPRODUCTION/PHOTOPIONPRODUCTIONMOD/g" in two of those
  6. change #include "sophia.h" to #include "sophiamod.h" in PhotoPionProductionMod.cpp
  7. add #include "crpropa/module/PhotoPionProductionMod.h" in include/CRPropa.h
  8. in python/2_headers.i add %include "crpropa/module/PhotoPionProductionMod.h"
  9. edit the main CMakeLists.txt by adding src/module/PhotoPionProductionMod.cpp in the add_library(crpropa ... list and insert:
# SOPHIAmod (provided)
add_subdirectory(libs/sophiamod)
list(APPEND CRPROPA_EXTRA_LIBRARIES sophiamod gfortran)
list(APPEND CRPROPA_EXTRA_INCLUDES libs/sophiamod)

Here you can see the diff in my branch.

I can import and use both modules in python in parallel after doing this.

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Apr 2, 2019

Thanks a real lot to @adundovi !

A branch of mine now contains a modified version of SOPHIA following the suggestions of @TobiasWinchen where only unused parts of the code have been removed. No indentation / other style specifications / goto removal or new functions and the like have been realized.

This is to tackle step 1 of the agreed roadmap. It should be shown that nothing changed to SOPHIA by removing unused parts of the code.

This modified version can be used via a new temporary module called PhotoPionProcuctionMod that is entirely identical to the regular PPP except that it uses SOPHIAmod.

At this point, how shall we proceed? I could create a pull request with this modification or conduct further tests on the scientific identity of the two versions.

So far I have conducted Kolmogorov-Smirnov tests on each of SOPHIAs output particles between two samples 1) drawn from the original SOPHIA in the master branch (for comparison of the KS parameters) and 2) drawn from master and SOPHIAmod. Results suggest identity between all of the produced secondary output particles, i.e. photons, electrons and neutrinos (hadrons not yet tested).

I am happy to share any setup or tests I have made. Any opinions?

@adundovi
Copy link
Member

adundovi commented Apr 2, 2019

Tests are certainly welcome if not necessary. I suggest that you write them in the form of unit tests and place them in test/testPhotoPionProduction.{py.cpp} for instance. Test any aspects of PhotoPionProduction that you can think of being relevant for CRPropa usage but which are not time-consuming (not running longer than ~10s).

It would be great if we could control the SOPHIA random number generator, i.e. to fix its seed so that both modules (the old and the new) produce deterministic results for given inputs. In that way, we would avoid time and resource consuming unit tests which are based on statistical comparisons. Moreover, if you have some theoretical estimates which should be easy to reproduce with SOPHIA, they could be included, too. Both modules should pass all these tests.

After ending the basic testing step, we should proceed by including your PPPmod in the master repo following manually testing in different "large-scale scenarios". If everything goes well we should make PPPmod as the default PPP module, while the current one label as Legacy and after some time to put a deprecated notice and remove it from the source.

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Apr 8, 2019

There is progress with two aspects:

  1. a) The RNG of SOPHIA is always deterministic as it will always use the same seed regardless of any setup in the PhotoPionProduction. This means, that if you called sophiaevent_(...) in lets say two separate instances of CRPropa, e.g. in two ipython terminal windows, the result of sophiaevent_(...) would always be exactly the same. Therefore, there can be conducted deterministic unit tests (which I will come up with eventually).
    b) For this to test, I have implemented an external interface method for SOPHIA as a member of the PhotoPionProduciton in my current working branch. This method can be used for debugging and testing in unit tests as well. It returns a vector of the output particles' IDs (first half of vector) and energies (second half of vector).

  2. I implemented your @adundovi suggested setup to have PhotoPionProductionMod include sophiaMod. However, although everything compiles just fine, there is a subtle yet critical bug as it seems that the different sophiaevent methods aren't yet properly linked. It seems like now there is always the modified version being called regardless of whether PhotoPionProduction or PhotoPionProductionMod call their versions of SOPHIA.
    For this to verify I have pushed your suggested setup with two almost identical versions of SOPHIA with the difference that sophiaMod would print "mod" to the console. This is the code I have run in an ipython shell and should show that PhotoPionProduction actually calls sophiaMod:

In [1]: from crpropa import *
In [2]: ppp = PhotoPionProduction()
In [3]: out = ppp.sophiaEvent(True, 1e12*GeV)
 mod
In [4]: for x in out:
   ...:   print(x)
   ...:
1000000010.0
14.0
12.0
-11.0
-14.0
148.03603804212008
3.242941249811455
2.3102363906818386
1.075986525593002
5.552446491793585

Note that you should get exactly the same output as SOPHIAs RNG always starts with the same seed. I observe that if you renamed sophiaevent_ to sophiaeventMod_ in libs/sophiaMod/sophiaMod.h, libs/sophiaMod/sophiaMod_interface.f and include/crpropa/module/PhotoPionProductionMod.cpp, there will be an error upon compilation stating "libcrpropa.so: undefined reference to sophiaeventMod_". Do you have any thoughts on this?

@lukasmerten
Copy link
Member

a) The RNG of SOPHIA is always deterministic as it will always use the same seed regardless of any setup in the PhotoPionProduction.

That might be problematic, as one usually assumes that the events are independent of each other. With this bug, to my understanding combining the results of two independent CRPropa runs does not reduce the MonteCarlo error for the combined results.

Although this helps us now to test the code we should definetly change that behavior in the future, i.e. using the random seed from the CRPropa Random class also in the SOPHIA RNG or replace the SOPHIA RNG by the Random-class.

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Apr 21, 2019

So far, following your @adundovi approach, I can tell that the compiler randomly links to either sophia or sophiaMod. Hence, PhotoPionProduction and PhotoPionProductionMod both use the same version of either sophia or sophiaMod and there is no separate access.

@antoniusf
Copy link
Member

Re: the linker problems

I've only taken a brief look at this, so feel free to disregard what I'm saying if you've already thought these points through.

As far as I can tell, the functions that sophia and sophiamod define have the same names. Is this correct? If it is, then I'm pretty sure that's a problem.

This is because the linker only resolves functions by their name; it has no information on where a function is meant to come from. (Consider that #include literally just copy-pastes the header file into the source. After that, no part of the compiler cares where the header came from.) So if a function has the same name in both sophia and sophiamod the compiler just won't know which function you mean... does this explanation make sense?

(Even so, it is a little bit confusing that the compiler didn't report this as an error, but it seems problematic nonetheless.)

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Apr 25, 2019

Thanks a lot @antoniusf for looking into this. I indeed have considered this and e.g. renaming "sophiaevent_" to "somethingElse" would result in a compilation error saying that functions (all SOPHIA ones) would be defined in different places, i.e. in /sophia/... and sophiaMod/.... I think we need a way here to tell the framework that there are supposed to be two different libs. Any idea?

@antoniusf
Copy link
Member

antoniusf commented Apr 25, 2019

would result in a compilation error saying that functions (all SOPHIA ones) would be defined in different places

This is the error I would have expected to occur with unchanged function names... Did you change all names or just a few ones? (To get rid of this error, you would definitely have to change all of them, or at least all that are exported... which are quite a few, it appears??)

In any case, I think that renaming all of the exported functions (and constants) is probably the most technically simple option. Unfortunately, this appears to be quite a bit of work, so here's some other possibilities I can think of right now:

– I believe that CRPropa currently uses static linking for sophia. In principle, it might be possible to split up the static linking process such that PhotoPionProduction/Mod are linked first against their respective versions of sophia (such that all function names are resolved), and only then linked together with the rest of CRPropa. However, I do not know how to make gcc or cmake do this – maybe you could build PhotoPionProduction in a separate folder, with its own CMakeLists, and then integrate that somehow?

– The other option might be to manually load the correct version of sophia at runtime. While this would mean that you don't have to muck around with gcc, it is not platform-independent and requires you to write extra code to load each function. (see here for what this would look like)

Edit: platform independence may not even be that big of a deal, since most people probably run on linux?? And you could set it up such that the platform-specific code is only required if you want to test the new sophia.

@antoniusf
Copy link
Member

Update on renaming the functions: Since only the sophiaevent_ function appears to be needed by external code, it might be possible to set up the library to export only this symbol.

Currently, libsophia.a contains quite a lot of symbols, so that's probably where the problems come from. I've done a brief search to see if hiding these symbols is possible; it appears that C supports this through its static keyword, but I haven't been able to find anything comparable for Fortran.

However, before proceeding with any of the approaches above, it might be prudent to re-evaluate if the capability to switch between sophia and sophiaMod at run-time (as opposed to compile-time) is worth the extra effort and added complexity. Almost every approach I can think of either complicates the build process (step-by-step linking), causes issues with platform independence (dynamic linking, symbol hiding for static libraries), or introduces the possibility for (additional) bugs in the sophia code (renaming all exported symbols). This is in addition to the time necessary to make each of the approaches work, which shouldn't be neglected either.

@adundovi
Copy link
Member

Sorry for the long absence on this topic. I'm looking into it.

@adundovi
Copy link
Member

adundovi commented Jul 4, 2019

Hi all!
You are absolutely correct, I've encountered all the problems mentioned above. I'm sorry for not testing my "split" proposal before proposing it. Since then, I've been thinking about how to avoid both the problem with linking and the problem with unnecessary code duplication. Now I suggest the following:

  1. split PPP into two classes as in the first proposal, so that the following example works:
from crpropa import *

ppp = PhotoPionProduction()
c = Candidate()
c.current.setId(nucleusId(1, 1))
c.current.setEnergy(100 * EeV)
c.setCurrentStep(1000 * Mpc)
ppp.process(c)

pppmod = PhotoPionProductionMod()
c = Candidate()
c.current.setId(nucleusId(1, 1))
c.current.setEnergy(100 * EeV)
c.setCurrentStep(1000 * Mpc)
pppmod.process(c)
  1. Make modifications within the sophia library in the form of a new inteface function (which includes the mod version of sophia):
extern "C" {
void sophiaevent_(int& channel, double& inputenergy, double momentum[][2000],
                int id[], int& n, double& redshift, int& photonbackground, double& maxz,
                int&, double[], double[]);
}

extern "C" {
void sophiaeventmod_(int& channel, double& inputenergy, double momentum[][2000],
                int id[], int& n, double& redshift, int& photonbackground, double& maxz,
                int&, double[], double[]);
}
  1. You shouldn't edit sophia_interface.f, but create a new file, for example, sophia_interface_mod.f, which include functions and routines from sophia_include.f (with the "include" command), and then either use header guards, or replace "sophia_interface.f" with "sophia_interface_mod.f" in CMakeLists.txt file in the sophia directory.

I tested this idea by putting different print output in sophiaevent_ and sophiaeventmod_, and the above python script gives:

$ python testppp.py
 sophiaevent: normal version
 sophiaevent: normal version
 sophiaevent: normal version
 sophiaevent: normal version
 sophiaevent: mod version
 sophiaevent: mod version
 sophiaevent: mod version
 sophiaevent: mod version
 sophiaevent: mod version
 sophiaevent: mod version

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Jul 9, 2019

Thanks a lot @adundovi for the proposal! Could you push a working example of what you propose? That would help me a lot!

@adundovi
Copy link
Member

@MHoerbe MHoerbe mentioned this pull request Jul 17, 2019
@MHoerbe
Copy link
Contributor Author

MHoerbe commented Jul 17, 2019

Thanks a lot @adundovi for pushing your example. Based on your changes, I am able / intend to continue with step 1 of the agreed road map above which is to clear up SOPHIA to introduce custom photon fields into CRPropa.

This sub-step is to demonstrate that, despite having an internal RNG:

  • SOPHIAs event generator is strictly deterministic in its arguments
  • SOPHIAs internal photon sampling is just as deterministic in its arguments
    This step is only supposed to point out aspects of SOPHIAs behaviour, thus not proposing actual changes to be merged into the master branch (for now).

Once we agree on this step, further changes to SOPHIA are much easier to validate as we would not have to rely on statistical tests as @TobiasWinchen has pointed out.

The modifications can be found in this branch of my repository.
<Edit: for the sake of completion, this setup can be found under this commit>

This modification adds the following features:

  1. the PhotoPionProduction has been given the ability to separately call SOPHIA without to propagate a candidate via a new class method
    std::vector<double> PhotoPionProduction::sophiaEvent(bool onProton, double Ein, double z=0.)
    which returns a vector containing SOPHIAs output particle IDs (PDG convention, in first half of the vector) and energies (SI, in second half of the vector).
  2. The SOPHIA code itself has not been changed, except that the photon energy (in eV) that has been internally sampled, is printed.

How non-randomness can be shown:

  • in an ipython terminal run
In [1]:  from crpropa import *
In [2]:  ppp = PhotoPionProduction()
In [3]:  for x in ppp.sophiaEvent(True, 1e11 * GeV):
   ...:      print(x)

The output should be:

eps = 1.4145363205544225E-003
1000000010.0
14.0
12.0
-11.0
-14.0
12.135608149326126
0.11742056103982144
0.4679922135766474
3.0552110944588775
0.2455328515985278

  • repeat this in a new console to get the same output.
  • this works for every parameter constellation

Can you verify this behaviour?

If so, I suggest cleaning out unused parts of the SOPHIA code to make it way more readable and show that we will get the very same output after this. Then, we could move on to manipulating physics inside SOPHIA.

@adundovi
Copy link
Member

@Froehliche-Kernschmelze OK, I can confirm the behaviour with your git branch:

 eps =    1.4145363205544225E-003
1000000010.0
14.0
12.0
-11.0
-14.0
12.135608149326126
0.11742056103982144
0.4679922135766474
3.0552110944588775
0.2455328515985278

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Jul 19, 2019

Thanks for your confirmation. Now I suggest to either (or both!)

  1. optional: (since we are at this point anyway) Clean out code in SOPHIA not used within CRPropa and proof it has not changed anything, i.e. reproducing the above mentioned output
  2. Remove SOPHIAs internal photon sampling in sophiamod, thus leading to a signature where the photon energy eps now is an external parameter (functions providing eps follow in a next step)
    sophiaeventmod(bool onProton, double Ein, double eps, outputEnergy, outPartID, nOutPart)

Which is the most viable way for you?

@adundovi
Copy link
Member

If I understand you correctly, the first point is about cleaning/refactoring the FORTRAN code, and the second one is about to introduce new features / change the interface, right?

As far as I'm concerned, you can do whatever you want and what you find necessary in the modified PPP as long as we have both versions of PPP available in parallel (the new and the original). However, to ease testing of the modified/new version (when you go beyond cleaning the code, and start to introduce new features/change the interface), you should provide an additional interface/wrapper which, by construction, would use the same usage logic, and produce identical output as the old procedure. If this approach is possible, of course. If not, then we have to do the 1st and the 2nd point separately, in which you will have to wait with the inclusion of the 2nd until the 1st is well tested.

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Jul 19, 2019

Exactly, the first would introduce changes to the original SOPHIA code by cutting away code that never gets used by CRPropa. This procedure may be lengthy time-wise but worth the review effort as this would reduce the code by approximately a factor of 2, i.e. ~8k lines of code. However, this step can still be done after having introduced the parallel version of SOPHIA.

Given that time is the scarcer resource, I would directly go to modifying the newly added interface for sophiamod (leaving the original completely unchanged and in parallel). The idea behind this step is to show that the photon sampling for SOPHIA can be done externally - and if done correctly, exactly reproduces the results of original SOPHIA.

Thus follows a detailed yet straight forward explanation of how I intend to do this:

  • I have pushed a version of CRPropa that introduces a new module called PhotoPionProductionMod that is identical to PhotoPionProduction except that it makes use of a new call to SOPHIA called sophiaeventmod (taken from and following @adundovi)
  • I have modified the original SOPHIA, here written in sophia_legacy.f for purposes of demonstration such that it disregards which photon energy has been internally sampled but always selects eps=1e-3 eV. This value is being printed to the console for clarification (lines 16812 and 16813)
  • PhotoPionProduction and PhotoPionProductionMod both contain a class method called "sophiaEvent". The signature of these methods are
    PhotoPionProduction::sophiaEvent(bool onProton, double Ein, double redshift=0.)
    and
    PhotoPionProductionMod::sophiaEvent(bool onProton, double Ein, double eps)
  • if PhotoPionProduction::sophiaEvent is called, the original SOPHIA code is called and calculates the interaction event for a photon energy of 1e-3 eV - if PhotoPionProductionMod::sophiaEvent is called, it calls sophiamod as written in "sophia_interface.f"

Here comes the critical part:

  • sophiaeventmod (lines 13-103 in sophia_interface.f) is identical to sophiaevent (lines 16699 to 16846 in sophia_legacy.f) with the exception that there is no call to any photon sampling function in sophiaeventmod (as this is an external parameter now) as opposed to a call to the photon sampling routines in lines 16780 to 16803 in the original code. The signature of sophiaeventmod has changed accordingly.
  • To proof that both approaches yield the same results, we make use of the fact that SOPHIA actually is deterministic in its arguments (at least seen from how it is integrated into CRPropa). Hence, in an ipython terminal run:
In [1]: from crpropa import *
In [2]: ppp = PhotoPionProduction()
In [3]: for x in ppp.sophiaEvent(True, 1e11 * GeV):
   ...:     print(x)
   ...:

the output should be

eps = 1.0000000000000000E-003
1000000010.0
14.0
12.0
-11.0
-14.0
13.097485723026773
0.26277784161314893
0.852441829038488
0.7331417821599351
1.0759176941616528

In another terminal (to re-set the RNG counter as both versions call sophia_legacy.f in the end)

In [1]: from crpropa import *
In [2]: pppmod = PhotoPionProductionMod()
In [3]: for x in pppmod.sophiaEvent(True, 1e11 * GeV, 1e-3 * eV):
   ...:     print(x)
   ...:

which should yield

sophiaeventmod version
1000000010.0
14.0
12.0
-11.0
-14.0
13.097485723026773
0.26277784161314893
0.852441829038488
0.7331417821599351
1.0759176941616528

This works for each combination of parameters that lie over the threshold CM energy for photo-pion production

Summary of this step:

  • PhotoPionProduction and its mod version are identical except that they call either sophiaevent or sophiaeventmod, respectively.
  • sophiaevent is the original version whereas sophiaeventmod does not call any photon sampling routine internal to SOPHIA but gets it externally
  • This is to show that with this modification one can have both versions of SOPHIA in parallel whereas the modified version lets you choose the photon energy of a scattering photon. This energy can later be sampled in future methods intrinsic to the PhotoPionProduction and not SOPHIA, allowing for variable photon fields

Can this be verified by anyone?

@adundovi
Copy link
Member

I've checked your changes. And it works as you presented, except one detail, you should probably change the data prefix in the mod version of PPP to use the same data files as the original PPP:

        if (haveRedshiftDependence)
                initRate(getDataPath("PhotoPionProduction/rate_" + fname.replace(0, 3, "IRBz") + ".txt"));
        else
                initRate(getDataPath("PhotoPionProduction/rate_" + fname + ".txt"));

BTW how do you plan to integrate performInteraction with sophiaEvent later?

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Jul 26, 2019

Thanks for looking into this!
...oops, that must have been a copy & paste gone wild. Changes to the data files have been made accordingly.

My suggestions on how to proceed / integrate are the following:

  1. We either remove PPP::sophiaEvent again in the future as it was supposed to show behaviour of SOPHIA without input of CRPropa itself.
  2. We keep and embed it into performInteraction, replacing the current way to call SOPHIA. The advantage I see doing this is that a user could use PPP(/mod)::sophiaEvent outside of a simulation as well.

Do your concerns evolve around how to provide eps? Because since we are at the same page up to here, this would be the next step(s). These could look like the following

  1. Implement photon sampling into CRPropa (let's say to PhotonBackground.cpp), taking SOPHIA methods and translate them into C++ (already achieved). This could be done via a new class, e.g. CustomPhotonField, that provides a method like
    sampleEps(bool onProton, double Ein, double z) (already achieved)
  2. This photon sampling gets verified against SOPHIA methods (already achieved)
  3. make PPPMod call these sampling routines and pass this to sophiaEvent (already achieved)

These points are pushed in another branch but I am happy to provide this step-by-step. I can also elaborate on how custom photon sampling would work in CRPropa before continuing.

Do you have a preferred way to move on?

@adundovi
Copy link
Member

Ciao @Froehliche-Kernschmelze ,
I agree with the second decision - to keep sophiaEvent as a separate method in both classes (the new and the old). I'm also fine with the second part of your comment as long as it affects only PPPMod.

@lukasmerten
Copy link
Member

Does anyone of @adundovi, @MHoerbe or @TobiasWinchen know if there is still something in this PR that was not merged with different pull requests?

I tried to quickly go through the discussion again but could come to a fast conclusion. So any hint would be appreciated.

@MHoerbe
Copy link
Contributor Author

MHoerbe commented Jan 31, 2023

Does anyone of @adundovi, @MHoerbe or @TobiasWinchen know if there is still something in this PR that was not merged with different pull requests?

I tried to quickly go through the discussion again but could come to a fast conclusion. So any hint would be appreciated.

I remember that we kept this PR open but eventually agreed to never merge it. Our outline was:

  1. the changes I proposed here are too big and deep
  2. nonetheless, we wanted to have all changes (and a few future ones, see below) implemented which are proposed here
  3. hence, we open, review, and merge new and smaller PRs until everything proposed here is in the main branch

NOTE: over the years more features came to life and I had documented their implementation in my PhD thesis.

Afaik the following changes are in fact implemented now:

  1. cut out photon sampling from SOPHIA, streamline and generalize it to any photon field, and make it a feature of PhotonField
  2. abolish PhotonField enums and introduce proper, tabulated PhotonField classes
  3. enhance the API such that everyone may create custom photon fields (+ update the data repo, e.g. calc_* files)
  4. interaction tags

Super good work so far; I am very happy to see this come to life in the main branch.

What remains, partly with respect to this PR, is

  1. replace the Fortran version of SOPHIA by a C++ version. It is operational and tested but I guess every further opinion helps
  2. make the C++ version of SOPHIA threadsafe (which the version above is not yet). Alternatively, have the entire code discretised, I remember others at DESY(?) having approached this
  3. introduce space- and time dependence on all interactions by introducing a 4D grid overlay. This is simply an extension to the domestic Grid Class to include time as well and would be passed to an interaction if desired.
  4. hadronic interactions, yet I have seen that work is ongoing and I do not overview the entirety of progress here.

TL;DR
we can safely close this one if the 4 points above if:

  1. the remaining proposed changes are discussed. Shall we opt out for all of these, this PR can be closed right ahead
  2. if any of the above remaining changes are to be realised, they should go into a separate issue or even a milestone. Then we can close this PR.

@lukasmerten
Copy link
Member

Thanks @MHoerbe for the very comprehensive summary.

I like the idea to discuss open points of this PR and then move the interesting ones into issues. I will bring it up in the next CRPropa meeting and make the necessary changes afterwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.