User Manual: Data Structures
From FLUX
The FLUX data structures are defined in data.h, and some interface routines are in data.c. The principal ones are listed and annotated below, with the current fields in the C structure and their corresponding names in the Perl hash.
Laurel Rachmeler has generated a helpful diagram illustrating the connections between different objects in a simple case with a small number of fluxons and flux concentrations; it is below.
Contents |
Overview
Binary trees for access
The main entities -- flux concentrations, fluxons, and vertices -- are tracked in several binary trees. The binary tree structure is expensive to add to (because it requires balancing the tree) but fast to access -- for example, on vintage-2008 hardware in a simulation with 1.5 million vertices, access times to a particular vertex are typically well under a microsecond, while rebalancing the vertex tree can take over 100 milliseconds.
Fluxons: Doubly linked lists of vertices
The vertices associated with each fluxon are also linked to one another in a doubly linked list, which is more natural to describe the topology of the fluxon. Each vertex also stores a collection of neighbors and a (different) collection of other vertices that consider this one to be a neighbor.
Neighbor lists: Variable-length arrays
Short ordered collections of entities can be stored in "dumblists", a roll-your-own variable-length array of pointers. Dumblists keep track of the current number of elements and the number of slots in the array, and expand when necessary to contain all of their elements.
Memory allocation/deallocation
As with many complex data structures in C, keeping track of allocation is tricky. In particular, each vertex is pointed to not only by its neighbors on its parent fluxon, but also by other vertices that consider it to be a neighbor. De-allocating a vertex, then, requires updating all of those links.
In general, pointers are maintained to be either valid or NULL. New data structures are created with new_<foo> routines that populate internal pointers with zero. Old ones are removed with free_<foo> routines that (hopefully) clean up dangling references from elsewhere in the data structure.
To aid debugging, some fenced and otherwise debugged malloc code is available; see data.c for details.
Primitive structures
These are used as the basis of the main structures (defined below):
- NUM
- This is an alias for
double, and is the basic numeric type for FLUX variables. It is defined high indata.h, and can in principal be set tofloatto save memory. That has never yet happened. - POINT2D
- Just an array of 2 NUMs.
- POINT3D
- Just an array of 3 NUMs.
- LINKS
- This is used to keep track of the myriad tree structures in the main data elements. LINKS just maintains the information required to be a node in a tree:
left,right, andupare all generic pointers. There is also a subnode counter (n), a floating point value accumulator (sum), and a balance flag (balanced). LINKS structures abound in the larger structs like FLUXON (below), but using each one requires knowing which data structure it is a part of and hence the offset from the beginning of the data structure. (see "Offsets", below) - DUMBLIST
- This is an extensible array, to avoid multiple allocations of frequently-reused arrays. It contains a generic array pointer (
stuff, which is avoid **, that is to say a pointer to an array of generic pointers), a current element count (n), and a currently-allocated size (size). Dumblists are used for keeping track of variable-length lists of things, such as neighbors of a particular fluxel. You can manipulate them with several routines indata.c - PLANE
- Represents a plane in 3-space, with a point that is on the plane, together with a normal vector. In principle, this can be done with three numbers total (a unit vector requires two unique numbers, and the plane can be defined by an offset from the origin in the direction of the normal vector), but it's oh so much easier to represent the origin and a full vector. The PLANE structure is currently (summer 2007) only used for boundary conditions, and it gets, er, creatively re-used by some of the boundary condition routines. For example, the cylindrical boundary condition uses the six numbers in a different way.
- PHOTOSPHERE
- A PHOTOSPHERE is actually a boundary condition, just poorly named. The structure is also a little stupid -- it just clumps together a PLANE (above) and a type variable. that probably should be an enum but is handled as a collection of #defines in the C code. The current values are: 0 - none; 1 - plane; 2 - sphere; 3 - cylinder. The value of the type variable indicates how the numbers in the PLANE are to be interpreted -- only type 1 treats all six as an actual plane specification.
High-level structures
These hold information about the model itself, building on the lower-level stuff (e.g. these structures contain DUMBLISTs, POINT3Ds, and LINKSes). Each of the high level structures has a corresponding Perl object in the Perl control/interface side of the code.
VERTEX
VERTEX describes an individual fluxel. Recall, a fluxon is an ordered list of fluxels, with vertices between them. Each VERTEX stores the information about itself and the following fluxel on the same fluxon. Thus some values (for example the neighbor list) aren't valid for the last VERTEX in a fluxon data structure. VERTEX structures correspond to Perl objects of type Flux::Vertex.
Each VERTEX currently uses 320 bytes. The fields are as follows:
| C name | C type | Perl name | Perl type | Perl R/W? | Description |
| line | FLUXON * | line | Flux::Fluxon | R | Pointer to the fluxon that contains this fluxel |
| prev
next | VERTEX * | prev
next | Flux::Vertex | R | Forward and reverse links along the linked list of VERTEXes for this fluxon. (Zero/undefined values at the ends of the list). |
| x | POINT3D | x | PDL (3) | RW | The current location of this vertex in 3-space |
| neighbors | DUMBLIST | neighbors | list ref | R | The current collection of neighbor vertices, stored as a DUMBLIST (or readable in Perl as a Perl list of Flux::Vertex objects) |
| nearby | DUMBLIST | nearby | list ref | R | The current collection of vertices who think that this vertex is their neighbor, stored as a DUMBLIST (or readable in Perl as a Perl list of Flux::Vertex objects) |
| scr | POINT3D | scr | PDL (3) | RW | A scratch vector that is used during force/neighbor calculation |
| r
a | NUM | r
a | scalar | RW | Scratch values used during hull operations (the 2-D distance and angle, respectively, to the current center of the hull being calculated) |
| passno | long | An anti-collision gate used during neighbor accumulation - holds a temporary longint designed to prevent gathering the same hull element twice. | |||
| b_vec | POINT3D | b_vec | PDL (3) | RW | The current magnetic field vector at the center of the following fluxel (calculated by one of the B laws in physics.c) |
| b_mag | NUM | b_mag | scalar | RW | The current magnetic field magnitude at the center of the following fluxel (calculated by one of the B laws in physics.c) |
| energy | NUM | energy | scalar | RW | Accumulator for field energy calculations (calculated by routines in physics.c; may store the total energy for the spatial neighborhood around the following fluxel) |
| f_v
f_s f_t | POINT3D | f_v
f_s f_t | PDL (3) | RW | Each of these holds a force accumulator into which routines in physics.c add force vectors: f_v is the current vector total force on the vertex itself (e.g. curvature forces, vertex pseudoforces), and f_s is the current vector total force on the following fluxel segment (e.g. magnetic pressure force). f_t is used to hold the sum of f_v and f_s after the force accumulation is finished. |
| f_s_tot
f_v_tot | NUM | f_s_tot
f_v_tot | scalar | RW | Contain the sum-of-magnitudes of the vertex and following-segment forces; these are accumulated in by the routines in physics.c. |
| r_v
r_s | NUM | r_v
r_s | scalar | RW | Contain the length scales associated with the segment and vertex forces, respectively. |
| r_cl | NUM | r_cl | scalar | RW | This is the closest-approach distance (in 2-D) of any of the neighbors. |
| label | long | label | scalar | RW | This is a unique longint assigned to each vertex as an access key. You should assign positive numbers greater than 10. Negative numbers are used for automatically generated vertices, and small positive numbers are used for the image fluxels that enforce boundary conditions. |
| world_links | LINKS | links_foo | misc | R | world_links is the tree data structure that's used to keep track of all VERTEXes associated with a particular WORLD (simulation). The Perl side treats the individual links fields independently, as links_sum, links_n, links_up, links_left, and links_right. The links_up, links_left, and links_right fields are Flux::Vertex objects and read-only. The numeric fields are read/write, but probably shouldn't be tampered with. |
| plan_step | POINT3D | plan_step | PDL (3) | RW | This is a scratch space for the calculated naive relaxation step, before large-eigenvector acceleration (see the discussion on stepping in model.c).
|
FLUXON
The FLUXON structure represents a single fluxon. It contains information that is tied to each fluxon but doesn't need to be repeated between vertices. Of course it contains the head and tail of a linked list of VERTEX structures. FLUXON structures correspond to Flux::Fluxon objects in the Perl environment.
| C name | C type | Perl name | Perl type | Perl R/W? | Description |
| world | WORLD * | A pointer to the WORLD that contains the fluxon. | |||
| flux | NUM | flux | scalar | RW | The magnetic flux contained in the fluxon. This field is not currently used -- it is a placeholder for the flux-scaling code. The purpose is to generalize the hull routine by scaling the distance to neighbors. |
| label | long | label | scalar | RW | The unique label of this fluxon -- can be used to find it with the Flux::World::fluxon() method. |
| start
end | VERTEX * | start
end | FLUX::VERTEX | R | The start and end of the fluxon -- these are the ends of the linked list. In a well-constructed FLUXON these should never be NULL, and they should never be the same either. (During allocation or cleanup they might be NULL...) |
| v_ct | long | v_ct | scalar | RW | This is the vertex count in the fluxon. You should probably not write to it from the Perl side, or some things (it's hard to predict what) will get screwed up. Inserting or deleting vertices via delete_vertex, add_vertex_pos, or add_vertex_after takes care of it for you. |
| all_links | LINKS | all_links_foo | misc | R | This is the LINKS structure that's used to maintain a worldwide tree of all existing fluxons for quick access. |
| start_links | LINKS | start_links_foo | misc | R | This is the LINKS structure that's used to maintain a tree of all fluxons that share the same starting (North pole) flux concentration. |
| end_links | LINKS | end_links_foo | misc | R | This is the LINKS structure that's used to maintain a tree of all fluxons that share the same ending (South pole) flux concentration. |
| fc0
fc1 | FLUX_
CONCENTRATION * | fc0
fc1 | Flux::
Concentration | R | Pointers to the flux concentrations that start and end this fluxon. These should never be NULL in any well-formed fluxon. There are some dummy flux concentrations in each WORLD, that represent open or plasmoid boundary conditions. |
| plasmoid | char | This is a flag indicating that the fluxon is actually a plasmoid. It should not be set arbitrarily, and exists as a convenience -- plasmoids should also be linked to the specific plasmoid flux concentrations associated with the WORLD that contains them. |
FLUX_CONCENTRATION
These represent a pole on the solar photosphere -- a point at which some flux is anchored on the Sun. Each flux concentration can support many fluxons. Because they are all anchored to the flux concentration at a single point there is (currently) no conservation of twist between the different fluxons attached to a given concentration: everything is free to swivel and eliminate mutual currents. To represent current requires at least two different flux concentrations, to hold the fluxons in place against the untwisting forces imposed by the currents. FLUX_CONCENTRATION structures correspond to Perl objects of type Flux::Concentration.
| C name | C type | Perl name | Perl type | Perl R/W? | Description |
| world | WORLD * | A pointer to the WORLD that contains this flux concentration. Should never be null in a well-formed FLUX_CONCENTRATION. | |||
| flux | NUM | flux | scalar | rw | Represents the total amount of flux contained in this flux concentration. Currently (Summer 2007) ignored; will become more important for code that includes evolving fluxes at the boundary from, say, feature tracking. Positive for north poles, negative for south poles. The sum of all the fluxes in all flux concentrations (including the dummy boundaries) in a WORLD should be zero, but that is not currently checked for (summer 2007). |
| label | long | label | scalar | RW | The unique label used to identify this flux concentration. Positive for north poles, negative for south poles. |
| lines | FLUXON * | lines | Flux::Fluxon | R | This is the root of the fluxon tree (either the start_links or the end_links tree, depending on the sign of the flux concentration -- positive for starts, negative for ends) containing all the FLUXONs associated with this flux concentration. |
| links | LINKS | links_foo | misc | R | This is the link structure of a tree containing all the flux concentrations in the simulation. |
| x | POINT3D | x | PDL (3) | RW | The location of the flux concentration. |
| locale_radius | NUM | locale_radius | scalar | RW | This is intended to establish a size parameter for the flux concentration. It has been repurposed - for open boundary conditions, the location of the end flux concentration is regarded as the center of a large spherical boundary, and the locale_radius tells the radius of the sphere. Fluxons that begin or end at the open flux concentration are forced to have their endpoints on the sphere. The endpoint location is determined by allowing the endpoint to move under the curvature and pressure forces, and then projecting it radially onto the surface. |
| passno | long | Temporary variable used in hull routines (or not?) | |||
| bound | void (*bound)(VERTEX *v) | This is a function pointer to the boundary condition routine associated with this flux concentration. See the discussion on boundary conditions for details. |
WORLD
The WORLD represents a simulation arena. It should contain everything that the coders are tempted to make into a global variable. WORLD structures correspond to Perl objects of type Flux::World.
| C name | C type | Perl name | Perl type | Perl R/W? | Description |
| frame_number | long | frame_number | scalar | RW | This is the current elapsed number of relaxation steps in the simulation. It's used as a kind of odometer in current (Summer 2007) runs, constantly incrementing. It will probably one day be replaced with a pair of numbers representing frame number (for relaxation steps) at the current physical time, and a separate number representing the elapsed physical time in the model. |
| state | long | state | long | RW | This is another very underused feature that is intended to be brought up to full utility. It is a number that indicates the current state of the WORLD -- whether it is freshly defined, freshly updated, relaxed, etc. The states are a pseudo-enum handled with a bunch of #define statements in the C code: 0 - NEW; 1 - LOADING; 2 - LOADED; 3 - WORKING; 4 - RELAXED; 5 - READY. The intent is that you should be able to restore a WORLD from disk and know exactly what stage of relaxation it was in when it was saved.
|
| concentrations | FLUX_
CONCENTRATION * | concentrations | Flux::
Concentration | R | This is the root of the tree holding all the flux concentrations. It could conceivably be NULL if the WORLD is empty, but is not generally. |
| lines | FLUXON * | lines | Flux::Fluxon | R | This is the root of the all_links tree holding all the fluxons in the entire simulation. (The tree infrastructure is held in the FLUXON field all_links). It could conceivably be NULL if the WORLD is empty, but is not generally.
|
| vertices | VERTEX * | vertices | Flux::Vertex | R | This is the root of a tree containing all the vertices in the entire simulation (the links are held in the VERTEX field links). It could conceivably be NULL if the WORLD is empty, but is not generally.
|
| photosphere | PHOTOSPHERE | The boundary condition structure for the photosphere. (See the discussion on boundary conditions). | |||
| image
image2 | VERTEX * | image
image2 | Flux::Vertex | R | A workspace for holding image fluxels -- these are constructed on a per-fluxel basis by the geometry code to enforce the photospheric boundary. They should never be null in a well-constructed WORLD. |
| locale_radius | NUM | locale_radius | scalar | RW | Meant to hold the default locale radius for flux concentrations, this field is largely ignored. |
| fc_ob
fc_oe | FLUX_
CONCENTRATION * | fc_ob
fc_oe | Flux::
Concentration | R | These are dummy beginning and ending flux concentrations used for anchoring fluxons that are open. Technically they aren't required as you could make any old flux concentration an open flux concentration and terminate your fluxons at it, but these are recommended for that purpose as they concentrate (heh) all such fluxons in one place. |
| fc_pb
fc_pe | FLUX_
CONCENTRATION * | fc_pb
fc_pe | Flux::
Concentration | R | These are dummy beginning and ending flux concentrations used for anchoring fluxons that are plasmoids. Technically they aren't required as you could make any old flux concentration a "plasmoid" concentration, terminate your fluxons at it, and mark their plasmoid flags -- but these are recommended for that purpose as they concentrate (heh) all such fluxons in one place. |
| verbosity | long | verbosity | scalar | RW | Controls how much diagnostic information is printed during the simulation. Normal operation is at 0; setting it to 6 yields several megabytes of information per timestep with 100 fluxels. |
| f_funcs | ((void *)())[] | This is an array of function pointers to the force (and field) functions used at each step of the simulation. There is as yet no hash-like interface; you must use the force() method to read and/or set them, using a string lookup table of function names. | |||
| rc_funcs | (RC_FUNC *)[] | This is an array of function pointers to the reconnection-test routines in use for the simulations. Simulations can choose several parameterized tests for deciding where and whether reconnection will take place. The parameters are contained in the rc_params array (just below).
| |||
| rc_params | NUM[][]; | This is a holding tank for numeric parameters used by the reconnection-test routines. The first index runs across position in rc_funcs (just above), and the second across parameter index within the function call.
| |||
| step_scale.b_power
step_scale.d_power step_scale.s_power step_scale.ds_power | NUM | scale_b_power
scale_d_power scale_s_power scale_ds_power | scalar | RW | These are scaling powers used in force law/step length calculation. Each corresponding variable is raised to the appropriate power and multiplied by the calculated force. The final product is the step length. The forces are: 'b' - magnetic field; 'd' - distance to nearest neighbor; 's' - stiffness coefficient (<1); 'ds' - relative difference in force between this segment and adjacent ones on the same fluxon. See User Manual: step law for more details. |
| passno | long | passno | scalar | RW | Scratch space for hull algorithms |
| handle_skew | long | handle_skew | scalar | RW | Turns on an experimental distance-scaling function that increases the effective distance between fluxels with a significant skew angle. |
| max_angle
mean_angle | NUM | max_angle
mean_angle | scalar | RW | These are accumulators for keeping track of bend angle characteristics in the simulation. They are updated automatically during a relaxation step. The angles are poorly named: they are not angles in radians but rather (cosθ)2, because that takes many fewer cycles to calculate. |
| dtau | NUM | dtau | scalar | RW | This is the relaxation time step, in units of linear relaxation times near equilibrium. The step size is scaled by dtau after all other factors are taken into account (check User Manual: step law for details). Numbers greater than about 0.3 are generally unstable. If you find yourself turning this up, e.g. late in the simulation, then you've probably screwed up your various power laws (above). |
| coeffs | NUM[] | coeffs | PDL | RW | This is the array of acceleration coefficients (see User Manual: step law) for accelerating large relaxation modes with small eigenvalues. |
| n_coeffs | long | n_coeffs | scalar | RW | This is the number of acceleration coefficients. Set to 0 for non-accelerated relaxation. |
| maxn_coeffs | long | maxn_coeffs | scalar | RW | This is the maximum allowable number of acceleration coefficients. Do not write to this lightly, as it will probably break things. It's primarily there as a way of getting the #defined number MAXNUMCOEFFS out of the C world and into the perl world. |
Example
To see how the various high level structures above link together it is helpful to refer to Laurel's diagram below (click on it for full size; it is 3179x2048 pixels). You can also get it in open-document or PDF formats.
The example WORLD has three flux concentrations, and two fluxons connecting them. Concentrations 7 and 9 each source one fluxon; concentration 8 sinks both of them. Each fluxon contains three vertices. Neighbor relations are not shown.
