Advanced: Optimizing Performance with PHOEBE

Setup

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 [ ]:
#!pip install -I "phoebe>=2.3,<2.4"

In [1]:
import phoebe


PHOEBE: passband "Bolometric:900-40000" has a newer version available.  Run phoebe.update_passband("Bolometric:900-40000") or phoebe.update_all_passbands() to update.

In [2]:
b = phoebe.default_binary()

Interactivity Options

When running in an interactive Python session, PHOEBE updates all constraints and runs various checks after each command. Although this is convenient, it does take some time, and it can sometimes be advantageous to disable this to save computation time.

Interactive Checks

By default, interactive checks are enabled when PHOEBE is being run in an interactive session (either an interactive python, IPython, or Jupyter notebook session), but disabled when PHOEBE is run as a script directly from the console. When enabled, PHOEBE will re-run the system checks after every single change to the bundle, raising warnings via the logger as soon as they occur.

This default behavior can be changed via phoebe.interactive_checks_on() or phoebe.interactive_checks_off(). The current value can be accessed via phoebe.conf.interactive_checks.


In [3]:
print(phoebe.conf.interactive_checks)


True

In [4]:
phoebe.interactive_checks_off()

In [5]:
print(phoebe.conf.interactive_checks)


False

If disabled, you can always manually run the checks via b.run_checks().


In [6]:
print(b.run_checks())


Run Checks Report: PASS


In [7]:
b.set_value('requiv', component='primary', value=50)

In [8]:
print(b.run_checks())


Run Checks Report: FAIL
ERROR: primary is overflowing at periastron (requiv=50.0, requiv_max=2.013275176537638).  Use contact model if overflowing is desired. (3 affected parameters)
ERROR: triangles on ['primary'] may be larger than the entire bodies of ['secondary'], resulting in inaccurate eclipse detection.  Check values for requiv of ['secondary'] and/or ntriangles of ['primary'].  If your system is known to NOT eclipse, you can set eclipse_method to 'only_horizon' to circumvent this check. (4 affected parameters)

Interactive Constraints

By default, interactive constraints are always enabled in PHOEBE, unless explicitly disabled. Whenever a value is changed in the bundle that affects the value of a constrained value, that constraint is immediately executed and all applicable values updated. The ensures that all constrained values are "up-to-date".

If disabled, constraints are delayed and only executed when needed by PHOEBE (when calling run_compute, for example). This can save significant time, as each value that needs updating only needs to have its constraint executed once, instead of multiple times.

This default behavior can be changed via phoebe.interactive_constraints_on() or phoebe.interactive_constraints_off(). The current value can be accessed via phoebe.conf.interactive_constraints.

Let's first look at the default behavior with interactive constraints on.


In [9]:
print(phoebe.conf.interactive_constraints)


True

In [10]:
print(b.filter('mass', component='primary'))


ParameterSet: 2 parameters
*         mass@primary@component: 0.9988131358058302 solMass
         mass@primary@constraint: (39.478418 * ({sma@binary@component} ** 3.000000)) / ((({period@binary@component} ** 2.000000) * (1.000000 + {q@binary@component})) * 2942.206217504418873431859537959099)

In [11]:
b.set_value('sma@binary', 10)

In [12]:
print(b.filter('mass', component='primary'))


ParameterSet: 2 parameters
*         mass@primary@component: 6.708982151748292 solMass
         mass@primary@constraint: (39.478418 * ({sma@binary@component} ** 3.000000)) / ((({period@binary@component} ** 2.000000) * (1.000000 + {q@binary@component})) * 2942.206217504418873431859537959099)

Note that the mass has already updated, according to the constraint, when the value of the semi-major axes was changed. If we disable interactive constraints this will not be the case.


In [13]:
phoebe.interactive_constraints_off()

In [14]:
print(phoebe.conf.interactive_constraints)


False

In [15]:
print(b.filter('mass', component='primary'))


ParameterSet: 2 parameters
*         mass@primary@component: 6.708982151748292 solMass
         mass@primary@constraint: (39.478418 * ({sma@binary@component} ** 3.000000)) / ((({period@binary@component} ** 2.000000) * (1.000000 + {q@binary@component})) * 2942.206217504418873431859537959099)

In [16]:
b.set_value('sma@binary', 15)

In [17]:
print(b.filter('mass', component='primary'))


ParameterSet: 2 parameters
*         mass@primary@component: 6.708982151748292 solMass
         mass@primary@constraint: (39.478418 * ({sma@binary@component} ** 3.000000)) / ((({period@binary@component} ** 2.000000) * (1.000000 + {q@binary@component})) * 2942.206217504418873431859537959099)

No need to worry though - all constraints will be run automatically before passing to the backend. If you need to access the value of a constrained parameter, you can explicitly ask for all delayed constraints to be executed via b.run_delayed_constraints().


In [18]:
b.run_delayed_constraints()


