Repository: qutip/qutip

Issue:
Add boundary condition options for spline based `QobjEvo`.
### Problem Description

The choice of boundary condition for spline used in `QobjEvo` can have observable impact on the system, see #2063.
But `QobjEvo` can only use scipy default boundary condition.


### Proposed Solution

Add the `bc_type` keyword to `InterCoefficient` and `QobjEvo`. 
`coefficient` pass though `kwargs` from `QobjEvo` to `InterCoefficient`, but should be added to the doc-string.

### Alternate Solutions

_No response_

### Additional Context

_No response_


Discussion:
Hi Eric, I would like to work on this issue. Can you give me a bit more details on this?
When `QobjEvo` is made using a numpy array (`QobjEvo([qobj, array], tlist=tlist)`), we use `scipy.interpolate.make_interp_spline` to compute the spline used in the evaluation.
We pass the keyword order: 
`QobjEvo([qobj, array], tlist=tlist, order=order)` -> `make_interp_spline(tlist, array, k=order)`.
But `make_interp_spline` also support `bc_type` keyword which we don't pass though:
```
>>> QobjEvo([qobj, array], tlist=tlist, bc_type="natural")
TypeError: __init__() got an unexpected keyword argument 'bc_type'
```
If would be useful for `QobjEvo` to pass the  'bc_type'  along the `order` and `tlist` to `make_interp_spline`.

Hi Eric. Thanks for the info! So, I have gone over the code base and have identified the specific files that would be modified as a part of the change. I have listed them below:
- [QobjEvo.pyx](https://github.com/qutip/qutip/blob/master/qutip/core/cy/qobjevo.pyx) : Modify `__init__` and `read_element` methods to include `bc_type`
- [coefficient.py](https://github.com/qutip/qutip/blob/master/qutip/core/coefficient.py): Modify `coefficient` method to include `bc_type` in the arguments
- [coefficient.pyx](https://github.com/qutip/qutip/blob/master/qutip/core/cy/coefficient.pyx): Modify `InterCoefficient` to include `bc_type` and pass the argument to `make_interp_spline`

Let me know if I have missed out on anything else? 

Also, for the documentation changes, is there a specific example that needs to be specified? Or should I just specify details similar to the [Scipy docs](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.make_interp_spline.html)?
The list look fine.
For the docstring, you can just refer the the scipy documentation and skip some of the details.

Resolving patch:
diff --git a/qutip/core/coefficient.py b/qutip/core/coefficient.py
--- a/qutip/core/coefficient.py
+++ b/qutip/core/coefficient.py
@@ -53,7 +53,8 @@ def _return(base, **kwargs):
 
 
 def coefficient(base, *, tlist=None, args={}, args_ctypes={},
-                order=3, compile_opt=None, function_style=None, **kwargs):
+                order=3, compile_opt=None, function_style=None,
+                boundary_conditions=None, **kwargs):
     """Build ``Coefficient`` for time dependent systems:
 
     ```
@@ -149,6 +150,9 @@ def f2_t(t, args):
     compile_opt : CompilationOptions, optional
         Sets of options for the compilation of string based coefficients.
 
+    boundary_conditions: 2-tupule, str or None, optional
+        Specify boundary conditions for spline interpolation.
+
     **kwargs
         Extra arguments to pass the the coefficients.
     """
@@ -159,6 +163,7 @@ def f2_t(t, args):
         'order': order,
         'compile_opt': compile_opt,
         'function_style': function_style,
+        'boundary_conditions': boundary_conditions
     })
 
     for type_ in coefficient_builders:


Test patch:
diff --git a/qutip/tests/core/test_coefficient.py b/qutip/tests/core/test_coefficient.py
--- a/qutip/tests/core/test_coefficient.py
+++ b/qutip/tests/core/test_coefficient.py
@@ -372,6 +372,10 @@ def test_CoeffFromScipy():
     from_scipy = coefficient(interp.make_interp_spline(tlist, y, k=3))
     _assert_eq_over_interval(coeff, from_scipy, rtol=1e-8, inside=True)
 
+    coeff = coefficient(y, tlist=tlist, order=3, boundary_conditions="natural")
+    from_scipy = coefficient(interp.make_interp_spline(tlist, y, k=3, bc_type="natural"))
+    _assert_eq_over_interval(coeff, from_scipy, rtol=1e-8, inside=True)
+
 
 @pytest.mark.parametrize('map_func', [
     pytest.param(qutip.solver.parallel.parallel_map, id='parallel_map'),