Conversation
eccbc22 to
2f526b9
Compare
| #endif // SCALAR | ||
| #endif | ||
|
|
||
| // 3D case |
There was a problem hiding this comment.
I'm a little confused about why this conditional now only exists for the 3D case - is it because MHD writes only exist for that case?
There was a problem hiding this comment.
I'm not entirely sure I understand what you are talking about. Can you be a little more specific?
I just checked back, and I don't think anything has changed.
Both before the change and after this PR:
- hydro fields (density, momentum_[xyz], Energy, & GasEnergy) and passive scalars are written for 1D, 2D, & 3D sims
- the temperature field can be written for 1D, 2D, and 3D sims
- the gravitational potential can only be written for 3D sims
- magnetic fields can only be written for 3D sims (previously, the logic conditionally enabled by
#ifdef MHDwas definitely nested withinif (H.nx > 1 && H.ny > 1 && H.nz > 1) {
As for why magnetic fields are only written 3D cases: your guess is probably correct (but only Bob would know for sure)
|
|
||
| /*! A callable method that writes a rotated projection of the grid data to file. | ||
| */ | ||
| void operator()(Grid3D &G, Parameters P, int nfile, const FnameTemplate &fname_template) const; |
There was a problem hiding this comment.
I'm confused by the choice of 'operator' for the name of this function, if it is just for writing rotated projections.
There was a problem hiding this comment.
A class member function named operator() overloads the "function call operator."
- Suppose we have an instance of the type (
FieldWriter) calledmy_field_writer. We would invoke this method by callingmy_field_writer(G, P, nfile, fname_template) - So, in a sense, instances of
FieldWriteract just like configurable functions - For added context, you would implement the special
__call__method to get analogous behavior for a python class.
We could name it something else. But the most scalable way to do that involves defining a base-class with a virtual method and each kind of output would implement a subclass that overwrites the virtual method.
This is a WIP PR. I need to revisit this to make sure its coherent and tweak a few things
Overview
This PR introduces the
FieldInfotype. TheGrid3Dtype stores aFieldInfoinstance calledfield_info.This type does a few things:
grid_enum)To help illustrate the benefits of this type, I partially refactored the
Output_Datafunction.Motivation
There are 2 main motivations for this type:
More immediately, it simplifies reading/writing field data files. This logic needs to map data to field names.
FieldInfo,FieldInfo,Output_Datafunction.OutputDataisn't super compelling, alone. Consequently, I have also refactoredGrid3D::Read_Grid_HDF5(RefactorRead_Grid_HDF5to make use ofFieldInfo#468), and in the process of refactoringOutput_Float32(WIP: RefactorOutput_float32#469)Longer-term, if we use
field_info.field_id(<name>)to infer the field ids of passive scalar, this will help us move toward an architecture where physics modules that work with passive scalars are always compiled and fully controlled at runtime,GasEnergyfield id is NEVER inferred fromgrid_enum::num_fields. At that point, we can make the passive scalars always have the highest field ids and the number of passive scalars could be configured at runtime.grid_enum::dust_densityfrom the code base, then the Dust module will be configurable at runtime.grid_enum::dust_density. As an example, considerDust_Kernel. We could modify that function to accept an extra argument calleddust_density_index, which would be determined on the host usingfield_info.field_id("dust_density"). Then, the occurrence ofgrid_enum::dust_densitywould be replaced bydust_density_index.More about
FieldInfoAs noted above, the
FieldInfotype primarily maps strings holding field names to the field's id if present and vise-versa.The type also tracks whether a field should be read from the host field-data buffer or the device field-data buffer during serialization.1
Additionally, the
FieldInfotype makes use of the idea of field-kinds.The kinds are defined by enumerators of the
field::Kindenumeration.The enumerators include (these are always present, regardless of how Cholla is compiled):
field::Kind::HYDRO(it includesGasEnergywhen Cholla is configured to use the DualEnergy formalism)field::Kind::PASSIVE_SCALARfield::Kind::MAGNETICThere are 2 related pieces of functionality:
FieldInfocan be used to loop over the field ids of all fields of a given field kind. The following snippet loops over the field ids of allfield::Kind::HYDROfieldsFieldInfolets you query the number of fields with a given field-kind. For example, the following snippet queries the number of hydro fields:int n_hydro = field_info.n_fields(field::Kind::HYDRO);The
FieldWriterTypeA portion of this PR was dedicated to refactoring
Write_Grid_HDF5and the creation of theFieldWritertype in order to make use ofFieldInfo.At a high-level,
FieldInfotracks all customization for the general field data-output and is a callable that can be invoked to create the format.It's worth mentioning
OUTPUT_MOMENTUM,OUTPUT_ENERGY,OUTPUT_METALS,OUTPUT_ELECTRONS).With that said:
Implementation of
FieldInfo:FrozenKeyIdxBiMapThe
FieldInfotype is implemented in terms ofFrozenKeyIdxBiMap. That type is a custom map type that allows fast mapping from string keys to indices and vise-versa.It's definitely faster than
std::maporstd::unordered_mapfor our purposes. For some context:std::mapis typically implemented as a Red-black tree. These operations have the worst average complexity.std::unordered_mapis typically implemented as a hash table that uses linked-list chaining for handling key collision.,FrozenKeyIdxBiMapis implemented with a hash table that uses open-addressing with linear probing for key collisions.For clarity, I'll briefly recap the distinction between hash maps that using chaining and linear probing. Let's consider the process of looking up a key. In both cases keys are organized by a storage array, and a key's hash-value always corresponds to an index of this array.
For our purposes (i.e. a relatively small map where the contents easily fit in cache and there is no need for resizing/deletion), the
FrozenKeyIdxBiMaphas significantly better cache-locality thanstd::unordered_map. With that said, I haven't actually benchmarked.It's also worth mentioning that:
std::unordered_mapwould probably require us to store a second copy of all keys if we wanted to make the reverse mapping efficient.std::shared_ptr2, it would be easy to makeFrozenKeyIdxBiMapwork on GPUs (we probably don't want to do that, but it's worth mentioning the option).Aside: Originally, I intended to work directly with a
FrozenKeyIdxBiMap, but I decided to makeFieldInfoas this PR progressed.Note
A case could be made for re-implementing
FrozenKeyIdxBiMapin terms ofstd::maporstd::unordered_map, in the name of simplicity. I'm open to doing that if that's your preference.Footnotes
It's not entirely clear that this should be tracked as part of
FieldInfo, but it is indeed a useful quantity to track. ↩This is left over from when I implemented a similar data structure in Enzo-E. We don't really get much benefit from it. ↩