Let's first make sure we have the latest version of PHOEBE 2.3 installed (uncomment this line if running in an online notebook session such as colab).
In [1]:
#!pip install -I "phoebe>=2.3,<2.4"
In [2]:
import phoebe
from phoebe import u # units
import numpy as np
import matplotlib.pyplot as plt
logger = phoebe.logger()
b = phoebe.Bundle()
Although the default empty Bundle doesn't include a system, there are available constructors that create default systems. To create a simple binary with component tags 'binary', 'primary', and 'secondary' (as above), you could call default_binary:
In [3]:
b = phoebe.Bundle.default_binary()
or for short:
In [4]:
b = phoebe.default_binary()
In [5]:
print(b.hierarchy)
To build the same binary but as a contact system, you would call:
In [6]:
b = phoebe.default_binary(contact_binary=True)
In [7]:
print(b.hierarchy)
For more details on dealing with contact binary systems, see the Contact Binary Hierarchy Tutorial and the Contact Binary Example Script.
By default, an empty Bundle does not contain any information about our system.
So, let's first start by adding a few stars. Here we'll call the generic add_component method. This method works for any type of component in the system - stars, orbits, planets, disks, rings, spots, etc. The first argument needs to be a callable or the name of a callable in phoebe.parameters.component which include the following options:
add_component also takes a keyword argument for the 'component' tag. Here we'll give them component tags 'primary' and 'secondary' - but note that these are merely convenience labels and do not hold any special roles. Some tags, however, are forbidden if they clash with other tags or reserved values - so if you get error stating the component tag is forbidden, try using a different string.
In [8]:
b = phoebe.Bundle()
In [9]:
b.add_component(phoebe.component.star, component='primary')
b.add_component('star', component='secondary')
Out[9]:
In [10]:
b.add_star('extrastarforfun', teff=6000)
Out[10]:
Here we call the add_component method of the bundle with several arguments:
and then we'll do the same to add an orbit:
In [11]:
b.add_orbit('binary')
Out[11]:
At this point all we've done is add a bunch of Parameters to our Bundle, but we still need to specify the hierarchical setup of our system.
Here we want to place our two stars (with component tags 'primary' and 'secondary') in our orbit (with component tag 'binary'). This can be done with several different syntaxes sent to b.set_hierarchy:
In [12]:
b.set_hierarchy(phoebe.hierarchy.binaryorbit, b['binary'], b['primary'], b['secondary'])
or
In [13]:
b.set_hierarchy(phoebe.hierarchy.binaryorbit(b['binary'], b['primary'], b['secondary']))
If you access the value that this set via get_hierarchy, you'll see that it really just resulted in a simple string representation:
In [14]:
b.get_hierarchy()
Out[14]:
We could just as easily have used this string to set the hierarchy:
In [15]:
b.set_hierarchy('orbit:binary(star:primary, star:secondary)')
If at any point we want to flip the primary and secondary components or make this binary a triple, its seriously as easy as changing this hierarchy and everything else will adjust as needed (including cross-ParameterSet constraints, and datasets)
In [16]:
b['hierarchy@system']
Out[16]:
or through any of these shortcuts:
In [17]:
b.get_hierarchy()
Out[17]:
In [18]:
b.hierarchy
Out[18]:
This HierarchyParameter then has several methods unique to itself. You can, for instance, list the component tags of all the stars or orbits in the hierarchy via get_stars or get_orbits, respectively:
In [19]:
print(b.hierarchy.get_stars())
In [20]:
print(b.hierarchy.get_orbits())
Or you can ask for the component tag of the top-level item in the hierarchy via get_top.
In [21]:
print(b.hierarchy.get_top())
And request the parent, children, child, or sibling of any item in the hierarchy via get_parent_of, get_children_of, or get_sibling_of.
In [22]:
print(b.hierarchy.get_parent_of('primary'))
In [23]:
print(b.hierarchy.get_children_of('binary'))
In [24]:
print(b.hierarchy.get_child_of('binary', 0)) # here 0 means primary component, 1 means secondary
In [25]:
print(b.hierarchy.get_sibling_of('primary'))
We can also check whether a given component (by component tag) is the primary or secondary component in its parent orbit via get_primary_or_secondary. Note that here its just a coincidence (although on purpose) that the component tag is also 'secondary'.
In [26]:
print(b.hierarchy.get_primary_or_secondary('secondary'))