diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1849890..f92998c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -26,6 +26,23 @@ TODO: in `main.cpp` check the returned policy of pybind11 and also the `py::call
 TODO: a cpp class that is able to compute (DC powerflow) ContingencyAnalysis and TimeSeries using PTDF and LODF
 TODO: integration test with pandapower (see `pandapower/contingency/contingency.py` and import `lightsim2grid_installed` and check it's True)
 
+[0.10.0] 2024-12-17
+-------------------
+- [BREAKING] disconnected storage now raises errors if some power is produced / absorbed, when using legacy grid2op version,
+  you can retrieve the previous behaviour by initializing the `LightSimBackend` with
+  `backend = LightSimBackend(..., stop_if_storage_disco=False)`
+- [BREAKING] with the new `detachment_is_allowed` feature in grid2op, the kwargs `stop_if_load_disco`,
+  `stop_if_gen_disco` (and `stop_if_storage_disco`) are now optional. They are set up from the 
+  call to `grid2op.make(...)` and are erased by the `allow_detachment` kwargs. In other words,
+  you don't need to set `stop_if_load_disco`, `stop_if_gen_disco` or `stop_if_storage_disco`. It is 
+  automatically set by `grid2op.make(..., allow_detachment=XXX)` to have the correct bahaviour.
+- [FIXED] an issue with the storage units (when asking it to produce / consume 
+  but deactivating them with the same action the grid did not diverge)
+- [IMPROVED] add the grid2op "detachement" support (loads and generators are allowed
+  to be disconnected from the grid)
+- [ADDED] a kwargs `stop_if_storage_disco` to control (in legacy grid2op version) the behaviour 
+  of the backend when a storage unit is disconnected.
+
 [0.9.2.post2] 2024-11-29
 --------------------------
 - [FIXED] The attribute `can_output_theta` (of base `Backend` class)
diff --git a/basic_perf.py b/basic_perf.py
new file mode 100644
index 0000000..686db5d
--- /dev/null
+++ b/basic_perf.py
@@ -0,0 +1,109 @@
+import warnings
+import pandapower as pp
+import numpy as np        
+from grid2op import make, Parameters
+from grid2op.Chronics import FromNPY
+from lightsim2grid import LightSimBackend
+import tempfile
+import os
+
+try:
+    from tabulate import tabulate
+    TABULATE_AVAIL = True
+except ImportError:
+    print("The tabulate package is not installed. Some output might not work properly")
+    TABULATE_AVAIL = False
+
+
+case_names = [
+            #   "case14.json",
+              "case118.json",
+            #   "case_illinois200.json",
+            #   "case300.json",
+            #   "case1354pegase.json",
+              "case1888rte.json",
+            #   "GBnetwork.json",  # 2224 buses
+              "case2848rte.json",
+            #   "case2869pegase.json",
+            #   "case3120sp.json",
+              "case6495rte.json",
+              "case6515rte.json",
+              "case9241pegase.json"
+              ]
+
+case_name = "case6495rte.json"
+case_name = "case14.json"
+    
+def make_grid2op_env(pp_case, case_name, load_p, load_q, gen_p, sgen_p):
+    param = Parameters.Parameters()
+    param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True})
+        
+    with warnings.catch_warnings():
+        warnings.filterwarnings("ignore")
+        env_lightsim = make("blank",
+                            param=param,
+                            test=True,
+                            backend=LightSimBackend(),
+                            chronics_class=FromNPY,
+                            data_feeding_kwargs={"load_p": load_p,
+                                                 "load_q": load_q,
+                                                 "prod_p": gen_p
+                                                },
+                            grid_path=case_name,
+                            _add_to_name=f"{case_name}",
+                            )
+    return env_lightsim
+
+if __name__ == "__main__":
+
+    import pandapower.networks as pn
+    for case_name in case_names:
+        tmp_nm =  os.path.splitext(case_name)[0]
+        print(f"====================== {tmp_nm} ======================")
+        case = getattr(pn,tmp_nm)()
+        pp.runpp(case)  # for slack
+        
+        load_p_init = 1.0 * case.load["p_mw"].values
+        load_q_init = 1.0 * case.load["q_mvar"].values
+        gen_p_init = 1.0 * case.gen["p_mw"].values
+        sgen_p_init = 1.0 * case.sgen["p_mw"].values
+        
+        nb_ts = 1
+        # add slack !
+        slack_gens =  np.zeros((nb_ts, case.ext_grid.shape[0]))
+        if "res_ext_grid" in case:
+            slack_gens += np.tile(case.res_ext_grid["p_mw"].values.reshape(1,-1), (nb_ts, 1))
+        gen_p_g2op = np.concatenate((gen_p_init.reshape(1,-1), slack_gens), axis=1)  
+        
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            pp.to_json(case, os.path.join(tmpdirname, case_name))
+            with open(os.path.join(tmpdirname, "config.py"), "w") as f:
+                f.write("config = {}")
+            
+            env = make_grid2op_env(None,
+                                os.path.join(tmpdirname, case_name),
+                                load_p=load_p_init.reshape(1,-1),
+                                load_q=load_q_init.reshape(1,-1),
+                                gen_p=gen_p_g2op.reshape(1,-1),
+                                sgen_p=None)
+            
+        env.backend._grid.tell_solver_need_reset()
+        _ = env.step(env.action_space())
+        ls_solver =  env.backend._grid.get_solver()
+        nb_iter_solver = ls_solver.get_nb_iter()
+        timers = ls_solver.get_timers_jacobian()
+        (timer_Fx, timer_solve, timer_init, timer_check, 
+        timer_compute_dS, timer_fillJ, timer_compVa_Vm, timer_preproc, timer_total) = timers
+        print(f"Total time for the powerflow (=pre proc + NR + post proc): {env.backend._grid.timer_last_ac_pf:.2e}s")
+        print(f"Total time spent in the Newton Raphson: {timer_total:.2e}s")
+        print(f"Time to pre process input data: {timer_preproc:.2e}s")
+        print(f"Time to intialize linear solver: {timer_init:.2e}s")
+        print(f"Then for all iterations (cumulated time over all {nb_iter_solver} iterations)")
+        print(f"\ttotal time to compute dS/dVm and dS/dVa: {timer_compute_dS:.2e}s")
+        print(f"\ttotal time fill jacobian matrix (from dS/dVm and dS/dVa): {timer_fillJ:.2e}s")
+        print(f"\ttotal time to solve J.x = b: {timer_solve:.2e}s")
+        print(f"\ttotal time to compute V, Va and Vm: {timer_compVa_Vm:.2e}s")
+        print(f"\ttotal time to compute p, q mismatch at buses: {timer_Fx:.2e}s")
+        print(f"\ttotal time to check if p,q mismatch at buses are within tolerance: {timer_check:.2e}s")
+        print(f"====================== {' '*len(tmp_nm)} ======================")
+        
\ No newline at end of file
diff --git a/benchmarks/benchmark_gauss_seidel.py b/benchmarks/benchmark_gauss_seidel.py
new file mode 100644
index 0000000..18be5ee
--- /dev/null
+++ b/benchmarks/benchmark_gauss_seidel.py
@@ -0,0 +1,192 @@
+# Copyright (c) 2024, RTE (https://www.rte-france.com)
+# See AUTHORS.txt
+# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
+# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
+# you can obtain one at http://mozilla.org/MPL/2.0/.
+# SPDX-License-Identifier: MPL-2.0
+# This file is part of LightSim2grid, LightSim2grid a implements a c++ backend targeting the Grid2Op platform.
+
+import warnings
+import copy
+import pandapower as pp
+import numpy as np        
+import hashlib
+from scipy.interpolate import interp1d
+import matplotlib.pyplot as plt
+from grid2op import make, Parameters
+from grid2op.Chronics import FromNPY, ChangeNothing
+from grid2op.Backend import PandaPowerBackend
+from grid2op.Exceptions import Grid2OpException
+import lightsim2grid
+from lightsim2grid import LightSimBackend
+from benchmark_grid_size import (get_loads_gens,
+                                 make_grid2op_env_pp,
+                                 run_grid2op_env,
+                                 make_grid2op_env)
+from benchmark_solvers import solver_gs, solver_names, order_solver_print
+    
+from tqdm import tqdm
+import os
+from utils_benchmark import print_configuration, get_env_name_displayed
+from benchmark_solvers import solver_names
+
+try:
+    from tabulate import tabulate
+    TABULATE_AVAIL = True
+except ImportError:
+    print("The tabulate package is not installed. Some output might not work properly")
+    TABULATE_AVAIL = False
+    
+VERBOSE = False
+MAKE_PLOT = False
+WITH_PP = False
+DEBUG = False
+
+case_names = [
+              "case14.json",
+              "case118.json",
+              "case_illinois200.json",
+              "case300.json",
+              "case1354pegase.json",
+              "case1888rte.json",
+            # #   "GBnetwork.json",  # 2224 buses
+            #   "case2848rte.json",
+            #   "case2869pegase.json",
+            #   "case3120sp.json",
+            #   "case6495rte.json",
+            #   "case6515rte.json",
+            #   "case9241pegase.json"
+              ]
+
+
+if __name__ == "__main__":
+    prng = np.random.default_rng(42)
+    case_names_displayed = [get_env_name_displayed(el) for el in case_names]
+    nb_iters = []
+    ts_sizes = []
+    errors = {}
+    for case_name in tqdm(case_names):
+
+        if not os.path.exists(case_name):
+            import pandapower.networks as pn
+            case = getattr(pn, os.path.splitext(case_name)[0])()
+            pp.to_json(case, case_name)
+
+        # load the case file
+        case = pp.from_json(case_name)
+        ts_sizes.append(case.bus.shape[0])
+        pp.runpp(case)  # for slack
+        
+        # create the env
+        param = Parameters.Parameters()
+        param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True})
+            
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore")
+            env_pp = make("blank",
+                        param=param, test=True,
+                        backend=PandaPowerBackend(lightsim2grid=False),
+                        chronics_class=ChangeNothing,
+                        grid_path=case_name,
+                        _add_to_name=f"{case_name}",
+                        )
+            env_ls = make("blank",
+                        param=param, test=True,
+                        backend=LightSimBackend(),
+                        chronics_class=ChangeNothing,
+                        grid_path=case_name,
+                        _add_to_name=f"{case_name}",
+                        )
+        env_ls.backend.set_solver_type(lightsim2grid.SolverType.GaussSeidel)
+        all_iters = [1, 3, 10, 30, 100, 300, 1_000, 3_000,
+                     10_000, 30_000,
+                     100_000, 300_000
+                    ]
+        iters = []
+        errors_p = []
+        errors_q = []
+        for max_iter in all_iters:
+            env_ls.backend.set_solver_max_iter(max_iter)
+            env_ls.backend._grid.tell_solver_need_reset()
+            conv = True
+            try:
+                obs = env_ls.reset()
+            except Grid2OpException as exc_:
+                conv = False
+            iters.append(env_ls.backend._grid.get_solver().get_nb_iter())
+            v_tmp = env_ls.backend._grid.get_solver().get_V()
+            res_tmp = env_ls.backend._grid.check_solution(v_tmp, False)
+            error_p = 1. * np.abs(res_tmp.real).max()
+            error_q = 1. * np.abs(res_tmp.imag).max()
+            errors_p.append(error_p)
+            errors_q.append(error_q)
+            if conv:
+                break
+        if conv:
+            nb_iters.append(iters[-1])
+        else:
+            nb_iters.append(None)
+            
+        errors[case.bus.shape[0]] = (errors_p, errors_q)
+        
+    print("Configuration:")
+    print_configuration()
+    print(f"Solver used for linear algebra: {lightsim2grid.SolverType.GaussSeidel}")
+    print()
+    hds = ["grid size (nb bus)", "gauss seidel max iter"]
+    tab = []
+    for sz, nb_it in zip(ts_sizes, nb_iters):
+        tab.append([sz, nb_it])
+        
+    if TABULATE_AVAIL:
+        res_use_with_grid2op_2 = tabulate(tab, headers=hds,  tablefmt="rst")
+        print(res_use_with_grid2op_2)
+    else:
+        print(tab)
+        
+    print(errors[118][0])
+    print(errors[118][1])
+    import pickle
+    with open("res_gauss_seidel.pickle", "wb") as f:
+        pickle.dump(errors, file=f)
+    with open("res_gauss_seidel_nb_iters.pickle", "wb") as f:
+        pickle.dump(nb_iters, file=f)
+    print()
+    print()
+
+
+# total computation time : 1h27min16s
+# Configuration:
+
+# - date: 2024-12-02 18:46  CET
+# - system: Linux 5.15.0-56-generic
+# - OS: ubuntu 20.04
+# - processor: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
+# - python version: 3.8.10.final.0 (64 bit)
+# - numpy version: 1.24.3
+# - pandas version: 2.0.3
+# - pandapower version: 2.14.0
+# - grid2op version: 1.11.0.dev2
+# - lightsim2grid version: 0.9.2.post2
+# - lightsim2grid extra information: 
+
+# 	- klu_solver_available: True 
+# 	- nicslu_solver_available: False 
+# 	- cktso_solver_available: False 
+# 	- compiled_march_native: False 
+# 	- compiled_o3_optim: False 
+
+# Solver used for linear algebra: SolverType.GaussSeidel
+
+# ====================  =======================
+#   grid size (nb bus)    gauss seidel max iter
+# ====================  =======================
+#                   14                      278
+#                  118                     3274
+#                  200                     8360
+#                  300                    40783
+#                 1354                   122169
+#                 1888
+# ====================  =======================
+# [31.858705410410803, 13.801689961508492, 7.912199121114395, 6.387621207822959, 4.5494311573542525, 1.3539274305627065, 0.01652457790687702, 5.5928201247405206e-08, 9.957519963773673e-09]
+# [111.7637849724719, 52.1105433668106, 6.3902552555152345, 1.1851759157023143, 0.8457897295792693, 0.25197455746676584, 0.0030761171444685202, 1.0415372012959338e-08, 1.8561325626140559e-09]
diff --git a/benchmarks/benchmark_grid_size.py b/benchmarks/benchmark_grid_size.py
index bbc2b62..5269b3f 100644
--- a/benchmarks/benchmark_grid_size.py
+++ b/benchmarks/benchmark_grid_size.py
@@ -190,7 +190,9 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
                     g2op_step_time,
                     ls_solver_time,
                     ls_gridmodel_time,
-                    g2op_sizes
+                    g2op_sizes,
+                    sgen_p,
+                    nb_ts
                     ):
     _ = env_lightsim.reset()
     done = False
@@ -330,7 +332,7 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
                                         g2op_step_time_reset,
                                         ls_solver_time_reset,
                                         ls_gridmodel_time_reset,
-                                        g2op_sizes_reset
+                                        g2op_sizes_reset, sgen_p, nb_ts
                                         )
         
         reset_solver = False  # default
@@ -340,7 +342,7 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
                                   g2op_step_time,
                                   ls_solver_time,
                                   ls_gridmodel_time,
-                                  g2op_sizes
+                                  g2op_sizes, sgen_p, nb_ts
                                   )
         
         # Perform the computation using TimeSerie
diff --git a/docs/conf.py b/docs/conf.py
index 93f2a29..512eedd 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -22,8 +22,8 @@
 author = 'Benjamin DONNOT'
 
 # The full version, including alpha/beta/rc tags
-release = "0.9.2.post2"
-version = '0.9'
+release = "0.10.0"
+version = '0.10'
 
 # -- General configuration ---------------------------------------------------
 
diff --git a/lightsim2grid/__init__.py b/lightsim2grid/__init__.py
index d1c3abe..f5c5ccd 100644
--- a/lightsim2grid/__init__.py
+++ b/lightsim2grid/__init__.py
@@ -6,7 +6,7 @@
 # SPDX-License-Identifier: MPL-2.0
 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform.
 
-__version__ = "0.9.2.post2"
+__version__ = "0.10.0"
 
 __all__ = ["newtonpf", "SolverType", "ErrorType", "solver", "compilation_options"]
 
diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py
index 8fe4ce3..1813442 100644
--- a/lightsim2grid/lightSimBackend.py
+++ b/lightsim2grid/lightSimBackend.py
@@ -36,6 +36,12 @@
 except ImportError:
     # for backward compatibility with grid2op <= 1.9.8
     DEFAULT_N_BUSBAR_PER_SUB = 2
+
+try:
+    from grid2op.Space import DEFAULT_ALLOW_DETACHMENT
+except ImportError:
+    # for backward compatibility with grid2op < 1.11.0
+    DEFAULT_ALLOW_DETACHMENT = False
     
 try:
     from typing import Literal
@@ -61,6 +67,9 @@ class LightSimBackend(Backend):
         # for legacy grid2op
         n_busbar_per_sub = DEFAULT_N_BUSBAR_PER_SUB
         
+    if not hasattr(Backend, "detachment_is_allowed"):
+        # for legacy grid2op (< 1.11.0)
+        detachment_is_allowed = DEFAULT_ALLOW_DETACHMENT
         
     def __init__(self,
                  detailed_infos_for_cascading_failures: bool=False,
@@ -73,8 +82,9 @@ def __init__(self,
                  use_static_gen: bool=False, # add the static generators as generator gri2dop side
                  loader_method: Literal["pandapower", "pypowsybl"] = "pandapower",
                  loader_kwargs : Optional[dict] = None,
-                 stop_if_load_disco : Optional[bool] = True,
-                 stop_if_gen_disco : Optional[bool] = True,
+                 stop_if_load_disco : Optional[bool] = None,
+                 stop_if_gen_disco : Optional[bool] = None,
+                 stop_if_storage_disco : Optional[bool] = None,
                  ):
         #: ``int`` maximum number of iteration allowed for the solver
         #: if the solver has not converge after this, it will 
@@ -131,13 +141,32 @@ def __init__(self,
         #:
         #: if set to ``True`` (default) then the backend will raise a 
         #: BackendError in case of disconnected load
+        #:
+        #: .. note:: When using grid2op >= 1.11.0 and lightsim2grid >= 0.10.0 this 
+        #:    is automatically set-up with the call to grid2op.make, 
+        #:    to match the behaviour expected by `allow_detachment`
         self._stop_if_load_disco = stop_if_load_disco
         
         #: .. versionadded:: 0.8.0
         #:
         #: if set to ``True`` (default) then the backend will raise a 
         #: BackendError in case of disconnected generator
+        #:
+        #: .. note:: When using grid2op >= 1.11.0 and lightsim2grid >= 0.10.0 this 
+        #:    is automatically set-up with the call to grid2op.make, 
+        #:    to match the behaviour expected by `allow_detachment`
         self._stop_if_gen_disco = stop_if_gen_disco
+        
+        #: .. versionadded:: 0.10.0
+        #:
+        #: if set to ``True`` (default) then the backend will raise a 
+        #: BackendError in case of disconnected storage that are 
+        #: asked to produce / absorb something
+        #:
+        #: .. note:: When using grid2op >= 1.11.0 and lightsim2grid >= 0.10.0 this 
+        #:    is automatically set-up with the call to grid2op.make, 
+        #:    to match the behaviour expected by `allow_detachment`
+        self._stop_if_storage_disco = stop_if_storage_disco
                                         
         self._aux_init_super(detailed_infos_for_cascading_failures,
                              can_be_copied,
@@ -150,7 +179,8 @@ def __init__(self,
                              loader_method,
                              loader_kwargs,
                              stop_if_load_disco,
-                             stop_if_gen_disco)
+                             stop_if_gen_disco,
+                             stop_if_storage_disco)
         
         # backward compat: need to define it if not done by grid2op
         if not hasattr(self, "_can_be_copied"):
@@ -298,6 +328,13 @@ def __init__(self,
         # backend SHOULD not do these kind of stuff
         self._idx_hack_storage = []
         
+        #: ..versionadded: 0.9.3
+        #: sometimes some actions will make the grid fails
+        #: but grid2op expect it to fail not on "apply_action"
+        #: but rather when calling `runpf`
+        #: this flags remembers it
+        self._next_pf_fails : Optional[BackendError] = None
+        
         # speed optimization
         self._lineor_res = None
         self._lineex_res = None
@@ -323,7 +360,8 @@ def _aux_init_super(self,
                         loader_method,
                         loader_kwargs,
                         stop_if_load_disco,
-                        stop_if_gen_disco):
+                        stop_if_gen_disco,
+                        stop_if_storage_disco):
         try:
             # for grid2Op >= 1.7.1
             Backend.__init__(self,
@@ -339,6 +377,7 @@ def _aux_init_super(self,
                              loader_kwargs=loader_kwargs,
                              stop_if_load_disco=stop_if_load_disco,
                              stop_if_gen_disco=stop_if_gen_disco,
+                             stop_if_storage_disco=stop_if_storage_disco
                              )
         except TypeError as exc_:
             warnings.warn("Please use grid2op >= 1.7.1: with older grid2op versions, "
@@ -350,6 +389,10 @@ def _aux_init_super(self,
             # do not forget to propagate this if needed
             self.can_handle_more_than_2_busbar()
             
+        if hasattr(type(self), "can_handle_detachment"):
+            # do not forget to propagate this if needed
+            self.can_handle_detachment()
+            
     def turnedoff_no_pv(self):
         self._turned_off_pv = False
         self._grid.turnedoff_no_pv()
@@ -516,7 +559,9 @@ def _handle_turnedoff_pv(self):
             self._grid.turnedoff_no_pv()
     
     def _assign_right_solver(self):
-        has_single_slack = np.where(np.array([el.slack_weight for el in self._grid.get_generators()]) != 0.)[0].shape[0] == 1
+        slack_weights = np.array([el.slack_weight for el in self._grid.get_generators()])
+        nb_slack_nonzero = (np.abs(slack_weights) > 0.).sum()
+        has_single_slack = nb_slack_nonzero == 1
         if has_single_slack and not self._dist_slack_non_renew:
             if SolverType.KLUSingleSlack in self.available_solvers:
                 # use the faster KLU if available
@@ -530,14 +575,96 @@ def _assign_right_solver(self):
                 self._grid.change_solver(SolverType.KLU)
             else:
                 self._grid.change_solver(SolverType.SparseLU)
+    
+    def _aux_set_correct_detach_flags_d_allowed(self):
+        # user allowed detachment, I check the correct flags
+        if self._stop_if_gen_disco is None:
+            # user did not specify anything
+            self._stop_if_gen_disco = False
+        elif not self._stop_if_gen_disco:
+            # force conversion to proper type
+            self._stop_if_gen_disco = False
+        elif self._stop_if_gen_disco:
+            # erase default values and continue like the grid2op call specifies
+            warnings.warn("Call to `grid2op.make(..., allow_detachement=True)` will erase the lightsim2grid kwargs `stop_if_gen_disco=True`")
+            self._stop_if_gen_disco = False
+            
+        if self._stop_if_load_disco is None:
+            # user did not specify anything
+            self._stop_if_load_disco = False
+        elif not self._stop_if_load_disco:
+            # force conversion to the proper type
+            self._stop_if_load_disco = False
+        elif self._stop_if_load_disco:
+            # erase default values and continue like the grid2op call specifies
+            warnings.warn("Call to `grid2op.make(..., allow_detachement=True)` will erase the lightsim2grid kwargs `stop_if_load_disco=True`")
+            self._stop_if_load_disco = False
+            
+        if self._stop_if_storage_disco is None:
+            # user did not specify anything
+            self._stop_if_storage_disco = False
+        elif not self._stop_if_storage_disco:
+            # force conversion to the proper type
+            self._stop_if_storage_disco = False
+        elif self._stop_if_storage_disco:
+            # erase default values and continue like the grid2op call specifies
+            warnings.warn("Call to `grid2op.make(..., allow_detachement=True)` will erase the lightsim2grid kwargs `stop_if_storage_disco=True`")
+            self._stop_if_storage_disco = False
+    
+    def _aux_set_correct_detach_flags_d_not_allowed(self):# user did not allow detachment (or it's a legacy grid2op version), I check the correct flags
+        if self._stop_if_gen_disco is None:
+            # user did not specify anything
+            self._stop_if_gen_disco = True
+        elif self._stop_if_gen_disco:
+            # force conversion to proper type
+            self._stop_if_gen_disco = True
+        elif not self._stop_if_gen_disco:
+            # erase default values and continue like the grid2op call specifies
+            warnings.warn("Call to `grid2op.make(..., allow_detachement=False)` will erase the lightsim2grid kwargs `stop_if_gen_disco=False`")
+            self._stop_if_gen_disco = True
+            
+        if self._stop_if_load_disco is None:
+            # user did not specify anything
+            self._stop_if_load_disco = True
+        elif self._stop_if_load_disco:
+            # force conversion to proper type
+            self._stop_if_load_disco = True
+        elif not self._stop_if_load_disco:
+            # erase default values and continue like the grid2op call specifies
+            warnings.warn("Call to `grid2op.make(..., allow_detachement=False)` will erase the lightsim2grid kwargs `stop_if_load_disco=False`")
+            self._stop_if_load_disco = True
+            
+        if self._stop_if_storage_disco is None:
+            # user did not specify anything
+            self._stop_if_storage_disco = True
+        elif self._stop_if_storage_disco:
+            # force conversion to proper type
+            self._stop_if_storage_disco = True
+        elif not self._stop_if_storage_disco:
+            # erase default values and continue like the grid2op call specifies
+            warnings.warn("Call to `grid2op.make(..., allow_detachement=False)` will erase the lightsim2grid kwargs `stop_if_storage_disco=False`")
+            self._stop_if_storage_disco = True
                 
+    def _aux_set_correct_detach_flags(self):
+        if self.detachment_is_allowed:
+            self._aux_set_correct_detach_flags_d_allowed()
+        else:
+            self._aux_set_correct_detach_flags_d_not_allowed()
+        
     def load_grid(self,
                   path : Union[os.PathLike, str],
                   filename : Optional[Union[os.PathLike, str]]=None) -> None: 
-        if hasattr(type(self), "can_handle_more_than_2_busbar"):
+        cls = type(self)
+        if hasattr(cls, "can_handle_more_than_2_busbar"):
             # grid2op version >= 1.10.0 then we use this
             self.can_handle_more_than_2_busbar()
-        
+            
+        if hasattr(cls, "can_handle_detachment"):
+            # grid2op version >= 1.11.0 then we use this
+            self.can_handle_detachment()
+            
+        self._aux_set_correct_detach_flags()
+            
         if self._loader_method == "pandapower":
             self._load_grid_pandapower(path, filename)
         elif self._loader_method == "pypowsybl":
@@ -546,6 +673,7 @@ def load_grid(self,
             raise BackendError(f"Impossible to initialize the backend with '{self._loader_method}'")
         self._grid.tell_solver_need_reset()
         self._reset_res_pointers()  # force the re reading of the accessors at next powerflow
+        self.V = np.ones(self.nb_bus_total, dtype=np.complex_)
     
     def _should_not_have_to_do_this(self, path=None, filename=None):
         # included in grid2op now !
@@ -1148,6 +1276,7 @@ def apply_action(self, backendAction: Union["grid2op.Action._backendAction._Back
         Specific implementation of the method to apply an action modifying a powergrid in the pandapower format.
         """
         tick = time.perf_counter()
+        self._next_pf_fails = None
         active_bus, *_, topo__, shunts__ = backendAction()
 
         # change the overall topology
@@ -1178,7 +1307,11 @@ def apply_action(self, backendAction: Union["grid2op.Action._backendAction._Back
                                              backendAction.storage_power.values)
             except RuntimeError as exc_:
                 # modification of power of disconnected storage has no effect in lightsim2grid
-                pass
+                if self.detachment_is_allowed:
+                    # a storage units is allowed to be disconnected in this case
+                    pass
+                else:
+                    self._next_pf_fails = BackendError("Some storage units would be disconnected")
 
         # handle shunts
         if type(self).shunts_data_available:
@@ -1231,7 +1364,10 @@ def _fetch_grid_data(self):
     def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]:
         my_exc_ = None
         res = False
-        try:
+        try:            
+            if self._next_pf_fails is not None:
+                raise self._next_pf_fails
+            
             beg_preproc = time.perf_counter()
             if is_dc:
                 # somehow, when asked to do a powerflow in DC, pandapower assign Vm to be
@@ -1331,20 +1467,31 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]:
             self.next_prod_p[:] = self.prod_p
             if self._stop_if_load_disco and ((~np.isfinite(self.load_v)).any() or (self.load_v <= 0.).any()):
                 disco = (~np.isfinite(self.load_v)) | (self.load_v <= 0.)
-                load_disco = np.where(disco)[0]
+                load_disco = disco.nonzero()[0]
                 self._timer_postproc += time.perf_counter() - beg_postroc
                 raise BackendError(f"At least one load is disconnected (check loads {load_disco})")
             if self._stop_if_gen_disco and ((~np.isfinite(self.prod_v)).any() or (self.prod_v <= 0.).any()):
                 disco = (~np.isfinite(self.prod_v)) | (self.prod_v <= 0.)
-                gen_disco = np.where(disco)[0]
+                gen_disco = disco.nonzero()[0]
                 self._timer_postproc += time.perf_counter() - beg_postroc
                 raise BackendError(f"At least one generator is disconnected (check gen {gen_disco})")
+            
+            if self.__has_storage:
+                sto_active = (np.abs(self.storage_p) > 0.)
+                sto_act_disco = (((~np.isfinite(self.storage_v)) & sto_active).any() or 
+                                 ((self.storage_v <= 0.) & sto_active).any()
+                                )
+                if self._stop_if_storage_disco and sto_act_disco:
+                    disco = ((~np.isfinite(self.storage_v)) | (self.storage_v <= 0.)) & sto_active
+                    sto_disco = disco.nonzero()[0]
+                    self._timer_postproc += time.perf_counter() - beg_postroc
+                    raise BackendError(f"At least one storage unit is disconnected (check gen {sto_disco})")
             # TODO storage case of divergence !
 
             if type(self).shunts_data_available:
                 self._set_shunt_info()
 
-            if (self.line_or_theta >= 1e6).any() or (self.line_ex_theta >= 1e6).any():
+            if (np.abs(self.line_or_theta) >= 1e6).any() or (np.abs(self.line_ex_theta) >= 1e6).any():
                 raise BackendError(f"Some theta are above 1e6 which should not be happening !")
             res = True
             self._grid.unset_changes()
@@ -1369,6 +1516,7 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]:
 
     def _fill_nans(self):
         """fill the results vectors with nans"""
+        self._next_pf_fails = None
         self.p_or[:] = np.NaN
         self.q_or[:] = np.NaN
         self.v_or[:] = np.NaN
@@ -1449,7 +1597,8 @@ def copy(self) -> Self:
                             self._loader_method,
                             self._loader_kwargs,
                             self._stop_if_load_disco,
-                            self._stop_if_gen_disco)
+                            self._stop_if_gen_disco,
+                            self._stop_if_storage_disco)
         
         # for backward compat (attribute was not necessarily present in early grid2op)
         if not hasattr(res, "_can_be_copied"):
@@ -1473,8 +1622,8 @@ def copy(self) -> Self:
                            "supported_grid_format", 
                            "max_it", "tol", "_turned_off_pv", "_dist_slack_non_renew",
                            "_use_static_gen", "_loader_method", "_loader_kwargs",
-                           "_stop_if_load_disco", "_stop_if_gen_disco",
-                           "_timer_fetch_data_cpp"
+                           "_stop_if_load_disco", "_stop_if_gen_disco", "_stop_if_storage_disco",
+                           "_timer_fetch_data_cpp", "_next_pf_fails"
                            ]
         for attr_nm in li_regular_attr:
             if hasattr(self, attr_nm):
diff --git a/lightsim2grid/tests/test_issue_66.py b/lightsim2grid/tests/test_issue_66.py
index be2cc08..2643bd4 100644
--- a/lightsim2grid/tests/test_issue_66.py
+++ b/lightsim2grid/tests/test_issue_66.py
@@ -83,14 +83,15 @@ def test_disco_storage(self):
         obs = self.env.reset()
         act = self.env.action_space({"set_bus": {"storages_id": [(0, -1)]}})
         obs, reward, done, info = self.env.step(act)
-        assert len(info['exception']) == 0
+        assert len(info['exception']) == 0, f"{len(info['exception'])} vs 0"
         assert not done
         # should not raise any RuntimeError
         
         act = self.env.action_space({"set_storage": [(0, -1)]})
         obs, reward, done, info = self.env.step(act)
-        assert len(info['exception']) == 0
-        assert not done
+        # grid2op >= 1.11.0 requires this 
+        assert len(info['exception']) == 1, f"{len(info['exception'])} vs 1"
+        assert done
         # should not raise any RuntimeError
         
         
diff --git a/setup.py b/setup.py
index 8667f7c..2cff340 100644
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,7 @@
 from pybind11.setup_helpers import Pybind11Extension, build_ext
 
 
-__version__ = "0.9.2.post2"
+__version__ = "0.10.0"
 KLU_SOLVER_AVAILABLE = False
 
 # Try to link against SuiteSparse (if available)
diff --git a/src/GridModel.h b/src/GridModel.h
index 0c50403..e16e829 100644
--- a/src/GridModel.h
+++ b/src/GridModel.h
@@ -494,9 +494,9 @@ class GridModel : public GenericContainer
 //                reactivate_storage(storage_id);  // requirement from grid2op, might be discussed
 //                storages_.change_p(storage_id, new_p, need_reset_);
 //            }
-               storages_.change_p(storage_id, new_p, solver_control_);
+               storages_.change_p_nothrow(storage_id, new_p, solver_control_);
             }
-        void change_q_storage(int storage_id, real_type new_q) {storages_.change_q(storage_id, new_q, solver_control_); }
+        void change_q_storage(int storage_id, real_type new_q) {storages_.change_q_nothrow(storage_id, new_q, solver_control_); }
         int get_bus_storage(int storage_id) {return storages_.get_bus(storage_id);}
 
         //deactivate a powerline (disconnect it)
diff --git a/src/element_container/LoadContainer.cpp b/src/element_container/LoadContainer.cpp
index e7c7483..fedf1f6 100644
--- a/src/element_container/LoadContainer.cpp
+++ b/src/element_container/LoadContainer.cpp
@@ -117,10 +117,7 @@ void LoadContainer::change_p(int load_id, real_type new_p, SolverControl & solve
         exc_ << ")";
         throw std::runtime_error(exc_.str());
     }
-    if (p_mw_(load_id) != new_p) {
-        solver_control.tell_recompute_sbus();
-        p_mw_(load_id) = new_p;
-    }
+    change_p_nothrow(load_id, new_p, solver_control);
 }
 
 void LoadContainer::change_q(int load_id, real_type new_q, SolverControl & solver_control)
@@ -134,10 +131,7 @@ void LoadContainer::change_q(int load_id, real_type new_q, SolverControl & solve
         exc_ << ")";
         throw std::runtime_error(exc_.str());
     }
-    if (q_mvar_(load_id) != new_q) {
-        solver_control.tell_recompute_sbus();
-        q_mvar_(load_id) = new_q;
-    }
+    change_q_nothrow(load_id, new_q, solver_control);
 }
 
 void LoadContainer::reconnect_connected_buses(std::vector<bool> & bus_status) const {
diff --git a/src/element_container/LoadContainer.h b/src/element_container/LoadContainer.h
index 9a6710b..edfea92 100644
--- a/src/element_container/LoadContainer.h
+++ b/src/element_container/LoadContainer.h
@@ -153,7 +153,21 @@ class LoadContainer : public GenericContainer
     void change_bus(int load_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(load_id, new_bus_id, bus_id_, solver_control, nb_bus);}
     int get_bus(int load_id) {return _get_bus(load_id, status_, bus_id_);}
     void change_p(int load_id, real_type new_p, SolverControl & solver_control);
+    void change_p_nothrow(int load_id, real_type new_p, SolverControl & solver_control)
+    {
+        if (p_mw_(load_id) != new_p) {
+            solver_control.tell_recompute_sbus();
+            p_mw_(load_id) = new_p;
+        }
+    }
     void change_q(int load_id, real_type new_q, SolverControl & solver_control);
+    void change_q_nothrow(int load_id, real_type new_q, SolverControl & solver_control)
+    {
+        if (q_mvar_(load_id) != new_q) {
+            solver_control.tell_recompute_sbus();
+            q_mvar_(load_id) = new_q;
+        }
+    }
     virtual void reconnect_connected_buses(std::vector<bool> & bus_status) const;
     virtual void disconnect_if_not_in_main_component(std::vector<bool> & busbar_in_main_component);