<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Compute
Now that we have datasets added to our Bundle, our next step is to run the forward model and compute a synthetic model for each of these datasets.
Setup
Let's first make sure we have the latest version of PHOEBE 2.2 installed. (You can comment out this line if you don't use pip for your installation or don't want to update to the latest release).
Step1:  As always, let's do imports and initialize a logger and a new Bundle.  See the building a system tutorial for more details.
Step2:  And we'll attach some dummy datasets.  See the datasets tutorial for more details.
Step3:  Default Compute Options
Any default Bundle already has a set of default compute options to run the backend for PHOEBE 2.  In most cases, you can just edit the options in this default set of compte options.
Step4:  Adding Compute Options
In other cases, we may want to manually add additional sets of compute options.
This syntax should look very familiar by now, it takes a function (or the name of a recognized function in phoebe.parameters.compute) and then any
kwargs to set in that ParameterSet, passed to b.add_compute.
Let's say that we want to create two sets of compute options - in this example, we'll create one called 'preview' which will cut some corners to quickly get us a model, and one called 'detailed' which will get a much more precise model but likely take longer.  As with other tags, the string you provide for the compute tag is up to you (so long as it doesn't raise an error because it conflicts with other tags).
Step5:  Editing Compute Options
Backend-Specific Compute Options
Most of the parameters in the compute options are specific to the backend being used.  Here, of course, we're using the PHOEBE 2.0 backend - but for details on other backends see the Alternate Backends Tutorial.
The PHOEBE 2.0 compute options are described in the tutorial on their relevant dataset types
Step6:  as you can see, there is a copy for both of our compute options ('preview' and 'detailed').
If we know which set of compute options we'll be using, or only want to enable/disable for a given set, then we can do that
Step7:  or to enable/disable a dataset for all sets of compute options, we can use the set_value_all method
Step8:  If the enabled parameter is missing for a set of compute options - it is likely that that particular backend does not support that dataset type.
Running Compute
Simplest Case
run_compute takes arguments for the compute tag as well as the model tag for the resulting synthetic model(s).  
You do not need to provide the compute tag if only 0 or 1 set of compute options exist in the Bundle.  If there are no compute options, the default PHOEBE 2.0 options will be added on your behalf and used.  If there is a single set of compute options, those will be assumed.  In our case, we have two compute options in the Bundle (with tags 'preview' and 'detailed') so we must provide an argument for compute.
If you do not provide a tag for the model, one will be created for you called 'latest'.  Note that the 'latest' model will be overwritten without throwing any errors, whereas other named models can only be overwritten if you pass overwrite=True (see the run_compute API docs for details).  In general, though, if you want to maintain the results from previous calls to run_compute, you must provide a NEW model tag.
Step9:  Storing Models
Now let's compute models for three different 'versions' of parameters.  By providing a model tag, we can keep the synthetics for each of these different runs in the bundle - which will be handy later on for plotting and comparing models.
Step10:  We will now have three new sets of synthetics which can be compared, plotted, or removed.
Step11:  To remove a model, call remove_model.
Step12:  Overriding Times
times kwarg in run_compute
You can pass times to run_compute to override the times across all datasets.
Step13:  compute_times parameter
NEW in PHOEBE 2.2
Alternatively, you can override the times (or phases) per-dataset at which the model should be computed.  This is particularly useful if you have attached real data (with a large number of data points) but want the model computed at only a subset of times.
Note that the method above (sending times as a keyword argument to run_compute) will take precedence over compute_times.
Step14:  for more details, see the advanced
Step15:  We probably have the same problem with 'lc01', but just didn't get far enough to raise the error.  So let's fix that as well
Step16:  So in this case, 'lc01' will be computed using the options in 'detailed' while 'orb01' will use the options in 'preview'.
Step17:  Accessing Synthetics from Models
The synthetics can be accessed by their dataset and model tags.
Step18:  or of course through method access
<ASSISTANT_TASK:>
Python Code:
!pip install -I "phoebe>=2.2,<2.3"
Explanation: Compute
Now that we have datasets added to our Bundle, our next step is to run the forward model and compute a synthetic model for each of these datasets.
Setup
Let's first make sure we have the latest version of PHOEBE 2.2 installed. (You can comment out this line if you don't use pip for your installation or don't want to update to the latest release).
End of explanation
import phoebe
from phoebe import u # units
import numpy as np
import matplotlib.pyplot as plt
logger = phoebe.logger()
b = phoebe.default_binary()
Explanation: As always, let's do imports and initialize a logger and a new Bundle.  See the building a system tutorial for more details.
End of explanation
b.add_dataset(phoebe.dataset.orb, compute_times=np.linspace(0,10,10), dataset='orb01', component=['primary', 'secondary'])
times, fluxes, sigmas = np.loadtxt('test.lc.in', unpack=True)
# test.lc.in has 1000 datapoints... let's use every 10 just for brevity
times, fluxes, sigmas = times[::10], fluxes[::10], sigmas[::10]
b.add_dataset(phoebe.dataset.lc, times=times, fluxes=fluxes, sigmas=sigmas, dataset='lc01')
Explanation: And we'll attach some dummy datasets.  See the datasets tutorial for more details.
End of explanation
print(b.computes)
print(b.filter(context='compute'))
b.set_value('irrad_method', 'none')
Explanation: Default Compute Options
Any default Bundle already has a set of default compute options to run the backend for PHOEBE 2.  In most cases, you can just edit the options in this default set of compte options.
End of explanation
b.add_compute(phoebe.compute.phoebe, compute='preview', irrad_method='none')
print(b['preview@compute'])
b.add_compute('phoebe', compute='detailed', irrad_method='wilson')
print(b.get_compute('detailed'))
Explanation: Adding Compute Options
In other cases, we may want to manually add additional sets of compute options.
This syntax should look very familiar by now, it takes a function (or the name of a recognized function in phoebe.parameters.compute) and then any
kwargs to set in that ParameterSet, passed to b.add_compute.
Let's say that we want to create two sets of compute options - in this example, we'll create one called 'preview' which will cut some corners to quickly get us a model, and one called 'detailed' which will get a much more precise model but likely take longer.  As with other tags, the string you provide for the compute tag is up to you (so long as it doesn't raise an error because it conflicts with other tags).
End of explanation
print(b['enabled@lc01'])
Explanation: Editing Compute Options
Backend-Specific Compute Options
Most of the parameters in the compute options are specific to the backend being used.  Here, of course, we're using the PHOEBE 2.0 backend - but for details on other backends see the Alternate Backends Tutorial.
The PHOEBE 2.0 compute options are described in the tutorial on their relevant dataset types:
Light Curves/Fluxes (lc)
Radial Velocities (rv)
Line Profiles (lp)
Orbits (orb)
Meshes (mesh)
Enabling/Disabling Datasets
By default, synthetic models will be created for all datasets in the Bundle when run_compute is called.  But you can disable a dataset to have run_compute ignore that dataset.  This is handled by a BoolParameter with the qualifier 'enabled' - and has a copy that lives in each set of compute options
Let's say we wanted to compute the orbit but not light curve - so we want to set enabled@lc01:
End of explanation
b['enabled@lc01@preview'] = False
print(b['enabled@lc01'])
Explanation: as you can see, there is a copy for both of our compute options ('preview' and 'detailed').
If we know which set of compute options we'll be using, or only want to enable/disable for a given set, then we can do that:
End of explanation
b.set_value_all('enabled@lc01', True)
print(b['enabled@lc01'])
Explanation: or to enable/disable a dataset for all sets of compute options, we can use the set_value_all method:
End of explanation
b.run_compute(compute='preview')
print(b.models)
Explanation: If the enabled parameter is missing for a set of compute options - it is likely that that particular backend does not support that dataset type.
Running Compute
Simplest Case
run_compute takes arguments for the compute tag as well as the model tag for the resulting synthetic model(s).  
You do not need to provide the compute tag if only 0 or 1 set of compute options exist in the Bundle.  If there are no compute options, the default PHOEBE 2.0 options will be added on your behalf and used.  If there is a single set of compute options, those will be assumed.  In our case, we have two compute options in the Bundle (with tags 'preview' and 'detailed') so we must provide an argument for compute.
If you do not provide a tag for the model, one will be created for you called 'latest'.  Note that the 'latest' model will be overwritten without throwing any errors, whereas other named models can only be overwritten if you pass overwrite=True (see the run_compute API docs for details).  In general, though, if you want to maintain the results from previous calls to run_compute, you must provide a NEW model tag.
End of explanation
b.set_value('incl@orbit', 90)
b.run_compute(compute='preview', model='run_with_incl_90') 
b.set_value('incl@orbit', 85)
b.run_compute(compute='preview', model='run_with_incl_85')
b.set_value('incl@orbit', 80)
b.run_compute(compute='preview', model='run_with_incl_80')
Explanation: Storing Models
Now let's compute models for three different 'versions' of parameters.  By providing a model tag, we can keep the synthetics for each of these different runs in the bundle - which will be handy later on for plotting and comparing models.
End of explanation
print(b.models)
Explanation: We will now have three new sets of synthetics which can be compared, plotted, or removed.
End of explanation
b.remove_model('latest')
print(b.models)
Explanation: To remove a model, call remove_model.
End of explanation
b.run_compute(compute='preview', 
              times=[0,0.1,0.2], 
              model='override_times')
print("dataset times: {}\nmodel times: {}".format(
    b.get_value('times', dataset='lc01', context='dataset'),
    b.get_value('times', dataset='lc01', model='override_times')))
Explanation: Overriding Times
times kwarg in run_compute
You can pass times to run_compute to override the times across all datasets.
End of explanation
b.set_value('compute_times', dataset='lc01', value=[0, 0.2, 0.4])
b.run_compute(compute='preview',  
              model='override_compute_times')
print("dataset times: {}\ndataset compute_times: {}\ndataset compute_phases: {}\n model times: {}".format(
    b.get_value('times', dataset='lc01', context='dataset'),
    b.get_value('compute_times', dataset='lc01', context='dataset'),
    b.get_value('compute_phases', dataset='lc01', context='dataset'),
    b.get_value('times', dataset='lc01', model='override_compute_times')))
Explanation: compute_times parameter
NEW in PHOEBE 2.2
Alternatively, you can override the times (or phases) per-dataset at which the model should be computed.  This is particularly useful if you have attached real data (with a large number of data points) but want the model computed at only a subset of times.
Note that the method above (sending times as a keyword argument to run_compute) will take precedence over compute_times.
End of explanation
print(b['enabled@orb01'])
b.set_value_all('enabled@orb01@detailed', False)
b.set_value_all('enabled@orb01@preview', True)
print(b['enabled@orb01'])
Explanation: for more details, see the advanced: compute times & phases tutorial.
Running Compute with Multiple Sets of Options
So far we've seen how setting up different sets of compute options can be handy - 'preview' vs 'detailed', for example.  But there could also be situations where you want to use different sets of options per dataset.  Perhaps you have a high-precision follow-up light curve of an eclipse along with a lower-precision light curve over a longer time baseline.  So here you'd want to run 'detailed' on the high-precision light curve, but 'preview' on the lower-precision light curve.
You could of course call run_compute twice and create two separate models - but that isn't always convenient and will be a problem in the future when we want to fit data.
Instead we can send a list of compute options to run_compute.
A given dataset can only be enabled in up to 1 of the compute options we're sending to run_compute.  So let's take care of that first (if we don't, we'd get an error when trying to call run_compute):
End of explanation
print(b['enabled@lc01'])
b.set_value_all('enabled@lc01@detailed', True)
b.set_value_all('enabled@lc01@preview', False)
print(b['enabled@lc01'])
Explanation: We probably have the same problem with 'lc01', but just didn't get far enough to raise the error.  So let's fix that as well
End of explanation
b.run_compute(compute=['detailed', 'preview'], model='multiplecompute')
print(b.models)
Explanation: So in this case, 'lc01' will be computed using the options in 'detailed' while 'orb01' will use the options in 'preview'.
End of explanation
b['run_with_incl_90']
b['primary@run_with_incl_90']
b['us@primary@run_with_incl_90']
Explanation: Accessing Synthetics from Models
The synthetics can be accessed by their dataset and model tags.
End of explanation
print(b.get_value(qualifier='us', dataset='orb01', component='primary', model='run_with_incl_90')[:10])
Explanation: or of course through method access:
End of explanation
<END_TASK>