Out[18]:
[<Parameter: asini=15.0 solRad | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: mass=22.642814762150483 solMass | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: sma=7.5 solRad | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: requiv_max=5.697948612842372 solRad | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: mass=22.642814762150483 solMass | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: sma=7.5 solRad | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: requiv_max=5.697948612842372 solRad | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: logg=2.395058032299506 | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: mass=22.642814762150483 solMass | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: logg=5.792998040971543 | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>,
 <Parameter: mass=22.642814762150483 solMass | keys: description, value, quantity, default_unit, limits, visible_if, copy_for, advanced>]

In [19]:
print(b.filter('mass', component='primary'))


ParameterSet: 2 parameters
*         mass@primary@component: 22.642814762150483 solMass
         mass@primary@constraint: (39.478418 * ({sma@binary@component} ** 3.000000)) / ((({period@binary@component} ** 2.000000) * (1.000000 + {q@binary@component})) * 2942.206217504418873431859537959099)

In [20]:
phoebe.reset_settings()

Filtering Options

check_visible

By default, everytime you call filter or set_value, PHOEBE checks to see if the current value is visible (meaning it is relevant given the value of other parameters). Although not terribly expensive, these checks can add up... so disabling these checks can save time. Note that these are automatically temporarily disabled during run_compute. If disabling these checks, be aware that changing the value of some parameters may have no affect on the resulting computations. You can always manually check the visibility/relevance of a parameter by calling parameter.is_visible.

This default behavior can be changed via phoebe.check_visible_on() or phoebe.check_visible_off().

Let's first look at the default behavior with check_visible on.


In [21]:
b.add_dataset('lc')


Out[21]:
<ParameterSet: 43 parameters | contexts: constraint, compute, dataset, figure>

In [22]:
print(b.get_dataset())


ParameterSet: 19 parameters
              times@lc01@dataset: [] d
             fluxes@lc01@dataset: [] W / m2
           passband@lc01@dataset: Johnson:V
   intens_weighting@lc01@dataset: energy
*               ebv@lc01@dataset: 0.0
                 Av@lc01@dataset: 0.0
                 Rv@lc01@dataset: 3.1
      compute_times@lc01@dataset: [] d
*    compute_phases@lc01@dataset: []
  compute_phases_t0@lc01@dataset: t0_supconj
             sigmas@lc01@dataset: [] W / m2
         pblum_mode@lc01@dataset: component-coupled
    pblum_component@lc01@dataset: primary
            l3_mode@lc01@dataset: flux
                 l3@lc01@dataset: 0.0 W / m2
            exptime@lc01@dataset: 0.0 s
    ld_mode@primary@lc01@dataset: interp
  ld_mode@secondary@lc01@dataset: interp
      pblum@primary@lc01@dataset: 12.566370614359172 W

Now if we disable check_visible, we'll see the same thing as if we passed check_visible=False to any filter call.


In [23]:
phoebe.check_visible_off()

In [24]:
print(b.get_dataset())


ParameterSet: 29 parameters
              times@lc01@dataset: [] d
             fluxes@lc01@dataset: [] W / m2
           passband@lc01@dataset: Johnson:V
   intens_weighting@lc01@dataset: energy
*               ebv@lc01@dataset: 0.0
                 Av@lc01@dataset: 0.0
                 Rv@lc01@dataset: 3.1
      compute_times@lc01@dataset: [] d
*    compute_phases@lc01@dataset: []
  compute_phases_t0@lc01@dataset: t0_supconj
             sigmas@lc01@dataset: [] W / m2
         pblum_mode@lc01@dataset: component-coupled
    pblum_component@lc01@dataset: primary
      pblum_dataset@lc01@dataset: 
             pbflux@lc01@dataset: 1.0 W / m2
            l3_mode@lc01@dataset: flux
                 l3@lc01@dataset: 0.0 W / m2
            l3_frac@lc01@dataset: 0.0
            exptime@lc01@dataset: 0.0 s
    ld_mode@primary@lc01@dataset: interp
  ld_mode@secondary@lc01@dataset: interp
    ld_func@primary@lc01@dataset: logarithmic
  ld_func@secondary@lc01@dataset: logarithmic
  ld_coeffs_source@primary@lc...: auto
  ld_coeffs_source@secondary@...: auto
  ld_coeffs@primary@lc01@dataset: [0.5 0.5]
  ld_coeffs@secondary@lc01@da...: [0.5 0.5]
      pblum@primary@lc01@dataset: 12.566370614359172 W
    pblum@secondary@lc01@dataset: 12.566370614359172 W

Now the same filter is returning additional parameters. For example, ld_coeffs_source parameters were initially hidden because ld_mode is set to 'interp'. We can see the rules that are being followed:


In [25]:
print(b.get_parameter(qualifier='ld_coeffs_source', component='primary').visible_if)


ld_mode:lookup

