Skip to content

Add more test cases #228

Add more test cases

Add more test cases #228

GitHub Actions / Test Results failed Aug 11, 2023 in 0s

2 fail, 16 skipped, 475 pass in 2m 25s

493 tests   475 ✔️  2m 25s ⏱️
    1 suites    16 💤
    1 files        2

Results for commit 42898d8.

Annotations

Check warning on line 0 in tests.api.test_energy_model_optimization

See this annotation in the file changed.

@github-actions github-actions / Test Results

test_optimize_model[1node_Example] (tests.api.test_energy_model_optimization) failed

result.xml [took 3s]
Raw output
TypeError: At least one solver must be installed. Have a look at the FINE documentation to see how to install possible solvers. https://vsa-fine.readthedocs.io/en/latest/
client = <starlette.testclient.TestClient object at 0x7ff582dfd3c0>
normal_user_headers = {'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTI0NDg1NzgsInN1YiI6IjEifQ.dzGB81rDfKN5LWyxbvHhosqhFGdvZ-V3BS5BdnfB8Js'}
db = <sqlalchemy.orm.session.Session object at 0x7ff58064c3d0>
data_folder = '1node_Example'

    @pytest.mark.slow
    @pytest.mark.parametrize("data_folder", ["1node_Example", "Multi-regional_Example"])
    def test_optimize_model(client: TestClient, normal_user_headers: Dict[str, str], db: Session, data_folder: str):
        """
        Test optimizing an energy model.
        """
        example_model = data_generator.create_example_model(db, data_folder)
>       response = client.get(f"/models/{example_model.id}/optimize/", headers=normal_user_headers)

