flixopt 2.1.5__tar.gz → 2.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flixopt might be problematic. Click here for more details.

Files changed (98) hide show
  1. {flixopt-2.1.5 → flixopt-2.1.6}/CHANGELOG.md +16 -0
  2. {flixopt-2.1.5 → flixopt-2.1.6}/PKG-INFO +23 -9
  3. {flixopt-2.1.5 → flixopt-2.1.6}/examples/02_Complex/complex_example.py +1 -0
  4. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/aggregation.py +0 -1
  5. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/components.py +130 -21
  6. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/flow_system.py +53 -0
  7. flixopt-2.1.6/flixopt/network_app.py +612 -0
  8. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt.egg-info/PKG-INFO +23 -9
  9. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt.egg-info/SOURCES.txt +2 -0
  10. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt.egg-info/requires.txt +18 -2
  11. {flixopt-2.1.5 → flixopt-2.1.6}/pyproject.toml +32 -16
  12. {flixopt-2.1.5 → flixopt-2.1.6}/tests/conftest.py +5 -6
  13. flixopt-2.1.6/tests/test_network_app.py +29 -0
  14. {flixopt-2.1.5 → flixopt-2.1.6}/.github/CONTRIBUTING.md +0 -0
  15. {flixopt-2.1.5 → flixopt-2.1.6}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  16. {flixopt-2.1.5 → flixopt-2.1.6}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  17. {flixopt-2.1.5 → flixopt-2.1.6}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  18. {flixopt-2.1.5 → flixopt-2.1.6}/.github/pull_request_template.md +0 -0
  19. {flixopt-2.1.5 → flixopt-2.1.6}/.github/workflows/python-app.yaml +0 -0
  20. {flixopt-2.1.5 → flixopt-2.1.6}/.gitignore +0 -0
  21. {flixopt-2.1.5 → flixopt-2.1.6}/LICENSE +0 -0
  22. {flixopt-2.1.5 → flixopt-2.1.6}/README.md +0 -0
  23. {flixopt-2.1.5 → flixopt-2.1.6}/docs/SUMMARY.md +0 -0
  24. {flixopt-2.1.5 → flixopt-2.1.6}/docs/contribute.md +0 -0
  25. {flixopt-2.1.5 → flixopt-2.1.6}/docs/examples/00-Minimal Example.md +0 -0
  26. {flixopt-2.1.5 → flixopt-2.1.6}/docs/examples/01-Basic Example.md +0 -0
  27. {flixopt-2.1.5 → flixopt-2.1.6}/docs/examples/02-Complex Example.md +0 -0
  28. {flixopt-2.1.5 → flixopt-2.1.6}/docs/examples/03-Calculation Modes.md +0 -0
  29. {flixopt-2.1.5 → flixopt-2.1.6}/docs/examples/index.md +0 -0
  30. {flixopt-2.1.5 → flixopt-2.1.6}/docs/faq/contribute.md +0 -0
  31. {flixopt-2.1.5 → flixopt-2.1.6}/docs/faq/index.md +0 -0
  32. {flixopt-2.1.5 → flixopt-2.1.6}/docs/getting-started.md +0 -0
  33. {flixopt-2.1.5 → flixopt-2.1.6}/docs/images/architecture_flixOpt-pre2.0.0.png +0 -0
  34. {flixopt-2.1.5 → flixopt-2.1.6}/docs/images/architecture_flixOpt.png +0 -0
  35. {flixopt-2.1.5 → flixopt-2.1.6}/docs/images/flixopt-icon.svg +0 -0
  36. {flixopt-2.1.5 → flixopt-2.1.6}/docs/index.md +0 -0
  37. {flixopt-2.1.5 → flixopt-2.1.6}/docs/javascripts/mathjax.js +0 -0
  38. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/Bus.md +0 -0
  39. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +0 -0
  40. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/Flow.md +0 -0
  41. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/LinearConverter.md +0 -0
  42. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/Piecewise.md +0 -0
  43. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/Storage.md +0 -0
  44. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/index.md +0 -0
  45. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/Mathematical Notation/others.md +0 -0
  46. {flixopt-2.1.5 → flixopt-2.1.6}/docs/user-guide/index.md +0 -0
  47. {flixopt-2.1.5 → flixopt-2.1.6}/examples/00_Minmal/minimal_example.py +0 -0
  48. {flixopt-2.1.5 → flixopt-2.1.6}/examples/01_Simple/simple_example.py +0 -0
  49. {flixopt-2.1.5 → flixopt-2.1.6}/examples/02_Complex/complex_example_results.py +0 -0
  50. {flixopt-2.1.5 → flixopt-2.1.6}/examples/03_Calculation_types/Zeitreihen2020.csv +0 -0
  51. {flixopt-2.1.5 → flixopt-2.1.6}/examples/03_Calculation_types/example_calculation_types.py +0 -0
  52. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/__init__.py +0 -0
  53. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/calculation.py +0 -0
  54. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/commons.py +0 -0
  55. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/config.py +0 -0
  56. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/config.yaml +0 -0
  57. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/core.py +0 -0
  58. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/effects.py +0 -0
  59. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/elements.py +0 -0
  60. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/features.py +0 -0
  61. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/interface.py +0 -0
  62. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/io.py +0 -0
  63. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/linear_converters.py +0 -0
  64. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/plotting.py +0 -0
  65. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/results.py +0 -0
  66. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/solvers.py +0 -0
  67. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/structure.py +0 -0
  68. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt/utils.py +0 -0
  69. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt.egg-info/dependency_links.txt +0 -0
  70. {flixopt-2.1.5 → flixopt-2.1.6}/flixopt.egg-info/top_level.txt +0 -0
  71. {flixopt-2.1.5 → flixopt-2.1.6}/mkdocs.yml +0 -0
  72. {flixopt-2.1.5 → flixopt-2.1.6}/pics/architecture_flixOpt-pre2.0.0.png +0 -0
  73. {flixopt-2.1.5 → flixopt-2.1.6}/pics/architecture_flixOpt.png +0 -0
  74. {flixopt-2.1.5 → flixopt-2.1.6}/pics/flixOpt_plotting.jpg +0 -0
  75. {flixopt-2.1.5 → flixopt-2.1.6}/pics/flixopt-icon.svg +0 -0
  76. {flixopt-2.1.5 → flixopt-2.1.6}/pics/pics.pptx +0 -0
  77. {flixopt-2.1.5 → flixopt-2.1.6}/scripts/extract_release_notes.py +0 -0
  78. {flixopt-2.1.5 → flixopt-2.1.6}/scripts/gen_ref_pages.py +0 -0
  79. {flixopt-2.1.5 → flixopt-2.1.6}/setup.cfg +0 -0
  80. {flixopt-2.1.5 → flixopt-2.1.6}/tests/__init__.py +0 -0
  81. {flixopt-2.1.5 → flixopt-2.1.6}/tests/ressources/Zeitreihen2020.csv +0 -0
  82. {flixopt-2.1.5 → flixopt-2.1.6}/tests/run_all_tests.py +0 -0
  83. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_bus.py +0 -0
  84. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_component.py +0 -0
  85. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_dataconverter.py +0 -0
  86. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_effect.py +0 -0
  87. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_examples.py +0 -0
  88. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_flow.py +0 -0
  89. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_functional.py +0 -0
  90. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_integration.py +0 -0
  91. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_io.py +0 -0
  92. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_linear_converter.py +0 -0
  93. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_on_hours_computation.py +0 -0
  94. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_plots.py +0 -0
  95. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_results_plots.py +0 -0
  96. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_storage.py +0 -0
  97. {flixopt-2.1.5 → flixopt-2.1.6}/tests/test_timeseries.py +0 -0
  98. {flixopt-2.1.5 → flixopt-2.1.6}/tests/todos.txt +0 -0
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+
11
+ ## [2.1.6] - 2025-09-02
12
+
13
+ ### Changed
14
+ - `Sink`, `Source` and `SourceAndSink` now accept multiple `flows` as `inputs` and `outputs` instead of just one. This enables to model more use cases using these classes. [[#291](https://github.com/flixOpt/flixopt/pull/291) by [@FBumann](https://github.com/FBumann)]
15
+ - Further, both `Sink` and `Source` now have a `prevent_simultaneous_flow_rates` argument to prevent simultaneous flow rates of more than one of their Flows. [[#291](https://github.com/flixOpt/flixopt/pull/291) by [@FBumann](https://github.com/FBumann)]
16
+
17
+ ### Added
18
+ - Added `FlowSystem.start_network_app()` and `FlowSystem.stop_network_app()` to easily visualize the network structure of a flow system in an interactive dash web app. This is still experimental and might change in the future. [[#293](https://github.com/flixOpt/flixopt/pull/293) by [@FBumann](https://github.com/FBumann)]
19
+
20
+ ### Deprecated
21
+ - For the classes `Sink`, `Source` and `SourceAndSink`: `.sink`, `.source` and `.prevent_simultaneous_sink_and_source` are deprecated in favor of the new arguments `inputs`, `outputs` and `prevent_simultaneous_flow_rates`. [[#291](https://github.com/flixOpt/flixopt/pull/291) by [@FBumann](https://github.com/FBumann)]
22
+
23
+ ### Fixed
24
+ - Fixed testing issue with new `linopy` version 0.5.6 [[#296](https://github.com/flixOpt/flixopt/pull/296) by [@FBumann](https://github.com/FBumann)]
25
+
10
26
  ## [2.1.5] - 2025-07-08
11
27
 
12
28
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flixopt
3
- Version: 2.1.5
3
+ Version: 2.1.6
4
4
  Summary: Vector based energy and material flow optimization framework in Python.
5
5
  Author-email: "Chair of Building Energy Systems and Heat Supply, TU Dresden" <peter.stange@tu-dresden.de>, Felix Bumann <felixbumann387@gmail.com>, Felix Panitz <baumbude@googlemail.com>, Peter Stange <peter.stange@tu-dresden.de>
6
6
  Maintainer-email: Felix Bumann <felixbumann387@gmail.com>, Peter Stange <peter.stange@tu-dresden.de>
@@ -18,13 +18,12 @@ Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Intended Audience :: Developers
19
19
  Classifier: Intended Audience :: Science/Research
20
20
  Classifier: Topic :: Scientific/Engineering
21
- Classifier: License :: OSI Approved :: MIT License
22
21
  Requires-Python: >=3.10
23
22
  Description-Content-Type: text/markdown
24
23
  License-File: LICENSE
25
24
  Requires-Dist: numpy<3,>=1.21.5
26
25
  Requires-Dist: pandas<3,>=2.0.0
27
- Requires-Dist: linopy<0.6.0,>=0.5.1
26
+ Requires-Dist: linopy<0.5.6,>=0.5.1
28
27
  Requires-Dist: netcdf4<2,>=1.6.1
29
28
  Requires-Dist: PyYAML<7,>=6.0.0
30
29
  Requires-Dist: rich>=13.0.0
@@ -32,6 +31,22 @@ Requires-Dist: tomli>=2.0.1; python_version < "3.11"
32
31
  Requires-Dist: highspy>=1.5.3
33
32
  Requires-Dist: matplotlib<4.0.0,>=3.5.2
34
33
  Requires-Dist: plotly<6.0.0,>=5.15.0
34
+ Provides-Extra: network-viz
35
+ Requires-Dist: dash>=3.0.0; extra == "network-viz"
36
+ Requires-Dist: dash-cytoscape>=1.0.0; extra == "network-viz"
37
+ Requires-Dist: dash-daq>=0.6.0; extra == "network-viz"
38
+ Requires-Dist: networkx>=3.0.0; extra == "network-viz"
39
+ Requires-Dist: werkzeug>=3.0.0; extra == "network-viz"
40
+ Provides-Extra: full
41
+ Requires-Dist: pyvis==0.3.1; extra == "full"
42
+ Requires-Dist: tsam<3.0.0,>=2.3.1; extra == "full"
43
+ Requires-Dist: scipy<2.0.0,>=1.15.1; extra == "full"
44
+ Requires-Dist: gurobipy>=10.0.0; extra == "full"
45
+ Requires-Dist: dash>=3.0.0; extra == "full"
46
+ Requires-Dist: dash-cytoscape>=1.0.0; extra == "full"
47
+ Requires-Dist: dash-daq>=0.6.0; extra == "full"
48
+ Requires-Dist: networkx>=3.0.0; extra == "full"
49
+ Requires-Dist: werkzeug>=3.0.0; extra == "full"
35
50
  Provides-Extra: dev
36
51
  Requires-Dist: pytest>=7.0.0; extra == "dev"
37
52
  Requires-Dist: ruff>=0.9.0; extra == "dev"
@@ -39,12 +54,11 @@ Requires-Dist: pyvis==0.3.1; extra == "dev"
39
54
  Requires-Dist: tsam<3.0.0,>=2.3.1; extra == "dev"
40
55
  Requires-Dist: scipy<2.0.0,>=1.15.1; extra == "dev"
41
56
  Requires-Dist: gurobipy>=10.0.0; extra == "dev"
42
- Provides-Extra: full
43
- Requires-Dist: pyvis==0.3.1; extra == "full"
44
- Requires-Dist: tsam<3.0.0,>=2.3.1; extra == "full"
45
- Requires-Dist: scipy<2.0.0,>=1.15.1; extra == "full"
46
- Requires-Dist: streamlit<2.0.0,>=1.44.0; extra == "full"
47
- Requires-Dist: gurobipy>=10.0.0; extra == "full"
57
+ Requires-Dist: dash>=3.0.0; extra == "dev"
58
+ Requires-Dist: dash-cytoscape>=1.0.0; extra == "dev"
59
+ Requires-Dist: dash-daq>=0.6.0; extra == "dev"
60
+ Requires-Dist: networkx>=3.0.0; extra == "dev"
61
+ Requires-Dist: werkzeug>=3.0.0; extra == "dev"
48
62
  Provides-Extra: docs
49
63
  Requires-Dist: mkdocs-material<10,>=9.0.0; extra == "docs"
50
64
  Requires-Dist: mkdocstrings-python>=1.0.0; extra == "docs"
@@ -182,6 +182,7 @@ if __name__ == '__main__':
182
182
  flow_system.add_elements(bhkw_2) if use_chp_with_piecewise_conversion else flow_system.add_elements(bhkw)
183
183
 
184
184
  pprint(flow_system) # Get a string representation of the FlowSystem
185
+ flow_system.start_network_app() # Start the network app. DOes only work with extra dependencies installed
185
186
 
186
187
  # --- Solve FlowSystem ---
187
188
  calculation = fx.FullCalculation('complex example', flow_system, time_indices)
@@ -34,7 +34,6 @@ from .structure import (
34
34
  if TYPE_CHECKING:
35
35
  import plotly.graph_objects as go
36
36
 
37
- warnings.filterwarnings('ignore', category=DeprecationWarning)
38
37
  logger = logging.getLogger('flixopt')
39
38
 
40
39
 
@@ -3,6 +3,7 @@ This module contains the basic components of the flixopt framework.
3
3
  """
4
4
 
5
5
  import logging
6
+ import warnings
6
7
  from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Set, Tuple, Union
7
8
 
8
9
  import linopy
@@ -586,52 +587,160 @@ class SourceAndSink(Component):
586
587
  def __init__(
587
588
  self,
588
589
  label: str,
589
- source: Flow,
590
- sink: Flow,
591
- prevent_simultaneous_sink_and_source: bool = True,
590
+ inputs: List[Flow] = None,
591
+ outputs: List[Flow] = None,
592
+ prevent_simultaneous_flow_rates: bool = True,
592
593
  meta_data: Optional[Dict] = None,
594
+ **kwargs,
593
595
  ):
594
596
  """
595
597
  Args:
596
598
  label: The label of the Element. Used to identify it in the FlowSystem
597
- source: output-flow of this component
598
- sink: input-flow of this component
599
- prevent_simultaneous_sink_and_source: If True, inflow and outflow can not be active simultaniously.
599
+ outputs: output-flows of this component
600
+ inputs: input-flows of this component
601
+ prevent_simultaneous_flow_rates: If True, inflow and outflow can not be active simultaniously.
600
602
  meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
601
603
  """
604
+ source = kwargs.pop('source', None)
605
+ sink = kwargs.pop('sink', None)
606
+ prevent_simultaneous_sink_and_source = kwargs.pop('prevent_simultaneous_sink_and_source', None)
607
+ if source is not None:
608
+ warnings.deprecated(
609
+ 'The use of the source argument is deprecated. Use the outputs argument instead.',
610
+ stacklevel=2,
611
+ )
612
+ if outputs is not None:
613
+ raise ValueError('Either source or outputs can be specified, but not both.')
614
+ outputs = [source]
615
+
616
+ if sink is not None:
617
+ warnings.deprecated(
618
+ 'The use of the sink argument is deprecated. Use the outputs argument instead.',
619
+ stacklevel=2,
620
+ )
621
+ if inputs is not None:
622
+ raise ValueError('Either sink or outputs can be specified, but not both.')
623
+ inputs = [sink]
624
+
625
+ if prevent_simultaneous_sink_and_source is not None:
626
+ warnings.deprecated(
627
+ 'The use of the prevent_simultaneous_sink_and_source argument is deprecated. Use the prevent_simultaneous_flow_rates argument instead.',
628
+ stacklevel=2,
629
+ )
630
+ prevent_simultaneous_flow_rates = prevent_simultaneous_sink_and_source
631
+
602
632
  super().__init__(
603
633
  label,
604
- inputs=[sink],
605
- outputs=[source],
606
- prevent_simultaneous_flows=[sink, source] if prevent_simultaneous_sink_and_source is True else None,
634
+ inputs=inputs,
635
+ outputs=outputs,
636
+ prevent_simultaneous_flows=inputs + outputs if prevent_simultaneous_flow_rates is True else None,
607
637
  meta_data=meta_data,
608
638
  )
609
- self.source = source
610
- self.sink = sink
611
- self.prevent_simultaneous_sink_and_source = prevent_simultaneous_sink_and_source
639
+ self.prevent_simultaneous_flow_rates = prevent_simultaneous_flow_rates
640
+
641
+ @property
642
+ def source(self) -> Flow:
643
+ warnings.warn(
644
+ 'The source property is deprecated. Use the outputs property instead.',
645
+ DeprecationWarning,
646
+ stacklevel=2,
647
+ )
648
+ return self.outputs[0]
649
+
650
+ @property
651
+ def sink(self) -> Flow:
652
+ warnings.warn(
653
+ 'The sink property is deprecated. Use the outputs property instead.',
654
+ DeprecationWarning,
655
+ stacklevel=2,
656
+ )
657
+ return self.inputs[0]
658
+
659
+ @property
660
+ def prevent_simultaneous_sink_and_source(self) -> bool:
661
+ warnings.warn(
662
+ 'The prevent_simultaneous_sink_and_source property is deprecated. Use the prevent_simultaneous_flow_rates property instead.',
663
+ DeprecationWarning,
664
+ stacklevel=2,
665
+ )
666
+ return self.prevent_simultaneous_flow_rates
612
667
 
613
668
 
614
669
  @register_class_for_io
615
670
  class Source(Component):
616
- def __init__(self, label: str, source: Flow, meta_data: Optional[Dict] = None):
671
+ def __init__(
672
+ self,
673
+ label: str,
674
+ outputs: List[Flow] = None,
675
+ meta_data: Optional[Dict] = None,
676
+ prevent_simultaneous_flow_rates: bool = False,
677
+ **kwargs
678
+ ):
617
679
  """
618
680
  Args:
619
681
  label: The label of the Element. Used to identify it in the FlowSystem
620
- source: output-flow of source
682
+ outputs: output-flows of source
621
683
  meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
622
684
  """
623
- super().__init__(label, outputs=[source], meta_data=meta_data)
624
- self.source = source
685
+ source = kwargs.pop('source', None)
686
+ if source is not None:
687
+ warnings.warn(
688
+ 'The use of the source argument is deprecated. Use the outputs argument instead.',
689
+ DeprecationWarning,
690
+ stacklevel=2,
691
+ )
692
+ if outputs is not None:
693
+ raise ValueError('Either source or outputs can be specified, but not both.')
694
+ outputs = [source]
695
+
696
+ self.prevent_simultaneous_flow_rates = prevent_simultaneous_flow_rates
697
+ super().__init__(label, outputs=outputs, meta_data=meta_data, prevent_simultaneous_flows=outputs if prevent_simultaneous_flow_rates else None)
698
+
699
+ @property
700
+ def source(self) -> Flow:
701
+ warnings.warn(
702
+ 'The source property is deprecated. Use the outputs property instead.',
703
+ DeprecationWarning,
704
+ stacklevel=2,
705
+ )
706
+ return self.outputs[0]
625
707
 
626
708
 
627
709
  @register_class_for_io
628
710
  class Sink(Component):
629
- def __init__(self, label: str, sink: Flow, meta_data: Optional[Dict] = None):
711
+ def __init__(
712
+ self,
713
+ label: str,
714
+ inputs: List[Flow] = None,
715
+ meta_data: Optional[Dict] = None,
716
+ prevent_simultaneous_flow_rates: bool = False,
717
+ **kwargs
718
+ ):
630
719
  """
631
720
  Args:
632
721
  label: The label of the Element. Used to identify it in the FlowSystem
633
- meta_data: used to store more information about the element. Is not used internally, but saved in the results
634
- sink: input-flow of sink
722
+ inputs: output-flows of source
723
+ meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
635
724
  """
636
- super().__init__(label, inputs=[sink], meta_data=meta_data)
637
- self.sink = sink
725
+ sink = kwargs.pop('sink', None)
726
+ if sink is not None:
727
+ warnings.warn(
728
+ 'The use of the sink argument is deprecated. Use the outputs argument instead.',
729
+ DeprecationWarning,
730
+ stacklevel=2,
731
+ )
732
+ if inputs is not None:
733
+ raise ValueError('Either sink or outputs can be specified, but not both.')
734
+ inputs = [sink]
735
+
736
+ self.prevent_simultaneous_flow_rates = prevent_simultaneous_flow_rates
737
+ super().__init__(label, inputs=inputs, meta_data=meta_data, prevent_simultaneous_flows=inputs if prevent_simultaneous_flow_rates else None)
738
+
739
+ @property
740
+ def sink(self) -> Flow:
741
+ warnings.warn(
742
+ 'The sink property is deprecated. Use the outputs property instead.',
743
+ DeprecationWarning,
744
+ stacklevel=2,
745
+ )
746
+ return self.inputs[0]
@@ -61,6 +61,8 @@ class FlowSystem:
61
61
 
62
62
  self._connected = False
63
63
 
64
+ self._network_app = None
65
+
64
66
  @classmethod
65
67
  def from_dataset(cls, ds: xr.Dataset):
66
68
  timesteps_extra = pd.DatetimeIndex(ds.attrs['timesteps_extra'], name='time')
@@ -241,6 +243,57 @@ class FlowSystem:
241
243
  node_infos, edge_infos = self.network_infos()
242
244
  return plotting.plot_network(node_infos, edge_infos, path, controls, show)
243
245
 
246
+ def start_network_app(self):
247
+ """Visualizes the network structure of a FlowSystem using Dash, Cytoscape, and networkx.
248
+ Requires optional dependencies: dash, dash-cytoscape, networkx, werkzeug.
249
+ """
250
+ from .network_app import DASH_CYTOSCAPE_AVAILABLE, VISUALIZATION_ERROR, flow_graph, shownetwork
251
+
252
+ warnings.warn(
253
+ 'The network visualization is still experimental and might change in the future.',
254
+ stacklevel=2,
255
+ category=UserWarning,
256
+ )
257
+
258
+ if not DASH_CYTOSCAPE_AVAILABLE:
259
+ raise ImportError(
260
+ f"Network visualization requires optional dependencies. "
261
+ f"Install with: pip install flixopt[viz], flixopt[full] or pip install dash dash_cytoscape networkx werkzeug. "
262
+ f"Original error: {VISUALIZATION_ERROR}"
263
+ )
264
+
265
+ if not self._connected:
266
+ self._connect_network()
267
+
268
+ if self._network_app is not None:
269
+ logger.warning('The network app is already running. Restarting it.')
270
+ self.stop_network_app()
271
+
272
+ self._network_app = shownetwork(flow_graph(self))
273
+
274
+ def stop_network_app(self):
275
+ """Stop the network visualization server."""
276
+ from .network_app import DASH_CYTOSCAPE_AVAILABLE, VISUALIZATION_ERROR
277
+ if not DASH_CYTOSCAPE_AVAILABLE:
278
+ raise ImportError(
279
+ f'Network visualization requires optional dependencies. '
280
+ f'Install with: pip install flixopt[viz]. '
281
+ f'Original error: {VISUALIZATION_ERROR}'
282
+ )
283
+
284
+ if self._network_app is None:
285
+ logger.warning('No network app is currently running. Cant stop it')
286
+ return
287
+
288
+ try:
289
+ logger.info('Stopping network visualization server...')
290
+ self._network_app.server_instance.shutdown()
291
+ logger.info('Network visualization stopped.')
292
+ except Exception as e:
293
+ logger.error(f'Failed to stop the network visualization app: {e}')
294
+ finally:
295
+ self._network_app = None
296
+
244
297
  def network_infos(self) -> Tuple[Dict[str, Dict[str, str]], Dict[str, Dict[str, str]]]:
245
298
  if not self._connected:
246
299
  self._connect_network()