and can still manually check to see that it shouldn't be visible (isn't currently relevant given the value of ld_func):


In [26]:
print(b.get_parameter(qualifier='ld_coeffs_source', component='primary').is_visible)


False

In [27]:
phoebe.reset_settings()

check_default

Similarly, PHOEBE automatically excludes any parameter which is tagged with a '_default' tag. These parameters exist to provide default values when a new component or dataset are added to the bundle, but can usually be ignored, and so are excluded from any filter calls. Although not at all expensive, this too can be disabled at the settings level or by passing check_default=False to any filter call.

This default behavior can be changed via phoebe.check_default_on() or phoebe.check_default_off().


In [28]:
print(b.get_dataset())


ParameterSet: 19 parameters
              times@lc01@dataset: [] d
             fluxes@lc01@dataset: [] W / m2
           passband@lc01@dataset: Johnson:V
   intens_weighting@lc01@dataset: energy
*               ebv@lc01@dataset: 0.0
                 Av@lc01@dataset: 0.0
                 Rv@lc01@dataset: 3.1
      compute_times@lc01@dataset: [] d
*    compute_phases@lc01@dataset: []
  compute_phases_t0@lc01@dataset: t0_supconj
             sigmas@lc01@dataset: [] W / m2
         pblum_mode@lc01@dataset: component-coupled
    pblum_component@lc01@dataset: primary
            l3_mode@lc01@dataset: flux
                 l3@lc01@dataset: 0.0 W / m2
            exptime@lc01@dataset: 0.0 s
    ld_mode@primary@lc01@dataset: interp
  ld_mode@secondary@lc01@dataset: interp
      pblum@primary@lc01@dataset: 12.566370614359172 W

In [29]:
print(b.get_dataset(check_default=False))


ParameterSet: 20 parameters
              times@lc01@dataset: [] d
             fluxes@lc01@dataset: [] W / m2
   ld_mode@_default@lc01@dataset: interp
           passband@lc01@dataset: Johnson:V
   intens_weighting@lc01@dataset: energy
*               ebv@lc01@dataset: 0.0
                 Av@lc01@dataset: 0.0
                 Rv@lc01@dataset: 3.1
      compute_times@lc01@dataset: [] d
*    compute_phases@lc01@dataset: []
  compute_phases_t0@lc01@dataset: t0_supconj
             sigmas@lc01@dataset: [] W / m2
         pblum_mode@lc01@dataset: component-coupled
    pblum_component@lc01@dataset: primary
            l3_mode@lc01@dataset: flux
                 l3@lc01@dataset: 0.0 W / m2
            exptime@lc01@dataset: 0.0 s
    ld_mode@primary@lc01@dataset: interp
  ld_mode@secondary@lc01@dataset: interp
      pblum@primary@lc01@dataset: 12.566370614359172 W

In [30]:
phoebe.check_default_off()

In [31]:
print(b.get_dataset())


ParameterSet: 20 parameters
              times@lc01@dataset: [] d
             fluxes@lc01@dataset: [] W / m2
   ld_mode@_default@lc01@dataset: interp
           passband@lc01@dataset: Johnson:V
   intens_weighting@lc01@dataset: energy
*               ebv@lc01@dataset: 0.0
                 Av@lc01@dataset: 0.0
                 Rv@lc01@dataset: 3.1
      compute_times@lc01@dataset: [] d
*    compute_phases@lc01@dataset: []
  compute_phases_t0@lc01@dataset: t0_supconj
             sigmas@lc01@dataset: [] W / m2
         pblum_mode@lc01@dataset: component-coupled
    pblum_component@lc01@dataset: primary
            l3_mode@lc01@dataset: flux
                 l3@lc01@dataset: 0.0 W / m2
            exptime@lc01@dataset: 0.0 s
    ld_mode@primary@lc01@dataset: interp
  ld_mode@secondary@lc01@dataset: interp
      pblum@primary@lc01@dataset: 12.566370614359172 W

In [32]:
phoebe.reset_settings()

Passband Options

PHOEBE automatically fetches necessary tables from tables.phoebe-project.org. By default, only the necessary tables for each passband are fetched (except when calling download_passband manually) and the fits files are fetched uncompressed.

For more details, see the API docs on download_passband and update_passband as well as the passband updating tutorial.

The default values mentioned in the API docs for content and gzipped can be exposed via phoebe.get_download_passband_defaults and changed via phoebe.set_download_passband_defaults. Note that setting gzipped to True will minimize file storage for the passband files and will result in faster download speeds, but take significantly longer to load by PHOEBE as they have to be uncompressed each time they are loaded. If you have a large number of installed passbands, this could significantly slow importing PHOEBE.


In [34]:
phoebe.get_download_passband_defaults()


Out[34]:
{'content': 'all', 'gzipped': False}

Environment Variables

Some settings cannot be changed after importing PHOEBE, so they are available via environment variables.