tests/api/test_energy_model_optimization.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:499: in get
    return super().get(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:1041: in get
    return self.request(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:465: in request
    return super().request(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:814: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:901: in send
    response = self._send_handling_auth(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:929: in _send_handling_auth
    response = self._send_handling_redirects(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:966: in _send_handling_redirects
    response = self._send_single_request(request)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:1002: in _send_single_request
    response = transport.handle_request(request)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:342: in handle_request
    raise exc
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:339: in handle_request
    portal.call(self.app, scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/from_thread.py:277: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/concurrent/futures/_base.py:458: in result
    return self.__get_result()
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/concurrent/futures/_base.py:403: in __get_result
    raise self._exception
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/from_thread.py:217: in _call_func
    retval = await retval
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/applications.py:289: in __call__
    await super().__call__(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/applications.py:122: in __call__
    await self.middleware_stack(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/errors.py:184: in __call__
    raise exc
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/errors.py:162: in __call__
    await self.app(scope, receive, _send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/exceptions.py:79: in __call__
    raise exc
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/exceptions.py:68: in __call__
    await self.app(scope, receive, sender)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py:20: in __call__
    raise e
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py:17: in __call__
    await self.app(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/routing.py:718: in __call__
    await route.handle(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/routing.py:276: in handle
    await self.app(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/routing.py:66: in app
    response = await func(request)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/routing.py:273: in app
    raw_response = await run_endpoint_function(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/routing.py:192: in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/concurrency.py:41: in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/to_thread.py:33: in run_sync
    return await get_asynclib().run_sync_in_worker_thread(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/_backends/_asyncio.py:877: in run_sync_in_worker_thread
    return await future
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/_backends/_asyncio.py:807: in run
    result = context.run(func, *args)
ensysmod/api/endpoints/energy_models.py:131: in optimize_model
    result_file_path = optimize_esm(esM=esM)
ensysmod/core/fine_esm.py:215: in optimize_esm
    esM.optimize(timeSeriesAggregation=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FINE.energySystemModel.EnergySystemModel object at 0x7ff582fc31c0>
declaresOptimizationProblem = True, relaxIsBuiltBinary = False
timeSeriesAggregation = True, logFileName = '', threads = 3, solver = 'None'
timeLimit = None, optimizationSpecs = '', warmstart = False

    def optimize(
        self,
        declaresOptimizationProblem=True,
        relaxIsBuiltBinary=False,
        timeSeriesAggregation=False,
        logFileName="",
        threads=3,
        solver="None",
        timeLimit=None,
        optimizationSpecs="",
        warmstart=False,
    ):
        """
        Optimize the specified energy system for which a pyomo ConcreteModel instance is built or called upon.
        A pyomo instance is optimized with the specified inputs, and the optimization results are further
        processed.
    
        **Default arguments:**
    
        :param declaresOptimizationProblem: states if the optimization problem should be declared (True) or not (False).
    
            (a) If true, the declareOptimizationProblem function is called and a pyomo ConcreteModel instance is built.
            (b) If false a previously declared pyomo ConcreteModel instance is used.
    
            |br| * the default value is True
        :type declaresOptimizationProblem: boolean
    
        :param relaxIsBuiltBinary: states if the optimization problem should be solved as a relaxed LP to get the lower
            bound of the problem.
            |br| * the default value is False
        :type declaresOptimizationProblem: boolean
    
        :param timeSeriesAggregation: states if the optimization of the energy system model should be done with
    
            (a) the full time series (False) or
            (b) clustered time series data (True).
    
            |br| * the default value is False
        :type timeSeriesAggregation: boolean
    
        :param segmentation: states if the optimization of the energy system model based on clustered time series data
            should be done with
    
            (a) aggregated typical periods with the original time step length (False) or
            (b) aggregated typical periods with further segmented time steps (True).
    
            |br| * the default value is False
        :type segmentation: boolean
    
        :param logFileName: logFileName is used for naming the log file of the optimization solver output
            if gurobi is used as the optimization solver.
            If the logFileName is given as an absolute path (e.g. logFileName = os.path.join(os.getcwd(),
            'Results', 'logFileName.txt')) the log file will be stored in the specified directory. Otherwise,
            it will be stored by default in the directory where the executing python script is called.
            |br| * the default value is 'job'
        :type logFileName: string
    
        :param threads: number of computational threads used for solving the optimization (solver dependent
            input) if gurobi is used as the solver. A value of 0 results in using all available threads. If
            a value larger than the available number of threads are chosen, the value will reset to the maximum
            number of threads.
            |br| * the default value is 3
        :type threads: positive integer
    
        :param solver: specifies which solver should solve the optimization problem (which of course has to be
            installed on the machine on which the model is run).
            |br| * the default value is 'gurobi'
        :type solver: string
    
        :param timeLimit: if not specified as None, indicates the maximum solve time of the optimization problem
            in seconds (solver dependent input). The use of this parameter is suggested when running models in
            runtime restricted environments (such as clusters with job submission systems). If the runtime
            limitation is triggered before an optimal solution is available, the best solution obtained up
            until then (if available) is processed.
            |br| * the default value is None
        :type timeLimit: strictly positive integer or None
    
        :param optimizationSpecs: specifies parameters for the optimization solver (see the respective solver
            documentation for more information). Example: 'LogToConsole=1 OptimalityTol=1e-6'
            |br| * the default value is an empty string ('')
        :type optimizationSpecs: string
    
        :param warmstart: specifies if a warm start of the optimization should be considered
            (not always supported by the solvers).
            |br| * the default value is False
        :type warmstart: boolean
        """
    
        if not timeSeriesAggregation:
            self.segmentation = False
    
        if declaresOptimizationProblem:
            self.declareOptimizationProblem(
                timeSeriesAggregation=timeSeriesAggregation,
                segmentation=self.segmentation,
                relaxIsBuiltBinary=relaxIsBuiltBinary,
            )
        else:
            if self.pyM is None:
                raise TypeError(
                    "The optimization problem is not declared yet. Set the argument declaresOptimization"
                    " problem to True or call the declareOptimizationProblem function first."
                )
    
        # Get starting time of the optimization to, later on, obtain the total run time of the optimize function call
        timeStart = time.time()
    
        # Check correctness of inputs
        utils.checkOptimizeInput(
            timeSeriesAggregation,
            self.isTimeSeriesDataClustered,
            logFileName,
            threads,
            solver,
            timeLimit,
            optimizationSpecs,
            warmstart,
        )
    
        # Store keyword arguments in the EnergySystemModel instance
        self.solverSpecs["logFileName"], self.solverSpecs["threads"] = (
            logFileName,
            threads,
        )
        self.solverSpecs["solver"], self.solverSpecs["timeLimit"] = solver, timeLimit
        self.solverSpecs["optimizationSpecs"], self.solverSpecs["hasTSA"] = (
            optimizationSpecs,
            timeSeriesAggregation,
        )
    
        # Check which solvers are available and choose default solver if no solver is specified explicitely
        # Order of possible solvers in solverList defines the priority of chosen default solver.
        solverList = ["gurobi", "glpk", "cbc"]
    
        if solver != "None":
            try:
                opt.SolverFactory(solver).available()
            except:
                solver = "None"
    
        if solver == "None":
            for nSolver in solverList:
                if solver == "None":
                    try:
                        if opt.SolverFactory(nSolver).available():
                            solver = nSolver
                            utils.output(
                                "Either solver not selected or specified solver not available."
                                + str(nSolver)
                                + " is set as solver.",
                                self.verbose,
                                0,
                            )
                    except:
                        pass
    
        if solver == "None":
>           raise TypeError(
                "At least one solver must be installed."
                " Have a look at the FINE documentation to see how to install possible solvers."
                " https://vsa-fine.readthedocs.io/en/latest/"
            )
E           TypeError: At least one solver must be installed. Have a look at the FINE documentation to see how to install possible solvers. https://vsa-fine.readthedocs.io/en/latest/

/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/FINE/energySystemModel.py:1602: TypeError

Check warning on line 0 in tests.api.test_energy_model_optimization

See this annotation in the file changed.

@github-actions github-actions / Test Results

test_optimize_model[Multi-regional_Example] (tests.api.test_energy_model_optimization) failed

result.xml [took 31s]
Raw output
TypeError: At least one solver must be installed. Have a look at the FINE documentation to see how to install possible solvers. https://vsa-fine.readthedocs.io/en/latest/
client = <starlette.testclient.TestClient object at 0x7ff582dfd3c0>
normal_user_headers = {'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTI0NDg1NzgsInN1YiI6IjEifQ.dzGB81rDfKN5LWyxbvHhosqhFGdvZ-V3BS5BdnfB8Js'}
db = <sqlalchemy.orm.session.Session object at 0x7ff582915b10>
data_folder = 'Multi-regional_Example'

    @pytest.mark.slow
    @pytest.mark.parametrize("data_folder", ["1node_Example", "Multi-regional_Example"])
    def test_optimize_model(client: TestClient, normal_user_headers: Dict[str, str], db: Session, data_folder: str):
        """
        Test optimizing an energy model.
        """
        example_model = data_generator.create_example_model(db, data_folder)
>       response = client.get(f"/models/{example_model.id}/optimize/", headers=normal_user_headers)

tests/api/test_energy_model_optimization.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:499: in get
    return super().get(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:1041: in get
    return self.request(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:465: in request
    return super().request(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:814: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:901: in send
    response = self._send_handling_auth(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:929: in _send_handling_auth
    response = self._send_handling_redirects(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:966: in _send_handling_redirects
    response = self._send_single_request(request)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/httpx/_client.py:1002: in _send_single_request
    response = transport.handle_request(request)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:342: in handle_request
    raise exc
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/testclient.py:339: in handle_request
    portal.call(self.app, scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/from_thread.py:277: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/concurrent/futures/_base.py:458: in result
    return self.__get_result()
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/concurrent/futures/_base.py:403: in __get_result
    raise self._exception
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/from_thread.py:217: in _call_func
    retval = await retval
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/applications.py:289: in __call__
    await super().__call__(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/applications.py:122: in __call__
    await self.middleware_stack(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/errors.py:184: in __call__
    raise exc
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/errors.py:162: in __call__
    await self.app(scope, receive, _send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/exceptions.py:79: in __call__
    raise exc
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/middleware/exceptions.py:68: in __call__
    await self.app(scope, receive, sender)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py:20: in __call__
    raise e
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py:17: in __call__
    await self.app(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/routing.py:718: in __call__
    await route.handle(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/routing.py:276: in handle
    await self.app(scope, receive, send)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/routing.py:66: in app
    response = await func(request)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/routing.py:273: in app
    raw_response = await run_endpoint_function(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/fastapi/routing.py:192: in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/starlette/concurrency.py:41: in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/to_thread.py:33: in run_sync
    return await get_asynclib().run_sync_in_worker_thread(
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/_backends/_asyncio.py:877: in run_sync_in_worker_thread
    return await future
/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/anyio/_backends/_asyncio.py:807: in run
    result = context.run(func, *args)
ensysmod/api/endpoints/energy_models.py:131: in optimize_model
    result_file_path = optimize_esm(esM=esM)
ensysmod/core/fine_esm.py:215: in optimize_esm
    esM.optimize(timeSeriesAggregation=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FINE.energySystemModel.EnergySystemModel object at 0x7ff580f19690>
declaresOptimizationProblem = True, relaxIsBuiltBinary = False
timeSeriesAggregation = True, logFileName = '', threads = 3, solver = 'None'
timeLimit = None, optimizationSpecs = '', warmstart = False

    def optimize(
        self,
        declaresOptimizationProblem=True,
        relaxIsBuiltBinary=False,
        timeSeriesAggregation=False,
        logFileName="",
        threads=3,
        solver="None",
        timeLimit=None,
        optimizationSpecs="",
        warmstart=False,
    ):
        """
        Optimize the specified energy system for which a pyomo ConcreteModel instance is built or called upon.
        A pyomo instance is optimized with the specified inputs, and the optimization results are further
        processed.
    
        **Default arguments:**
    
        :param declaresOptimizationProblem: states if the optimization problem should be declared (True) or not (False).
    
            (a) If true, the declareOptimizationProblem function is called and a pyomo ConcreteModel instance is built.
            (b) If false a previously declared pyomo ConcreteModel instance is used.
    
            |br| * the default value is True
        :type declaresOptimizationProblem: boolean
    
        :param relaxIsBuiltBinary: states if the optimization problem should be solved as a relaxed LP to get the lower
            bound of the problem.
            |br| * the default value is False
        :type declaresOptimizationProblem: boolean
    
        :param timeSeriesAggregation: states if the optimization of the energy system model should be done with
    
            (a) the full time series (False) or
            (b) clustered time series data (True).
    
            |br| * the default value is False
        :type timeSeriesAggregation: boolean
    
        :param segmentation: states if the optimization of the energy system model based on clustered time series data
            should be done with
    
            (a) aggregated typical periods with the original time step length (False) or
            (b) aggregated typical periods with further segmented time steps (True).
    
            |br| * the default value is False
        :type segmentation: boolean
    
        :param logFileName: logFileName is used for naming the log file of the optimization solver output
            if gurobi is used as the optimization solver.
            If the logFileName is given as an absolute path (e.g. logFileName = os.path.join(os.getcwd(),
            'Results', 'logFileName.txt')) the log file will be stored in the specified directory. Otherwise,
            it will be stored by default in the directory where the executing python script is called.
            |br| * the default value is 'job'
        :type logFileName: string
    
        :param threads: number of computational threads used for solving the optimization (solver dependent
            input) if gurobi is used as the solver. A value of 0 results in using all available threads. If
            a value larger than the available number of threads are chosen, the value will reset to the maximum
            number of threads.
            |br| * the default value is 3
        :type threads: positive integer
    
        :param solver: specifies which solver should solve the optimization problem (which of course has to be
            installed on the machine on which the model is run).
            |br| * the default value is 'gurobi'
        :type solver: string
    
        :param timeLimit: if not specified as None, indicates the maximum solve time of the optimization problem
            in seconds (solver dependent input). The use of this parameter is suggested when running models in
            runtime restricted environments (such as clusters with job submission systems). If the runtime
            limitation is triggered before an optimal solution is available, the best solution obtained up
            until then (if available) is processed.
            |br| * the default value is None
        :type timeLimit: strictly positive integer or None
    
        :param optimizationSpecs: specifies parameters for the optimization solver (see the respective solver
            documentation for more information). Example: 'LogToConsole=1 OptimalityTol=1e-6'
            |br| * the default value is an empty string ('')
        :type optimizationSpecs: string
    
        :param warmstart: specifies if a warm start of the optimization should be considered
            (not always supported by the solvers).
            |br| * the default value is False
        :type warmstart: boolean
        """
    
        if not timeSeriesAggregation:
            self.segmentation = False
    
        if declaresOptimizationProblem:
            self.declareOptimizationProblem(
                timeSeriesAggregation=timeSeriesAggregation,
                segmentation=self.segmentation,
                relaxIsBuiltBinary=relaxIsBuiltBinary,
            )
        else:
            if self.pyM is None:
                raise TypeError(
                    "The optimization problem is not declared yet. Set the argument declaresOptimization"
                    " problem to True or call the declareOptimizationProblem function first."
                )
    
        # Get starting time of the optimization to, later on, obtain the total run time of the optimize function call
        timeStart = time.time()
    
        # Check correctness of inputs
        utils.checkOptimizeInput(
            timeSeriesAggregation,
            self.isTimeSeriesDataClustered,
            logFileName,
            threads,
            solver,
            timeLimit,
            optimizationSpecs,
            warmstart,
        )
    
        # Store keyword arguments in the EnergySystemModel instance
        self.solverSpecs["logFileName"], self.solverSpecs["threads"] = (
            logFileName,
            threads,
        )
        self.solverSpecs["solver"], self.solverSpecs["timeLimit"] = solver, timeLimit
        self.solverSpecs["optimizationSpecs"], self.solverSpecs["hasTSA"] = (
            optimizationSpecs,
            timeSeriesAggregation,
        )
    
        # Check which solvers are available and choose default solver if no solver is specified explicitely
        # Order of possible solvers in solverList defines the priority of chosen default solver.
        solverList = ["gurobi", "glpk", "cbc"]
    
        if solver != "None":
            try:
                opt.SolverFactory(solver).available()
            except:
                solver = "None"
    
        if solver == "None":
            for nSolver in solverList:
                if solver == "None":
                    try:
                        if opt.SolverFactory(nSolver).available():
                            solver = nSolver
                            utils.output(
                                "Either solver not selected or specified solver not available."
                                + str(nSolver)
                                + " is set as solver.",
                                self.verbose,
                                0,
                            )
                    except:
                        pass
    
        if solver == "None":
>           raise TypeError(
                "At least one solver must be installed."
                " Have a look at the FINE documentation to see how to install possible solvers."
                " https://vsa-fine.readthedocs.io/en/latest/"
            )
E           TypeError: At least one solver must be installed. Have a look at the FINE documentation to see how to install possible solvers. https://vsa-fine.readthedocs.io/en/latest/

/opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages/FINE/energySystemModel.py:1602: TypeError