<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Servers
Step1:  PHOEBE makes use of crimpl as a built-in depedency to manage submitting and monitoring jobs on external compute resources.  In order to make use of one of these server, you must first configure the server within crimpl (this only needs to be done once, after which the name of the crimpl server can be referenced from within PHOEBE on your local machine).
The available server types include
Step2:  The name provided when saving the crimpl configuration is the same name that we will reference later in PHOEBE as crimpl_name.  This keeps all the server configuration and potential sensitive information out of the bundle or so that the bundle (or notebook) can safely be saved and shared, while also not requiring the servers to be re-configured in each case.  If crimpl_name is not available from crimpl on the local machine, an error will be raised and the server can easily be configured or swapped.
Servers in the Bundle
Servers in the bundle are added and managed as any other set of options via b.add_server and with "kinds" for each of the supported crimpl server types listed above
Step3:  In this case, we'll skip having crimpl install the necessary dependencies, as we know they are already installed.  If running PHOEBE in a conda environment locally, you could set use_conda=True and reference the same conda environment in the then-exposed conda_env parameter.
Step4:  use_server parameters
compute and solver options both have use_server parameters that will then dictate how b.run_compute and b.run_solver should handle running on external resources.  
If 'none' (as by default), the job is run in the current thread.  use_server in solver options are set to 'compute' by default, which means they will fallback on the use_server parameter in the referenced compute options.
Step5:  So in this case, if running the nm_solver, PHOEBE will use the solver options for the phoebe01 compute options.
Step6:  As with any other parameter in compute or solver options, these can either be set or overridden as keyword arguments when calling b.run_compute and b.run_solver, respectively.
Running External Jobs with crimpl
There are several different use-cases for running compute or solver jobs on external servers
1. wait and load results automatically
As always, we can send kwargs to run_compute or run_solver to override any compute/solver options.  Here we can pass the server tag (note
Step7:  2. detach and monitor/retrieve results
For long jobs, we can submit the job and immediately detach from run_compute or run_solver.  This results in a a "Job Parameter" in the model or solution holding the necessary information to check the progress and retrieve the results at any time in the future.
Step8:  We can now (optionally) save the bundle, turn off our local machine, start a new python session, load the bundle, and all necessary information to check the job's status and retrieve results are handled automatically (with job information in the detached_job parameter and server setup/credentials in the crimpl configuration to avoid any security concerns when sharing bundles).
Step9:  Depending on how long we expect the job to take, we have a number of available methods we can call to act on this detached job parameter
Step10:  3. export script and submit manually
And lastly, for cases where we want to run the job later or from a different machine, we can call b.export_compute (or b.export_solver).  If use_server is 'none', this exports the python script directly.  If use_server is not 'none', this exports a script which accepts the following command-line options
Step11:  Note that the top of this file includes the crimpl options themselves, allowing the script to easily be modified to change the compute resources (particularly handy if sharing this file with a collaborator).
Step12:  If we just call the exported script, we'll see that it provides the available command-line arguments.
Step13:  We can now use these command line arguments to submit the script, check its status, wait for it to complete, and retrieve any output files.
Step14:  Exporting External Jobs without crimpl
Alternatively, you can always call b.export_compute (or b.export_solver) with use_server='none', in which case the returned script does not use crimpl at all but rather can be manually copied to any machine, run or submit to any type of scheduler, and have the results manually copied back and imported.  If the implemented crimpl configurations do not meet your setup or you would just rather have fine control over the submission of the script, then this is your best bet.
In this case, you can still save and load the bundle and import the model or solution - but you're responsible for keeping track of filenames, dependencies, and monitoring the status of external threads.
<ASSISTANT_TASK:>
Python Code:
#!pip install -I "phoebe>=2.4,<2.5"
import phoebe
from phoebe import u # units
import numpy as np
logger = phoebe.logger('error')
b = phoebe.default_binary()
b.add_dataset('lc', compute_times=phoebe.linspace(0,1,501))
b.add_solver('optimizer.nelder_mead', solver='nm_solver')
Explanation: Servers: Running Jobs on External Compute Resources
Setup
Let's first make sure we have the latest version of PHOEBE 2.4 installed (uncomment this line if running in an online notebook session such as colab).
End of explanation
from phoebe.dependencies import crimpl
s = crimpl.LocalThreadServer()
print(s)
s.save('local', overwrite=True)
Explanation: PHOEBE makes use of crimpl as a built-in depedency to manage submitting and monitoring jobs on external compute resources.  In order to make use of one of these server, you must first configure the server within crimpl (this only needs to be done once, after which the name of the crimpl server can be referenced from within PHOEBE on your local machine).
The available server types include:
* LocalThread
* RemoteSlurm
As the configuration for remote servers can differ significantly and to keep this example working for all users, we'll initialize a LocalThread server in order to run our compute and solver jobs within MPI while running the rest of the commands in this notebook on a single processor.  Besides the crimpl configuration itself and a few different exposed options in the bundle (which we will briefly overview), the servers can essentially be swapped.
End of explanation
b.add_server('localthread', crimpl_name='local',
             server='mylocalserver', overwrite=True)
print(b.get_server('mylocalserver'))
Explanation: The name provided when saving the crimpl configuration is the same name that we will reference later in PHOEBE as crimpl_name.  This keeps all the server configuration and potential sensitive information out of the bundle or so that the bundle (or notebook) can safely be saved and shared, while also not requiring the servers to be re-configured in each case.  If crimpl_name is not available from crimpl on the local machine, an error will be raised and the server can easily be configured or swapped.
Servers in the Bundle
Servers in the bundle are added and managed as any other set of options via b.add_server and with "kinds" for each of the supported crimpl server types listed above:
localthread
remoteslurm
End of explanation
b.set_value('install_deps', False)
Explanation: In this case, we'll skip having crimpl install the necessary dependencies, as we know they are already installed.  If running PHOEBE in a conda environment locally, you could set use_conda=True and reference the same conda environment in the then-exposed conda_env parameter.
End of explanation
print(b.filter(qualifier='use_server'))
Explanation: use_server parameters
compute and solver options both have use_server parameters that will then dictate how b.run_compute and b.run_solver should handle running on external resources.  
If 'none' (as by default), the job is run in the current thread.  use_server in solver options are set to 'compute' by default, which means they will fallback on the use_server parameter in the referenced compute options.
End of explanation
print(b.filter(qualifier='compute'))
Explanation: So in this case, if running the nm_solver, PHOEBE will use the solver options for the phoebe01 compute options.
End of explanation
b.run_compute(use_server='mylocalserver')
_ = b.plot(show=True)
Explanation: As with any other parameter in compute or solver options, these can either be set or overridden as keyword arguments when calling b.run_compute and b.run_solver, respectively.
Running External Jobs with crimpl
There are several different use-cases for running compute or solver jobs on external servers
1. wait and load results automatically
As always, we can send kwargs to run_compute or run_solver to override any compute/solver options.  Here we can pass the server tag (note: this is the PHOEBE server tag, not the crimpl server name) to run_compute overriding the use_server parameter, which then looks up the server options, loads the corresponding crimpl object, and passes the job to the remote (albeit not so remote in this case) machine.
End of explanation
b.run_compute(use_server='mylocalserver', detach=True)
print(b.get_model())
Explanation: 2. detach and monitor/retrieve results
For long jobs, we can submit the job and immediately detach from run_compute or run_solver.  This results in a a "Job Parameter" in the model or solution holding the necessary information to check the progress and retrieve the results at any time in the future.
End of explanation
b.save('running_job.phoebe')
import phoebe
b = phoebe.open('running_job.phoebe')
Explanation: We can now (optionally) save the bundle, turn off our local machine, start a new python session, load the bundle, and all necessary information to check the job's status and retrieve results are handled automatically (with job information in the detached_job parameter and server setup/credentials in the crimpl configuration to avoid any security concerns when sharing bundles).
End of explanation
b.attach_job()
Explanation: Depending on how long we expect the job to take, we have a number of available methods we can call to act on this detached job parameter:
b.get_job_status
b.get_job_crimpl_object
b.attach_job
b.load_job_progress
b.kill_job
b.resubmit_job
Here we'll call b.attach_job, which will re-enter the loop waiting for the job to complete and then load the results.
End of explanation
script_fname, results_fname = b.export_compute('export_compute_to_server.py', 
                                               use_server='mylocalserver')
Explanation: 3. export script and submit manually
And lastly, for cases where we want to run the job later or from a different machine, we can call b.export_compute (or b.export_solver).  If use_server is 'none', this exports the python script directly.  If use_server is not 'none', this exports a script which accepts the following command-line options:
export_compute_to_server.py submit to submit the job to the server according to the set options
export_compute_to_server.py status to query the status of the job on the remote server
export_compute_to_server.py wait to query the status until complete or failed
export_compute_to_server.py output to retrieve the results
export_compute_to_server.py kill to terminate a job before it completes
export_compute_to_server.py continue to continue a job.  Only supported for solvers that have a continue_from parameter.
Once the job is complete and the results retrieved, we can then call b.import_model (or b.import_solution).
End of explanation
!head export_compute_to_server.py
Explanation: Note that the top of this file includes the crimpl options themselves, allowing the script to easily be modified to change the compute resources (particularly handy if sharing this file with a collaborator).
End of explanation
!python3 export_compute_to_server.py
Explanation: If we just call the exported script, we'll see that it provides the available command-line arguments.
End of explanation
!python3 export_compute_to_server.py submit
!python3 export_compute_to_server.py wait
!python3 export_compute_to_server.py output
b.import_model('export_compute_to_server.py.out', overwrite=True)
Explanation: We can now use these command line arguments to submit the script, check its status, wait for it to complete, and retrieve any output files.
End of explanation
script_fname, results_fname = b.export_compute('export_compute.py', 
                                               use_server='none')
!tail -3 export_compute.py
!python3 export_compute.py
b.import_model(results_fname, overwrite=True)
Explanation: Exporting External Jobs without crimpl
Alternatively, you can always call b.export_compute (or b.export_solver) with use_server='none', in which case the returned script does not use crimpl at all but rather can be manually copied to any machine, run or submit to any type of scheduler, and have the results manually copied back and imported.  If the implemented crimpl configurations do not meet your setup or you would just rather have fine control over the submission of the script, then this is your best bet.
In this case, you can still save and load the bundle and import the model or solution - but you're responsible for keeping track of filenames, dependencies, and monitoring the status of external threads.
End of explanation
<END_TASK>