7.3. Choosing Ingredients
Alice: Okay, let us be more precise. We can think of passing data
across the interface, or of making data available at the interface.
Notice how the first formulation implies an active version, in the
form of a question and answer type of handshaking, and the second one
is more passive, like looking something up in memory.
Clearly, a lot of thought, as well as experimentation, will have to go
into the writing of a detailed interface specification. But for now,
to give a more concrete idea, let us take the first model, in terms of
handshaking. We also have to say which side initiates the handshake.
Let us put that responsibility for now at the side of the stellar
dynamics module. It can ask a particle to evolve its internal state,
or tell it that it is no longer needed, in the case of a merger.
We can find out what type of instructions should be passed through the
interface, by looking at the physical requirement of the system that
we are modeling. Let us make a list of what the stellar dynamics module
would like to have. It needs to tell a star to evolve itself for a certain
amount of time; it needs to tell a star to destroy itself, in a collision;
and it needs to ask a star for its mass and radius. That already
gives us a list of four necessary functions:
-
a star evolution function
-
a star destruction function
-
a function providing the mass of a star
-
a function providing the radius of a star
Bob: So these will be subroutine calls from the SD module, which can
be evaluated by the SE module, after both modules have been compiled and
linked, correct? I like to translate your abstract reasoning in concrete
terms that I am familiar with.
Alice: Yes, you can think of them as subroutines. You are not allowed
to add more arguments, unless you start a new version of the software,
and document how and why you made that particular change. And under
no circumstances are common blocks allowed in the interface.
Bob: Your list is far from complete. Don't you need a star constructor
as well?
Alice: Perhaps. I was thinking that you start the N-body calculations
with as input initial conditions that come from a file, or directly from
another program. Presumably that other program knows how to create stars.
Bob: But after a merger occurs, what do you do? You can destroy one
of the two stars, changing the other star into the merger, but that
would be rather arbitrary. Wouldn't it be better to destroy both of
the pre-merger stars, and create a new star instead, for the merger
product?
Alice: Yes, you are right, that is much cleaner. See, you are starting
to think in a more modular way already!
Bob: Hmm, you still haven't defined exactly what modular means, but I
won't push for yet more specifications. Coming back to the functions,
we also need a way to pass time information. Even though your nifty
scheduler module will give wake up calls, I presume that we need to
have a local way within each module to keep track of time. And then
there is a question of synchronizing that time, or at least to know
whether the information you get across this interface barrier is up to
date or slightly out of date, stemming from the last time step.
Alice: I agree. So we already get six functions then:
-
a star creation function
-
a star evolution function
-
a star destruction function
-
a function providing the mass of a star
-
a function providing the radius of a star
-
a function providing the current time for a star
Are you happy with this list?
Bob: From an astrophysical point of view, this is still not enough.
The reason that mergers are interesting is that they evolve in a very
different way from ZAMS stars. A merger product will be far from
thermal equilibrium, it will most likely be in a state of high
rotation, and there will be strong gradients in chemical composition.
In order to convey all this information, your interface specification
will have to include sets of tables and functions with many parameters!
Alice: For the time being, while we are developing our toy model,
we do not have to take all those effects into account. After we
reach a stable state, we can give our software a version number 1.0,
say, we can then update the interface specification in order to
include more physics.
Also, most of the complexities you talk about are a matter of
information transfer between the SE and SH modules, and the SD module
doesn't have to know those details.
Bob: Fine, as long as it is understood that we will have to widen up
those interfaces significantly later on, I'm happy to stay with the toy
interface. But even so, we need to convey at least some information about
chemical composition, to show that mergers have higher helium content,
and higher metallicity in general, compared to freshly born stars. We
don't need to worry about too many details, perhaps but at the very
least we need to communicate Y and Z, helium and metallicity
abundance.
7.4. An Interface Specification
Alice: Fine! So we get a list of eight functions then:
-
a star creation function
-
a star evolution function
-
a star destruction function
-
a function providing the mass of a star
-
a function providing the radius of a star
-
a function providing the current time for a star
-
a function providing the total helium fraction of a star
-
a function providing the total metallicity of a star
And since you asked for more concreteness in the examples, we can
write the specifications for these functions in Fortran. Similar
specifications can be prescribed for other languages, such as C or
C++, and in many cases the interface will be used to connect two
modules that may be written, say, in Fortran and C++. However, it is
convenient to choose one language in which to give the specification.
The alternative would be to specify the data passing on the byte level.
Bob: And in that case you would have to worry about architecture
dependencies, such as big-endian versus little-endian byte ordering
in integers and floating point numbers. But there would be much more
involved. The subroutine passing would have to be specified on the
level of assembly language, and that would in turn be architecture
dependent. So yes, it is almost unavoidable to pick a particular
language. Which language shall we choose? Why do you prefer Fortran;
wouldn't C be more natural, and closer to the machine architecture?
Alice: The choice of language deserves a separate discussion; let's
do that in a moment, for our toy model. For now, just to give you a
concrete example of an interface specification, let's take Fortran.
Starting with the first function on our list, here is how we can
create a star.
The function
integer function
CreateStar(M, Y, Z)
accepts as arguments real*8 variables for the initial mass,
the helium abundance and metallicity of a star created at the zero age
main sequence. The return value is a unique integer that acts as the
identifier for the particular star that has been created. A negative
return value will signal an error (e.g. not enough storage left;
unreasonable initial conditions provided; or some other internal error
in the stellar evolution module).
Note that we don't have to give the position and velocity of the new
star to the stellar evolution module. That is none of its business;
it is information that the stellar dynamics module keeps track of.
Position and velocity do not even have any meaning within the stellar
evolution part of the software.
Bob: We'd better specify the units for the variables as well. How
about:
M in solar masses
Y helium abundance fraction by weight; 0 <= Y <= 1
Z metallicity abundance by weight; 0 <= Y + Z <= 1
Alice: Fine. Moving right along to the second function, we can write:
real*8 function
EvolveStar(id, dtmax, dMmax,
dRmax, dYmax, dZmax)
for a function that accepts as first argument an integer variable for
the identifier id, followed by five real*8 variables
that determine halting criteria. The stellar evolution code will
start evolving the star, from the current time tnow, and it
will stop as soon as one of the following halting criteria is satisfied:
-
if the time exceeds that of the specified period: t >= tnow + dtmax;
-
if the mass M obeys | M - Mnow | > dMmax;
-
if the radius R obeys | R - Rnow | > dRmax;
-
if the helium fraction Y obeys | Y - Ynow | > dYmax;
-
if the metallicity Z obeys | Z - Znow | > dZmax.
This function then returns the new time tnew; a negative value for
tnew indicates an error condition.
Bob: And we need additional units, for example:
t in millions of years
R in solar radii
Okay, that all gives me a better idea of what you had in mind. No
need right now to write down the other six functions.