Add region arguments for toFieldAligned/fromFieldAligned - #1432
Conversation
Add region arguments (where it is valid to give RGN_NOX or RGN_NOBNDRY) so functions like toFieldAligned/fromFieldAligned can be used both when y-guard cells have been set and when they have not. e.g. when using fromFieldAligned on the results of interpolation or derivatives, y-guard cells cannot be included, but when using toFieldAligned on the inputs to interpolation or derivatives, y-guard cells must be included.
| const Field3D ShiftedMetric::shiftZ(const Field3D &f, const Field2D &zangle) { | ||
| const Field3D ShiftedMetric::shiftZ(const Field3D &f, const Field2D &zangle, const REGION region) { | ||
| ASSERT1(&mesh == f.getMesh()); | ||
| ASSERT1(region == RGN_NOX || region == RGN_NOBNDRY); // Never calculate x-guard cells here |
There was a problem hiding this comment.
Not sure this is necessary; it may be inefficient to perform shifting in the X guard cells, but shouldn't cause a problem. There are some cases where it may be useful e.g. calculating D2DXDY
There was a problem hiding this comment.
I think the problem I was originally trying to avoid was when shifting in the corner guard cells. I remember getting 'uninitialized value' warnings from Valgrind that I wanted to protect against. They must have been coming from interpolated zShift fields though, so probably the right solution is to make sure all points in zShifts get initialized to something even when checking is turned off, instead of trying to force the corners to not be used here.
I'll get rid of this check 👍
Following discussion in PR #1432: it is not necessary to restrict the regions for to/fromFieldAligned or shiftZ to RGN_NOX and RGN_NOBNDRY as was being done previously.
| shiftZ(f(jx,jy), phs[jx][jy], result(jx,jy)); | ||
| } | ||
|
|
||
| for (const auto &i : mesh.getRegion2D(REGION_STRING(region))) { |
There was a problem hiding this comment.
Could this use the BOUT_FOR(_SERIAL )macro rather than the range based loop?
| // (Note valgrind complains about corner guard cells if we try to loop over | ||
| // the whole grid, because zShift is not initialized in the corner guard | ||
| // cells.) | ||
| for(const auto &i : mesh.getRegion2D(REGION_STRING(region))) { |
| BOUT_FOR(i, zShift.getRegion("RGN_ALL")) { | ||
| for(int jz=0;jz<nmodes;jz++) { | ||
| BoutReal kwave=jz*2.0*PI/zlength; // wave number is 1/[rad] | ||
| fromAlignedPhs[i.x()][i.y()][jz] = dcomplex(cos(kwave*zShift(i.x(),i.y())) , -sin(kwave*zShift(i.x(),i.y()))); |
There was a problem hiding this comment.
I think you can just index zShift using i (i.e. zShift[i]) here.
| for(int jz=0;jz<nmodes;jz++) { | ||
| BoutReal kwave=jz*2.0*PI/zlength; // wave number is 1/[rad] | ||
| BOUT_FOR(i, zShift.getRegion("RGN_ALL")) { | ||
| BoutReal yupShift = zShift(i.x(),i.y()) - zShift(i.x(),i.y()+1); |
There was a problem hiding this comment.
Index with i and i.yp() possible here I think.
| shiftZ(&(f(jx,jy+1,0)), yupPhs[jx][jy], &(yup(jx,jy+1,0))); | ||
| } | ||
| BOUT_FOR(i, f.getRegion("RGN_NOX")) { | ||
| shiftZ(&(f(i.x(),i.y()+1,0)), yupPhs[i.x()][i.y()], &(yup(i.x(),i.y()+1,0))); |
There was a problem hiding this comment.
Again I think you can use i.yp().
There was a problem hiding this comment.
@d7919: This loop should be using getRegion2D, so can't use i.yp for f. I've updated to follow the rest of your comments.
There was a problem hiding this comment.
@johnomotani it's possible to index a Field3D with an Ind2D, you just need to specify the z index as well. This uses a multiply and add to get the corresponding Ind3D as opposed to three divides and a modulus to call i.x() and i.y(), as such I'd expect the Ind2D approach to be more efficient. I'll add these changes to the branch that fixes the OpenMP stuff
| shiftZ(&(f(jx,jy-1,0)), ydownPhs[jx][jy], &(ydown(jx,jy-1,0))); | ||
| } | ||
| BOUT_FOR(i, f.getRegion("RGN_NOX")) { | ||
| shiftZ(&(f(i.x(),i.y()-1,0)), ydownPhs[i.x()][i.y()], &(ydown(i.x(),i.y()-1,0))); |
| } | ||
|
|
||
| BOUT_FOR(i, f.getRegion(region)) { | ||
| shiftZ(f(i.x(),i.y()), phs[i.x()][i.y()], result(i.x(),i.y())); |
There was a problem hiding this comment.
Could you replace f(i.x(), i.y()) with &f(i.x(), i.y(), 0) and the same for result. Note I think you want f.getRegion2D(region) (or f.getMesh()->getRegion2D(region)) as currently you're looping over z as well.
| // the whole grid, because zShift is not initialized in the corner guard | ||
| // cells.) | ||
| BOUT_FOR(i, f.getRegion(region)) { | ||
| shiftZ(f(i.x(), i.y()), mesh.LocalNz, zangle(i.x(),i.y()), result(i.x(), i.y())); |
Regions used in BOUT_FOR loops must exclude y-guard cells, like the previous nested loops.
|
BOUT-dev/src/mesh/parallel/shiftedmetric.cxx Lines 138 to 152 in baa630c I can't see why the loop should not parallelize though. The loop body for each |
|
I don't have the code in front of me but done of these methods use a class member working space (vector) which would lead to race competition etc. I think this could probably be removed from class and made a routine local Array without performance penalty. Can look later if any help |
Those should help improve OpenMP safety.
|
I've pushed a branch at https://github.com/boutproject/BOUT-dev/tree/region-arguments-tofromFieldAligned-shiftZ-openmp which might help fix this. |
…dAligned-shiftZ-openmp Use function local storage in shiftZ rather than member storage
|
This needs updating now #1345 is in |
Need to set number of y-guard cells to 2 before creating default regions. Otherwise BOUT_FOR loops go out of bounds.
|
I've merged This PR does two things:
I think 1 is good and should be merged. @ZedThree do you want 2, and if so should I split it into a separate PR? |
RGN_ALL is no longer the default for toFieldAligned/fromFieldAligned. Force it to be used for the unit test.
|
This is failing with OpenMP enabled again. If I switch the BOUT-dev/src/mesh/parallel/shiftedmetric.cxx Lines 193 to 213 in e1857ec Can anyone see the race condition or other error? |
|
Probably unrelated but BOUT-dev/src/mesh/parallel/shiftedmetric.cxx Line 205 in e1857ec doesn't set the |
|
The only place inside the loop that I could see an issue arising is with BOUT-dev/src/mesh/parallel/shiftedmetric.cxx Line 211 in e1857ec As we're writing to |
L232-233 has been clobbered, it should look like: BOUT-dev/src/mesh/parallel/shiftedmetric.cxx Lines 232 to 233 in ace3f77 |
Prevents errors with OpenMP.
|
@ZedThree I'd changed I've just pushed a commit replacing |
Swap some array entries around so that 'input' in ShiftedMetricTest is not constant in x or y. Being constant could mask errors.
|
The previous test failure was because I'd messed up an index in |
|
Is this PR good to go in now, if the conflict is fixed? |
| /// Transform a field into field-aligned coordinates | ||
| const Field3D toFieldAligned(const Field3D &f) { | ||
| return getParallelTransform().toFieldAligned(f); | ||
| const Field3D toFieldAligned(const Field3D &f, const REGION region = RGN_NOX) { |
There was a problem hiding this comment.
Should the default just be RGN_ALL since (I think) that's what it was before?
| BOUT_FOR(i, mesh.getRegion2D("RGN_ALL")) { | ||
| for(int jz=0;jz<nmodes;jz++) { | ||
| BoutReal kwave=jz*2.0*PI/zlength; // wave number is 1/[rad] | ||
| fromAlignedPhs(i.x(), i.y(), jz) = |
There was a problem hiding this comment.
Is it worth caching the values of i.x(), i.y() outside the z-loop? Probably won't make much difference in the constructor, but could be more important in the other methods
(also clang-format please 🙂)
Add region arguments (where it is valid to give
RGN_NOXorRGN_NOBNDRY) so functions liketoFieldAligned/fromFieldAlignedcan be used both when y-guard cells have been set and when they have not. e.g. when usingfromFieldAlignedon the results of interpolation or derivatives, y-guard cells cannot be included, but when usingtoFieldAlignedon the inputs to interpolation or derivatives, y-guard cells must be included.Pass
RGN_NOBNDRYtofromFieldAlignedin various places where the guard cells aren't valid.Pulled out of #1176; is independent of other stuff in that PR, which now probably won't be merged.