-
Notifications
You must be signed in to change notification settings - Fork 486
Adding classes to calculate the space charge distortions and corrections #4618
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
Adding classes to calculate the space charge distortions and corrections #4618
Conversation
wiechula
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps instead of ...Structs.h you could use ...Helpers.h. In some of the file you declare structs with private members. I think I would rather make them classes.
Otherwise only minor comments
| /// Helper function to check if the integer is equal to a power of two | ||
| /// \param i the number | ||
| /// \return 1 if it is a power of two, else 0 | ||
| bool isPowerOfTwo(int i) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be simply
return ((i>0) && !(i & (i - 1)));
| /// operator to set the values | ||
| DataT& operator()(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) | ||
| { | ||
| return mStorage[iR + mNr * (iZ + mNz * iPhi)]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use getIndex
| /// operator to read the values | ||
| const DataT& operator()(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) const | ||
| { | ||
| return mStorage[iR + mNr * (iZ + mNz * iPhi)]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use getIndex
| /// this is a simple vector class which is used in the poisson solver class | ||
|
|
||
| /// \tparam DataT the data type of the mStorage which is used during the calculations | ||
| template <typename DataT = double> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this not in an own header as e.g. DataContainer3D?
| #include <cmath> | ||
| #include "TPCSpacecharge/TriCubic.h" | ||
| #include "DataFormatsTPC/Defs.h" | ||
| #include "TPCSpacecharge/SpaceChargeStructs.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove?
|
|
||
| static constexpr unsigned int getID() { return ID; } | ||
|
|
||
| // private: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why commented?
|
|
||
| public: | ||
| /// default constructor | ||
| Vector() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=default
| typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; | ||
| typedef Kernel::Point_3 Point_3; | ||
| typedef boost::tuple<Point_3, std::array<unsigned int, 3>> Point_Index; | ||
| typedef CGAL::Search_traits_3<Kernel> Traits_base; | ||
| typedef CGAL::Search_traits_adapter<Point_Index, CGAL::Nth_of_tuple_property_map<0, Point_Index>, Traits_base> Traits; | ||
| typedef CGAL::Orthogonal_k_neighbor_search<Traits> K_neighbor_search; | ||
| typedef K_neighbor_search::Tree Tree; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change to using?
|
|
||
| template <typename DataT = double> | ||
| struct TPCParameters { | ||
| static constexpr DataT TPCZ0{249.525}; ///< nominal gating grid position |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be nominal G1T position in the comment
| void PoissonSolver<DataT, Nz, Nr, Nphi>::poissonSolver2D(DataContainer& matricesV, const DataContainer& matricesCharge) | ||
| { | ||
| poissonMultiGrid2D(matricesV, matricesCharge); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note:
iPhi (third argument of the function) is set to 0 by default. In the function itself, only the iPhi bin of the output matrix matricesV will be filled (line 181). So for the 2D solution, only 1 phi bin is calculated and filled. Once (if) we start to use the 2D solutions for the ion drift, we have to revisit this.
| const o2::tpc::AnalyticalFields<DataT> analyticalFields; | ||
| // set the boudnary and charge for numerical poisson solver | ||
| setChargeDensityFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, charge); | ||
| setPotentialBoundaryFromFormula<DataT, Nz, Nr, Nphi>(analyticalFields, grid3D, potentialNumerical); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why you set the boundary potentials of the numerical calculation with the values from the analytical formula? The numerical solution should work fine without this as it should calculate the potential at these boundaries from the charge?
The function to set only the boundary potentials is supposed to provide to possibility to account for irregularities of the nominal potentials, e.g. charge-up effects at dielectric sector boundaries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why you set the boundary potentials of the numerical calculation with the values from the analytical formula?
It was like this in the original code. So I always used theses as the boundary conditions.
The numerical solution should work fine without this as it should calculate the potential at these boundaries from the charge?
I am not sure if boundary conditions are really needed or not for this method. I made a quick comparison by setting the boundary potential and then calculating the potential with the poisonsolver and without setting the boundary potential. The difference is quit big between the resulting potential to the reference potential. See the two plots "analytical potential - numerical potential". The first plot is with the boundary conditions and the setting plot without these boundary conditions for the potential.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Now I am thinking about what this means for the case when we use density histograms as input. Maybe at some point we can try to put there the nominal TPC boundary conditions (potential CE, G1T and field cage) already for the Poisson solver instead only adding it (Ez0=400 V/cm) for the calculation of the distortions.
|
Hello @matthias-kleiner , can you add a method to the main class which calculates all the quantities (potential, E-field, distortion, correction), similar to the function calculateDistortionsCorrections of the example macro? It would make the application of the class in future studies much simpler, or what do you think? Otherwise, it looks very good to me. Cheers, |
Hi Ernst, I also implemented all the other comments from you and Jens. |
|
I still have to invert the distortions in z direction for the A-(?)Side. Should I do it directly during the filling of the values or when quering the values (like it was done in the old code). I assume it should be done during the filling? And there is in the TPCSimulation folder a class with the same name "SpaceCharge.h", which causes some problems when running a simulation. |
|
Hi @matthias-kleiner ,
I think it concerns the values on the C side? On the A side, the direction along z in the array/matrix entries is 0 -> nZ and for the z coordinate 0 -> 250 cm. On the C side it is 0 -> nZ and 0 -> -250 cm. Differentiation / integration along z is done using the array / matrix index, right? So on the C side, you will basically invert the z axis (direction -z instead of z) which results in a change of the sign. I think this happens first when calculating the Ez field from the potential? So you can change the sign of Ez on the C side and it should be correct for all following steps? But we should check the signs of the E fields, local and global distortions after the change.
Is it related to the new classes? They are not yet used in the simulation and are in a different scope, right (e.g. TPCSpaceCharge instead of TPCSimulation)? Cheers, |
|
Hi @ehellbar
when running a simulation it completes, but I get some error from I think ROOT: |
Hi @ehellbar,
I just checked it, but the resulting corrections/distortions are slightly different after changing the sign of the ez field. I think changing the sign of Ez also changes these integrals "int er/ez dz" etc. when calculating the distortions. So i think one should integrate then from 0 -> -nZBins, but then I have to change several other things too. |
|
Hello @matthias-kleiner , |
The class is mainly used in the TPC digitizer and some macros. Maybe the best solution at the moment is to replace the usage of the other SpaceCharge class by your code, which requires a couple of new methods used in the other SpaceCharge class: distortElectron and correctElectron and an enum class SCDistortionType. Then, the old class can be removed from compilation. It still contains methods to propagate the space-charge density or ions which we should continue to develop and move to the spacecharge folder, either as a new class or as addition to SpaceCharge. But once the dependencies on the old SpaceCharge class are replaced by yours, we can basically delete the old one (getting the ion movement methods later from the history) and the GPU/TPCSpaceChargeBase folder as well. |
Hi @ehellbar, |
|
Hi @matthias-kleiner , |
Hi @ehellbar,
Okay, I fixed it to 129x129x180.
When I run the external macro to calculate the distortions/corrections for an input histogram it takes ~10sec (8 Threads) for each side. I replaced the old SpaceCharge class with the new one and it seems that it works in the digitizer. However when I simulate one event and then run the digitizer with
So this means the SpaceCharge object is then read from a ROOT file or is it calculated somewhere else and then just passed to the function? |
Yes, the way this is passed on at the moment, the distortions are calculated for each lane.
Yes, this is supposed to read a precalculated SpaceCharge object stored in a root file. |
|
Hello @matthias-kleiner , Line 364 in TPCDigitizerSpec.cxx also should be removed: Can you also adjust the usage in GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C, please? This macro creates the fast correction objects using spline fits of the corrections, to be then applied in the reconstruction. Cheers, |
I just tried to implement a method to store the SpaceCharge file in a ROOT file, but for some reason it doesnt store the members. I think it might be related to all the static members which are used. But I already implemented a function to set the values (the DataContainer3D member) for the distortions/corrections etc. from a ROOT file. I think it should be easier to store the values for the distortions/corrections in a ROOT file and then set the data members from the ROOT file. Do you think this would also be fine? And if so which values have to be loaded then (only the global distortions and the global corrections)? |
|
Hi @matthias-kleiner , |
hi @ehellbar, |
|
Hi @matthias-kleiner , But the macro I was actually looking for and which I meant is Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C . Here, the AliTPCSpaceCharge3DCalc class is used, so it is not directly affected by the changes, only when we also delete those classes. But it would still be great if you could also change this macro. Also here, the spaceCharge object is a pointer to keep flexibility with grid size and interpolation techniques, but I think in your new implementation this might be redundant and you can make it an object. There are also two debug functions in createTPCSpaceChargeCorrection.C, one to compare results on the spaceCharge grid and one to compare results on the spline points. In the past, we evaluated the results only for the points for which the corrected point is inside the TPC volume. |
Hi @ehellbar |
|
@ehellbar |
|
Hi @matthias-kleiner , |
Hi @ehellbar , |
|
Hello @matthias-kleiner , |
wiechula
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine. Only two very minor comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is SpaceCharge as variable capital?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for noticing it. It should be of course just spaceCharge. I probably changed it by accident
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this function be constexpr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes I changed it
f37641a to
d81a8c4
Compare
|
@matthias-kleiner : Could you apply clang-format :) |
|
@matthias-kleiner : The macro is now failing in all CI instances, not only MacOS, weird. Perhaps it is related to the problem we had before that some tests were simply not running. |
The calculateDistortionsCorrections.C macro needs to be compiled. The other createResidualDistortionObject.C macro should also run in interpreted mode. I will then indicate it with "COMPILE_ONLY" |
|
@ktf @TimoWilken : Could you check what is the problem with CGAL and GMP. After alisw/alidist#2648 GMP should be a direct build dependency of O2, So how can it be that the GMP include folder does not exist? : I have tried this locally in the slc7-builder container but cannot reproduce it. |
|
@davidrohr @ktf That includes something under |
|
@matthias-kleiner : Before I was definint the gridsize for the digitizer workflow via |
the gridsize is now set to a default of 129,180,129. There is currently no option to set it. |
|
@matthias-kleiner : I have retried without the gridsize setting, but the digitization output for the TPC looks broken. Below are 2 visualizations of a single pp collision before and after this PR digitized with |
Okay, thanks for doing this check. I will look into this and see if I can figure out what goes wrong |
|
@davidrohr could you please do the same check again? I just found a bug where I set the z coordinate equal to the y coordinate after the distortion. If this doesnt fix the issue, I could look tomorrow more into it. |
davidrohr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@matthias-kleiner : Thx, with this fix it looks OK, and also the reconstruction with the correction looks like before. So if the CI finally passes, we can finally merge.
Could you just quickly comment on the gridSize parameter? I have seen that also in the macro to create the tpcTransform object, the gridSize parameter is gone. So I have just left it out for my tests. Does it not exist anymore, or is the default just ok, or shall I set it somehow else?
Since the grid size is templated now in the spacecharge class, the easiest solution for now was to just set it to a default value during the simulations and in the macros. Of course the correct default value of the grid size still has to be determined, but gridR=129, gridPhi180, gridZ=129 should already be a good starting point. The grid size is just hardcoded for example in the digitizer Of course it would be better to be able to set the grid size and pass somehow the template parameters of the grid or choose between different presets of grid sizes, but I am not sure if there is an elegant way to implement this in the Digitizer. |
| /// analytical potential | ||
| std::function<DataT(DataT, DataT, DataT)> mPotentialFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { | ||
| const DataT zz = std::abs(z); | ||
| return -mParA * (std::pow((-r + 254.5 + 83.5), 4) - 338.0 * std::pow((-r + 254.5 + 83.5), 3) + 21250.75 * std::pow((-r + 254.5 + 83.5), 2)) * std::cos(mParB * phi) * std::cos(mParB * phi) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
depending on where or how often this function is used during runtime, I'd like to point out that constructs like std::pow((-r + 254.5 + 83.5), 4) are unfortunately inducing a function call to pow and therefore slow. You can check with https://godbolt.org/ that this is only optimized away with -ffast-math which we are not using.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the comment :) I didnt knew that about std::pow. But since these functions are only used for "debugging/testing" anyway, it does not really matter how fast the evaluation is.
| int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; | ||
| int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; | ||
|
|
||
| std::vector<DataT> coefficient1(Nr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not quite getting the rational why the GRID size is a template argument everywhere. But since it is, there is no need to use dynamic memory in many places. std::array<DataT, Nr> coefficent would be a more sensible choice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. The grid size could also be just a member in the different classes and then one could use a std::vector everywhere, which would make the code a lot easier. But at the beginning I thought it would make sense to use arrays instead of vectors and to ensure that one uses the correct grid sizes (there are only specific grid sizes which can be used) and that these classes can only be used with the same grid size. I might remove the templates in the future, but this would require some work...
Indeed it makes sense to use an array therestd::array<DataT, Nr> coefficent, but since this vector is passed to several member functions and in a different function in the code the same coefficient array is not depending on the templated grid size std::vector<DataT> coefficient1(tnRRow) it has to be a vector there. I could probably do some additional templating, to use vectors and arrays where appropriate.
sawenzel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a nice PR. Please consider my comments for a future polishing step.
|
shit, this breaks O2-dataflow defaults, because they disable CGAL.
|
Hi @davidrohr, if the CGAL package causes too much trouble, I could try and see if I can remove it in the space charge class. CGAL is used only in one specific function and I think it might be possible/equally fast to remove CGAL and modify the function where it is used. I can check this and see if its reasonable without CGAL. |
OK, can you check that quickly? If it is too much effort, I just discussed with @sawenzel that we could add CGAL as requirement also for o2-dataflow. |
|
But if that would mean copy&pasting much code, we can also just enforce having CGAL. So perhaps have a quick look what needs to be done and then report back. In the meantime, I am checking what would need to be done to make the CGAL optional. |
I use CGAL actually only for finding the nearest neighbour of some scattered datapoints and then apply some iterative method to approach some query point. I think i could just skip the nearest neighbour searching and start just at some point in the middle of the grid... There would be actually less code than before, but I have to check how fast it is and if the result will be the same. I can test this later today |
|
ok, thx, actually we should have thought about that before we implemented the other workarounds... |
Hi @davidrohr, it looks like I can get rid of the CGAL package and use some slightly different approach in the function where CGAL is used. I will then remove all the CGAL stuff from my classes and push it as soon as possible. |
|
@matthias-kleiner : Ok, great. |
…oximation of nearest neighbour
c60c4da




The distortions and corrections can be calculated by using the potential
from a 3D histogram or an analytical formula as an input. These classes are
much faster and consistent than the current methods located in GPU/TPCSpaceChargeBase and will replace them.