Coordinate system labelling - #1459
Conversation
Define enum class COORDINATE_SYSTEM. Add a COORDINATE_SYSTEM member to Field3D and write as an attribute in output files. The default is set by the ParallelTransform but changed to FieldAligned by 'toFieldAligned()' and changed back by 'fromFieldAligned()'.
Derivative functions are templates, so Field2D needs get/setCoordinates methods (even if these are never called).
Initialize ParallelTransform in BoutMesh::load(), so it is almost always available. Creating the FCITransform requires nx/ny/nz to be set in the mesh, so cannot call setParallelTransform() in the Mesh constructor, since it is called before these are initialized.
| T result(localmesh); | ||
| result.allocate(); // Make sure data allocated | ||
| result.setLocation(outloc); | ||
| if (std::is_base_of<Field3D, T>::value) { |
There was a problem hiding this comment.
It would be good to avoid special handling for particular field types so would it be possible to ensure all field types can provide this information? (also in the long run we might need to be able to identify if a Field2D is compatible with a Field3D etc.)
There was a problem hiding this comment.
I'm not sure what the right design would be. Things to consider:
Field2DandField3Dprobably need different labelling -Field2Dis the same forOrthogonalandFieldAlignedcoordinate systems- could have different
COORDINATE_SYSTEMenum classes forField3DandField2Dplus some methods/functions to compare them - or extend
COORDINATE_SYSTEMto something likeenum class COORDINATE_SYSTEM = {None, FieldAligned, Orthogonal, FieldAligned2D, FCI}whereFieldAligned2Dwould be used forField2Dand compatible with bothFieldAlignedandOrthogonal(suggestions for better naming welcome if we want to go with a scheme like this)
- could have different
- In the final implementation,
Field2Dmembers ofCoordinatesneedcoordinate_systemsetting. Best thing to do might be split initialization with 'uninitialized'ParallelTransformobjects constructed early on (so we cangetCoordinateSystem()from them), before most of the initialization ofCoordinates.
There was a problem hiding this comment.
Field2D could have COORDINATE_SYSTEM::AxisSymmetric?
Implementing operator==(COORDINATE_SYSTEM, COORDINATE_SYSTEM) would give us control over comparisons.
| /// localmesh->getCoordinateSystem() can call | ||
| /// ParallelTransform::getCoordinateSystem() for fields belonging to the | ||
| /// ParallelTransform | ||
| void initialize(); |
There was a problem hiding this comment.
This sort of two stage setup has advantages for a number of things, but it is an important design decision so I think there would need to be quite a bit of input on this from @bendudson, @ZedThree and others.
There was a problem hiding this comment.
@d7919 agreed. I did consider calling setCoordinateSystem explicitly for all the fields in each ParallelTransform, but it got quite messy. It could make more sense to put off this kind of redesign, e.g. if we will make ParallelTransforms belong to Coordinates, it would make sense to design the initialization of both together.
For what I need at the moment, I think could get away with reducing the amount of checking: I only really want the checks in to/fromFieldAligned, and it could be OK in the short term to allow some fields to float around with coordinate_system = COORDINATE_SYSTEM::None, especially since they would only be in the ParallelTransforms and are probably only accessed by index rather than with Field3D operations. I'll have a think if there's a sensible, simpler initial version to start with, so we can give longer consideration to something like the implementation here, which is a significant re-design.
There was a problem hiding this comment.
I've moved the split initialization into #1463. This PR still seems to work (at least passes the tests) without it. The split initialization is, I think, necessary for all Field3Ds to have their coordinate_system set correctly, but I think the only ones that don't get one set are in ShiftedMetric or FCI and only get accessed via indices, where no check is (or can really be) performed.
I think something like it was going to be more necessary when I was trying to get Field2D to also have a coordinate_system: then all the Field2Ds in Coordinates need to get set properly (which #1463 actually doesn't help with in the end...) because they multiply Field3Ds in the differential operators, etc., so if they had coordinate_system = COORDINATE_SYSTEM::None then checks would fail, or some horrible hacks would be necessary, like preventing copying the coordinate_system from a Field*D which has coordinate_system = COORDINATE_SYSTEM::None.
There was a problem hiding this comment.
I would much prefer to avoid two-phase initialisation if possible, as it increases the surface area for potential bugs.
Would moving the parallel transform from Mesh into Coordinates help? I think that's probably where it really belongs anyway.
Another thing that might help would be to use another type instead of Field*s in Coordinates/ParallelTransform that aren't "on" coordinates (though they'd still need to be "on" meshes).
There was a problem hiding this comment.
I can't actually remember now exactly what made me think two-phase initialization was necessary. I've added a few commits trying to sort this a different way, adding constructors which take an explicit CoordinateSystem for the ParallelTransform constructors to use. Also attempting to add coordinate_system to Field2D. Still needs testing, probably tweaking a bit too, but I've got to go now. I'll look over it again later, hopefully this week.
277d43b to
5e236f3
Compare
| * Return the coordinate system of this field | ||
| */ | ||
| COORDINATE_SYSTEM getCoordinateSystem() const { | ||
| #if CHECK > 0 |
There was a problem hiding this comment.
I think this should always throw as it's a sign of an invalid executable and there's no performance penalty (i.e. it's not a check that typically passes in a valid program).
test-yupdown has a custom field-aligned derivative that did not set the coordinate system, causing an exception when checking was turned on.
0a13ba4 to
15e40bb
Compare
| * created then must not need to know their COORDINATE_SYSTEM, or must set it | ||
| * explicitly later. | ||
| */ | ||
| enum class COORDINATE_SYSTEM { None, FieldAligned, Orthogonal, FCI }; |
There was a problem hiding this comment.
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#enum5-dont-use-all_caps-for-enumerators
We're a bit inconsistent here, but I think this should be CoordinateSystem
New constructor for Field3D which sets coordinate_system explicitly, rather than setting from fieldmesh. Allows constructors of ParallelTransform implementations to set coordinate_system for their member Field3Ds before the Mesh has a ParallelTransform member. Also add 'Mesh& thismesh' member of the base ParallelTransform class, so that all ParallelTransform objects keep track of the Mesh that they belong to.
Also move the definition from field.hxx to bout_types.hxx to be more consistent with other enums and enum classes. Need to rename the map for conversion to strings CoordinateSystem->CoordinateSystemMap to avoid a name conflict.
Custom operator to compare CoordinateSystem variables. FieldAligned, Orthogonal and FCI are used for Field3Ds and are all different. For Field2D, Axisymmetric returns true when compared with itself, FieldAligned or Orthogonal because with a ShiftedMetric ParallelTransform Field2Ds are compatible with either Field3D type. Field2Ds can also use CoordinateSystem::FCI, which only matches with FCI.
Zero initialize the Field2D or Field3D variable in Mesh::get() including passing 'this' pointer to ensure all members (fieldmesh, location, coordinate_system) are properly set.
Avoids 'multiple definition' errors due to implementing the function in a header.
dy is needed by the FCIMap constructor and dz by the ShiftedMetric constructor. Read directly from the Mesh data source instead of reading from Coordinates objects. This avoids the need to create the Coordinates objects until the ParallelTransforms have finished being created, so coordinate_system can be set for the member fields of Coordinates. To reduce code duplications, adds a 'getnz()' method to Mesh, which handles the 3 possible ways of specifying dz in input.
415065a to
467af37
Compare
|
Closing this proposal: we're working on a different approach to tagging fields with coordinate types. |
As a preliminary to the
ShiftToFieldAlignedimplementation ofParallelTransform, this PR introduces labelling of the coordinate system used byField3Ds. This is now stored as anenum class COORDINATE_SYSTEMmember ofField3D. The main aim is fortoFieldAligned()to be able to skip transforming if a field is already field-aligned. I think it's also helpful for record-keeping,Field3Ds as it allows the coordinate system of aField3Dto be kept with the field, both in the C++ code and in output files (where it is saved as an attribute).In order to be able to get theCOORDINATE_SYSTEMfor new fields from theParallelTransformwhile initializing theParallelTransform, I've split the initilization ofParallelTransforms into a minimal constructor and aninitializemethod: ininitializetheMeshhas a validParallelTransformsoParallelTransform::getCoordinateSystem()can be called.Uses the changes in #1432, so using
region-arguments-tofromFieldAlignedas the base (until that PR is merged).