foxes 0.8.2__py3-none-any.whl → 1.0__py3-none-any.whl

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 foxes might be problematic. Click here for more details.

Files changed (174) hide show
  1. docs/source/conf.py +353 -0
  2. examples/abl_states/run.py +160 -0
  3. examples/compare_rotors_pwakes/run.py +217 -0
  4. examples/compare_wakes/run.py +241 -0
  5. examples/dyn_wakes/run.py +311 -0
  6. examples/field_data_nc/run.py +121 -0
  7. examples/induction_RHB/run.py +201 -0
  8. examples/multi_height/run.py +113 -0
  9. examples/power_mask/run.py +249 -0
  10. examples/random_timeseries/run.py +210 -0
  11. examples/scan_row/run.py +193 -0
  12. examples/sector_management/run.py +162 -0
  13. examples/sequential/run.py +209 -0
  14. examples/single_state/run.py +201 -0
  15. examples/states_lookup_table/run.py +137 -0
  16. examples/streamline_wakes/run.py +138 -0
  17. examples/tab_file/run.py +142 -0
  18. examples/timelines/run.py +267 -0
  19. examples/timeseries/run.py +183 -0
  20. examples/timeseries_slurm/run.py +185 -0
  21. examples/wind_rose/run.py +141 -0
  22. examples/windio/run.py +29 -0
  23. examples/yawed_wake/run.py +196 -0
  24. foxes/__init__.py +4 -8
  25. foxes/algorithms/__init__.py +1 -1
  26. foxes/algorithms/downwind/downwind.py +232 -101
  27. foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -6
  28. foxes/algorithms/downwind/models/init_farm_data.py +1 -1
  29. foxes/algorithms/downwind/models/point_wakes_calc.py +5 -6
  30. foxes/algorithms/downwind/models/reorder_farm_output.py +0 -1
  31. foxes/algorithms/downwind/models/set_amb_point_results.py +4 -2
  32. foxes/algorithms/iterative/iterative.py +73 -33
  33. foxes/algorithms/iterative/models/farm_wakes_calc.py +11 -6
  34. foxes/algorithms/sequential/models/plugin.py +1 -1
  35. foxes/algorithms/sequential/sequential.py +126 -255
  36. foxes/constants.py +17 -2
  37. foxes/core/__init__.py +1 -0
  38. foxes/core/algorithm.py +631 -146
  39. foxes/core/data.py +252 -20
  40. foxes/core/data_calc_model.py +13 -289
  41. foxes/core/engine.py +630 -0
  42. foxes/core/farm_controller.py +37 -9
  43. foxes/core/farm_data_model.py +15 -0
  44. foxes/core/model.py +133 -80
  45. foxes/core/point_data_model.py +15 -0
  46. foxes/core/rotor_model.py +27 -21
  47. foxes/core/states.py +16 -0
  48. foxes/core/turbine_type.py +28 -0
  49. foxes/core/wake_frame.py +22 -4
  50. foxes/core/wake_model.py +2 -3
  51. foxes/data/windio/windio_5turbines_timeseries.yaml +23 -1
  52. foxes/engines/__init__.py +16 -0
  53. foxes/engines/dask.py +975 -0
  54. foxes/engines/default.py +75 -0
  55. foxes/engines/futures.py +72 -0
  56. foxes/engines/mpi.py +38 -0
  57. foxes/engines/multiprocess.py +74 -0
  58. foxes/engines/numpy.py +185 -0
  59. foxes/engines/pool.py +263 -0
  60. foxes/engines/single.py +139 -0
  61. foxes/input/farm_layout/__init__.py +1 -0
  62. foxes/input/farm_layout/from_csv.py +4 -0
  63. foxes/input/farm_layout/from_json.py +1 -1
  64. foxes/input/farm_layout/grid.py +2 -2
  65. foxes/input/farm_layout/ring.py +65 -0
  66. foxes/input/farm_layout/row.py +2 -2
  67. foxes/input/states/__init__.py +6 -0
  68. foxes/input/states/create/random_abl_states.py +1 -1
  69. foxes/input/states/field_data_nc.py +157 -32
  70. foxes/input/states/multi_height.py +127 -13
  71. foxes/input/states/one_point_flow.py +577 -0
  72. foxes/input/states/scan_ws.py +73 -2
  73. foxes/input/states/states_table.py +204 -35
  74. foxes/input/windio/__init__.py +1 -1
  75. foxes/input/windio/get_states.py +44 -23
  76. foxes/input/windio/read_attributes.py +41 -16
  77. foxes/input/windio/read_farm.py +116 -102
  78. foxes/input/windio/read_fields.py +13 -6
  79. foxes/input/windio/read_outputs.py +63 -22
  80. foxes/input/windio/runner.py +31 -17
  81. foxes/input/windio/windio.py +36 -22
  82. foxes/models/ground_models/wake_mirror.py +8 -4
  83. foxes/models/model_book.py +29 -18
  84. foxes/models/partial_wakes/rotor_points.py +3 -3
  85. foxes/models/rotor_models/centre.py +4 -0
  86. foxes/models/rotor_models/grid.py +22 -23
  87. foxes/models/rotor_models/levels.py +4 -5
  88. foxes/models/turbine_models/calculator.py +0 -2
  89. foxes/models/turbine_models/lookup_table.py +27 -2
  90. foxes/models/turbine_models/rotor_centre_calc.py +4 -3
  91. foxes/models/turbine_models/set_farm_vars.py +103 -34
  92. foxes/models/turbine_types/PCt_file.py +24 -0
  93. foxes/models/turbine_types/PCt_from_two.py +24 -0
  94. foxes/models/turbine_types/__init__.py +1 -0
  95. foxes/models/turbine_types/lookup.py +316 -0
  96. foxes/models/turbine_types/null_type.py +50 -0
  97. foxes/models/turbine_types/wsrho2PCt_from_two.py +24 -0
  98. foxes/models/turbine_types/wsti2PCt_from_two.py +24 -0
  99. foxes/models/vertical_profiles/data_profile.py +1 -1
  100. foxes/models/wake_frames/__init__.py +1 -0
  101. foxes/models/wake_frames/dynamic_wakes.py +424 -0
  102. foxes/models/wake_frames/farm_order.py +23 -3
  103. foxes/models/wake_frames/rotor_wd.py +4 -2
  104. foxes/models/wake_frames/seq_dynamic_wakes.py +56 -63
  105. foxes/models/wake_frames/streamlines.py +19 -20
  106. foxes/models/wake_frames/timelines.py +328 -127
  107. foxes/models/wake_frames/yawed_wakes.py +4 -1
  108. foxes/models/wake_models/dist_sliced.py +1 -3
  109. foxes/models/wake_models/induction/rankine_half_body.py +4 -4
  110. foxes/models/wake_models/induction/rathmann.py +2 -2
  111. foxes/models/wake_models/induction/self_similar.py +2 -2
  112. foxes/models/wake_models/induction/vortex_sheet.py +2 -2
  113. foxes/models/wake_models/ti/iec_ti.py +34 -17
  114. foxes/models/wake_models/top_hat.py +1 -1
  115. foxes/models/wake_models/wind/bastankhah14.py +2 -2
  116. foxes/models/wake_models/wind/bastankhah16.py +8 -7
  117. foxes/models/wake_models/wind/jensen.py +1 -1
  118. foxes/models/wake_models/wind/turbopark.py +2 -2
  119. foxes/output/__init__.py +4 -1
  120. foxes/output/farm_layout.py +2 -2
  121. foxes/output/flow_plots_2d/__init__.py +0 -1
  122. foxes/output/flow_plots_2d/flow_plots.py +70 -30
  123. foxes/output/grids.py +91 -21
  124. foxes/output/seq_plugins/__init__.py +2 -0
  125. foxes/output/{flow_plots_2d → seq_plugins}/seq_flow_ani_plugin.py +62 -20
  126. foxes/output/seq_plugins/seq_wake_debug_plugin.py +145 -0
  127. foxes/output/slice_data.py +131 -111
  128. foxes/output/state_turbine_map.py +18 -13
  129. foxes/output/state_turbine_table.py +19 -19
  130. foxes/utils/__init__.py +1 -1
  131. foxes/utils/dev_utils.py +42 -0
  132. foxes/utils/dict.py +1 -1
  133. foxes/utils/factory.py +147 -52
  134. foxes/utils/pandas_helpers.py +4 -3
  135. foxes/utils/wind_dir.py +0 -2
  136. foxes/utils/xarray_utils.py +23 -13
  137. foxes/variables.py +37 -0
  138. {foxes-0.8.2.dist-info → foxes-1.0.dist-info}/METADATA +71 -33
  139. foxes-1.0.dist-info/RECORD +307 -0
  140. {foxes-0.8.2.dist-info → foxes-1.0.dist-info}/WHEEL +1 -1
  141. foxes-1.0.dist-info/top_level.txt +4 -0
  142. tests/0_consistency/iterative/test_iterative.py +92 -0
  143. tests/0_consistency/partial_wakes/test_partial_wakes.py +90 -0
  144. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +85 -0
  145. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +103 -0
  146. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +85 -0
  147. tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +87 -0
  148. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +82 -0
  149. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +82 -0
  150. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/flappy/run.py +92 -0
  151. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +93 -0
  152. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/flappy/run.py +92 -0
  153. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +96 -0
  154. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/flappy/run.py +94 -0
  155. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +122 -0
  156. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/flappy/run.py +94 -0
  157. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +122 -0
  158. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/flappy/run.py +92 -0
  159. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +93 -0
  160. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +85 -0
  161. tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +130 -0
  162. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/flappy/run.py +96 -0
  163. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +116 -0
  164. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +93 -0
  165. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +99 -0
  166. tests/3_examples/test_examples.py +34 -0
  167. foxes/VERSION +0 -1
  168. foxes/output/flow_plots_2d.py +0 -0
  169. foxes/utils/runners/__init__.py +0 -1
  170. foxes/utils/runners/runners.py +0 -280
  171. foxes-0.8.2.dist-info/RECORD +0 -247
  172. foxes-0.8.2.dist-info/top_level.txt +0 -1
  173. foxes-0.8.2.dist-info/zip-safe +0 -1
  174. {foxes-0.8.2.dist-info → foxes-1.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,75 @@
1
+ import numpy as np
2
+
3
+ from foxes.core import Engine
4
+ import foxes.constants as FC
5
+
6
+
7
+ class DefaultEngine(Engine):
8
+ """
9
+ The case size dependent default engine.
10
+
11
+ :group: engines
12
+
13
+ """
14
+
15
+ def run_calculation(
16
+ self,
17
+ algo,
18
+ model,
19
+ model_data=None,
20
+ farm_data=None,
21
+ point_data=None,
22
+ **kwargs,
23
+ ):
24
+ """
25
+ Runs the model calculation
26
+
27
+ Parameters
28
+ ----------
29
+ algo: foxes.core.Algorithm
30
+ The algorithm object
31
+ model: foxes.core.DataCalcModel
32
+ The model that whose calculate function
33
+ should be run
34
+ model_data: xarray.Dataset, optional
35
+ The initial model data
36
+ farm_data: xarray.Dataset, optional
37
+ The initial farm data
38
+ point_data: xarray.Dataset, optional
39
+ The initial point data
40
+
41
+ Returns
42
+ -------
43
+ results: xarray.Dataset
44
+ The model results
45
+
46
+ """
47
+ max_n = np.sqrt(self.n_procs) * (500 / algo.n_turbines) ** 1.5
48
+
49
+ if (algo.n_states >= max_n) or (
50
+ point_data is not None
51
+ and self.chunk_size_points is not None
52
+ and point_data.sizes[FC.TARGET] > self.chunk_size_points
53
+ ):
54
+ ename = "process"
55
+ else:
56
+ ename = "single"
57
+
58
+ self.print(f"{type(self).__name__}: Selecting engine '{ename}'", level=1)
59
+
60
+ self.finalize()
61
+
62
+ with Engine.new(
63
+ ename,
64
+ n_procs=self.n_procs,
65
+ chunk_size_states=self.chunk_size_states,
66
+ chunk_size_points=self.chunk_size_points,
67
+ verbosity=self.verbosity,
68
+ ) as e:
69
+ results = e.run_calculation(
70
+ algo, model, model_data, farm_data, point_data=point_data, **kwargs
71
+ )
72
+
73
+ self.initialize()
74
+
75
+ return results
@@ -0,0 +1,72 @@
1
+ from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
2
+
3
+ from .pool import PoolEngine
4
+
5
+
6
+ class ThreadsEngine(PoolEngine):
7
+ """
8
+ The threads engine for foxes calculations.
9
+
10
+ :group: engines
11
+
12
+ """
13
+
14
+ def _create_pool(self):
15
+ """Creates the pool"""
16
+ self._pool = ThreadPoolExecutor(max_workers=self.n_procs)
17
+
18
+ def _submit(self, f, *args, **kwargs):
19
+ """
20
+ Submits to the pool
21
+
22
+ Parameters
23
+ ----------
24
+ f: Callable
25
+ The function f(*args, **kwargs) to be
26
+ submitted
27
+ args: tuple, optional
28
+ Arguments for the function
29
+ kwargs: dict, optional
30
+ Arguments for the function
31
+
32
+ Returns
33
+ -------
34
+ future: object
35
+ The future object
36
+
37
+ """
38
+ return self._pool.submit(f, *args, **kwargs)
39
+
40
+ def _result(self, future):
41
+ """
42
+ Waits for result from a future
43
+
44
+ Parameters
45
+ ----------
46
+ future: object
47
+ The future
48
+
49
+ Returns
50
+ -------
51
+ result: object
52
+ The calculation result
53
+
54
+ """
55
+ return future.result()
56
+
57
+ def _shutdown_pool(self):
58
+ """Shuts down the pool"""
59
+ self._pool.shutdown()
60
+
61
+
62
+ class ProcessEngine(ThreadsEngine):
63
+ """
64
+ The processes engine for foxes calculations.
65
+
66
+ :group: engines
67
+
68
+ """
69
+
70
+ def _create_pool(self):
71
+ """Creates the pool"""
72
+ self._pool = ProcessPoolExecutor(max_workers=self.n_procs)
foxes/engines/mpi.py ADDED
@@ -0,0 +1,38 @@
1
+ from foxes.utils import import_module
2
+
3
+ from .pool import PoolEngine
4
+ from .futures import ProcessEngine
5
+
6
+
7
+ def load_mpi():
8
+ """On-demand loading of the mpi4py package"""
9
+ global MPIPoolExecutor
10
+ MPIPoolExecutor = import_module(
11
+ "mpi4py.futures", hint="pip install mpi4py"
12
+ ).MPIPoolExecutor
13
+
14
+
15
+ class MPIEngine(ProcessEngine):
16
+ """
17
+ The MPI engine for foxes calculations.
18
+
19
+ Examples
20
+ --------
21
+ Run command, e.g. for 12 processors and a script run.py:
22
+
23
+ >>> mpiexec -n 12 -m mpi4py.futures run.py
24
+
25
+ :group: engines
26
+
27
+ """
28
+
29
+ def initialize(self):
30
+ """
31
+ Initializes the engine.
32
+ """
33
+ load_mpi()
34
+ PoolEngine.initialize(self)
35
+
36
+ def _create_pool(self):
37
+ """Creates the pool"""
38
+ self._pool = MPIPoolExecutor(max_workers=self.n_procs)
@@ -0,0 +1,74 @@
1
+ from foxes.utils import import_module
2
+
3
+ from .pool import PoolEngine
4
+
5
+
6
+ def load_multiprocess():
7
+ """On-demand loading of the multiprocess package"""
8
+ global Pool
9
+ Pool = import_module("multiprocess", hint="pip install multiprocess").Pool
10
+
11
+
12
+ class MultiprocessEngine(PoolEngine):
13
+ """
14
+ The multiprocessing engine for foxes calculations.
15
+
16
+ :group: engines
17
+
18
+ """
19
+
20
+ def initialize(self):
21
+ """
22
+ Initializes the engine.
23
+ """
24
+ load_multiprocess()
25
+ super().initialize()
26
+
27
+ def _create_pool(self):
28
+ """Creates the pool"""
29
+ self._pool = Pool(processes=self.n_procs)
30
+
31
+ def _submit(self, f, *args, **kwargs):
32
+ """
33
+ Submits to the pool
34
+
35
+ Parameters
36
+ ----------
37
+ f: Callable
38
+ The function f(*args, **kwargs) to be
39
+ submitted
40
+ args: tuple, optional
41
+ Arguments for the function
42
+ kwargs: dict, optional
43
+ Arguments for the function
44
+
45
+ Returns
46
+ -------
47
+ future: object
48
+ The future object
49
+
50
+ """
51
+ return self._pool.apply_async(f, args=args, kwds=kwargs)
52
+
53
+ def _result(self, future):
54
+ """
55
+ Waits for result from a future
56
+
57
+ Parameters
58
+ ----------
59
+ future: object
60
+ The future
61
+
62
+ Returns
63
+ -------
64
+ result: object
65
+ The calculation result
66
+
67
+ """
68
+ return future.get()
69
+
70
+ def _shutdown_pool(self):
71
+ """Shuts down the pool"""
72
+ self._pool.close()
73
+ self._pool.terminate()
74
+ self._pool.join()
foxes/engines/numpy.py ADDED
@@ -0,0 +1,185 @@
1
+ from tqdm import tqdm
2
+ from xarray import Dataset
3
+
4
+ from foxes.core import Engine
5
+ import foxes.constants as FC
6
+
7
+ from .pool import _run
8
+
9
+
10
+ class NumpyEngine(Engine):
11
+ """
12
+ The numpy engine for foxes calculations.
13
+
14
+ :group: engines
15
+
16
+ """
17
+
18
+ def __init__(self, *args, **kwargs):
19
+ """
20
+ Constructor.
21
+
22
+ Parameters
23
+ ----------
24
+ args: tuple, optional
25
+ Additional parameters for the base class
26
+ kwargs: dict, optional
27
+ Additional parameters for the base class
28
+
29
+ """
30
+ ignr = ["n_procs"]
31
+ for k in ignr:
32
+ if kwargs.pop(k, None) is not None:
33
+ print(f"{type(self).__name__}: Ignoring {k}")
34
+ super().__init__(
35
+ *args,
36
+ n_procs=1,
37
+ **kwargs,
38
+ )
39
+
40
+ def run_calculation(
41
+ self,
42
+ algo,
43
+ model,
44
+ model_data=None,
45
+ farm_data=None,
46
+ point_data=None,
47
+ out_vars=[],
48
+ chunk_store={},
49
+ sel=None,
50
+ isel=None,
51
+ iterative=False,
52
+ **calc_pars,
53
+ ):
54
+ """
55
+ Runs the model calculation
56
+
57
+ Parameters
58
+ ----------
59
+ algo: foxes.core.Algorithm
60
+ The algorithm object
61
+ model: foxes.core.DataCalcModel
62
+ The model that whose calculate function
63
+ should be run
64
+ model_data: xarray.Dataset
65
+ The initial model data
66
+ farm_data: xarray.Dataset
67
+ The initial farm data
68
+ point_data: xarray.Dataset
69
+ The initial point data
70
+ out_vars: list of str, optional
71
+ Names of the output variables
72
+ chunk_store: foxes.utils.Dict
73
+ The chunk store
74
+ sel: dict, optional
75
+ Selection of coordinate subsets
76
+ isel: dict, optional
77
+ Selection of coordinate subsets index values
78
+ iterative: bool
79
+ Flag for use within the iterative algorithm
80
+ calc_pars: dict, optional
81
+ Additional parameters for the model.calculate()
82
+
83
+ Returns
84
+ -------
85
+ results: xarray.Dataset
86
+ The model results
87
+
88
+ """
89
+ # subset selection:
90
+ model_data, farm_data, point_data = self.select_subsets(
91
+ model_data, farm_data, point_data, sel=sel, isel=isel
92
+ )
93
+
94
+ # basic checks:
95
+ super().run_calculation(algo, model, model_data, farm_data, point_data)
96
+
97
+ # prepare:
98
+ n_states = model_data.sizes[FC.STATE]
99
+ out_coords = model.output_coords()
100
+ coords = {}
101
+ if FC.STATE in out_coords and FC.STATE in model_data.coords:
102
+ coords[FC.STATE] = model_data[FC.STATE].to_numpy()
103
+ if farm_data is None:
104
+ farm_data = Dataset()
105
+ goal_data = farm_data if point_data is None else point_data
106
+
107
+ # DEBUG objec mem sizes:
108
+ # from foxes.utils import print_mem
109
+ # for m in [algo] + model.models:
110
+ # print_mem(m, pre_str="MULTIP CHECKING LARGE DATA", min_csize=9999)
111
+
112
+ # calculate chunk sizes:
113
+ n_targets = point_data.sizes[FC.TARGET] if point_data is not None else 0
114
+ chunk_sizes_states, chunk_sizes_targets = self.calc_chunk_sizes(
115
+ n_states, n_targets
116
+ )
117
+ n_chunks_states = len(chunk_sizes_states)
118
+ n_chunks_targets = len(chunk_sizes_targets)
119
+ self.print(
120
+ f"Selecting n_chunks_states = {n_chunks_states}, n_chunks_targets = {n_chunks_targets}",
121
+ level=2,
122
+ )
123
+
124
+ # prepare and submit chunks:
125
+ n_chunks_all = n_chunks_states * n_chunks_targets
126
+ self.print(f"Looping over {n_chunks_all} chunks")
127
+ pbar = tqdm(total=n_chunks_all) if self.verbosity > 1 else None
128
+ results = {}
129
+ i0_states = 0
130
+ for chunki_states in range(n_chunks_states):
131
+ i1_states = i0_states + chunk_sizes_states[chunki_states]
132
+ i0_targets = 0
133
+ for chunki_points in range(n_chunks_targets):
134
+ i1_targets = i0_targets + chunk_sizes_targets[chunki_points]
135
+
136
+ i = chunki_states * n_chunks_targets + chunki_points
137
+ self.print(f"Computing chunk {i}/{n_chunks_all}")
138
+
139
+ # get this chunk's data:
140
+ data = self.get_chunk_input_data(
141
+ algo=algo,
142
+ model_data=model_data,
143
+ farm_data=farm_data,
144
+ point_data=point_data,
145
+ states_i0_i1=(i0_states, i1_states),
146
+ targets_i0_i1=(i0_targets, i1_targets),
147
+ out_vars=out_vars,
148
+ )
149
+
150
+ # submit model calculation:
151
+ key = (chunki_states, chunki_points)
152
+ results[key] = _run(
153
+ algo,
154
+ model,
155
+ data,
156
+ iterative,
157
+ chunk_store,
158
+ (i0_states, i0_targets),
159
+ **calc_pars,
160
+ )
161
+ chunk_store.update(results[key][1])
162
+ del data
163
+
164
+ i0_targets = i1_targets
165
+
166
+ if pbar is not None:
167
+ pbar.update()
168
+
169
+ i0_states = i1_states
170
+
171
+ del calc_pars, farm_data, point_data
172
+ if pbar is not None:
173
+ pbar.close()
174
+
175
+ return self.combine_results(
176
+ algo=algo,
177
+ results=results,
178
+ model_data=model_data,
179
+ out_vars=out_vars,
180
+ out_coords=out_coords,
181
+ n_chunks_states=n_chunks_states,
182
+ n_chunks_targets=n_chunks_targets,
183
+ goal_data=goal_data,
184
+ iterative=iterative,
185
+ )
foxes/engines/pool.py ADDED
@@ -0,0 +1,263 @@
1
+ import xarray as xr
2
+ from abc import abstractmethod
3
+ from tqdm import tqdm
4
+
5
+ from foxes.core import Engine
6
+ import foxes.constants as FC
7
+
8
+
9
+ def _run(algo, model, data, iterative, chunk_store, i0_t0, **cpars):
10
+ """Helper function for running in a single process"""
11
+ algo.reset_chunk_store(chunk_store)
12
+ results = model.calculate(algo, *data, **cpars)
13
+ chunk_store = algo.reset_chunk_store() if iterative else {}
14
+ cstore = {i0_t0: chunk_store[i0_t0]} if i0_t0 in chunk_store else {}
15
+ return results, cstore
16
+
17
+
18
+ class PoolEngine(Engine):
19
+ """
20
+ Abstract engine for pool type parallelizations.
21
+
22
+ :group: engines
23
+
24
+ """
25
+
26
+ @abstractmethod
27
+ def _create_pool(self):
28
+ """Creates the pool"""
29
+ pass
30
+
31
+ @abstractmethod
32
+ def _submit(self, f, *args, **kwargs):
33
+ """
34
+ Submits to the pool
35
+
36
+ Parameters
37
+ ----------
38
+ f: Callable
39
+ The function f(*args, **kwargs) to be
40
+ submitted
41
+ args: tuple, optional
42
+ Arguments for the function
43
+ kwargs: dict, optional
44
+ Arguments for the function
45
+
46
+ Returns
47
+ -------
48
+ future: object
49
+ The future object
50
+
51
+ """
52
+ pass
53
+
54
+ @abstractmethod
55
+ def _result(self, future):
56
+ """
57
+ Waits for result from a future
58
+
59
+ Parameters
60
+ ----------
61
+ future: object
62
+ The future
63
+
64
+ Returns
65
+ -------
66
+ result: object
67
+ The calculation result
68
+
69
+ """
70
+ pass
71
+
72
+ @abstractmethod
73
+ def _shutdown_pool(self):
74
+ """Shuts down the pool"""
75
+ pass
76
+
77
+ def initialize(self):
78
+ """
79
+ Initializes the engine.
80
+ """
81
+ super().initialize()
82
+ self._create_pool()
83
+
84
+ def run_calculation(
85
+ self,
86
+ algo,
87
+ model,
88
+ model_data=None,
89
+ farm_data=None,
90
+ point_data=None,
91
+ out_vars=[],
92
+ chunk_store={},
93
+ sel=None,
94
+ isel=None,
95
+ iterative=False,
96
+ **calc_pars,
97
+ ):
98
+ """
99
+ Runs the model calculation
100
+
101
+ Parameters
102
+ ----------
103
+ algo: foxes.core.Algorithm
104
+ The algorithm object
105
+ model: foxes.core.DataCalcModel
106
+ The model that whose calculate function
107
+ should be run
108
+ model_data: xarray.Dataset
109
+ The initial model data
110
+ farm_data: xarray.Dataset
111
+ The initial farm data
112
+ point_data: xarray.Dataset
113
+ The initial point data
114
+ out_vars: list of str, optional
115
+ Names of the output variables
116
+ chunk_store: foxes.utils.Dict
117
+ The chunk store
118
+ sel: dict, optional
119
+ Selection of coordinate subsets
120
+ isel: dict, optional
121
+ Selection of coordinate subsets index values
122
+ iterative: bool
123
+ Flag for use within the iterative algorithm
124
+ calc_pars: dict, optional
125
+ Additional parameters for the model.calculate()
126
+
127
+ Returns
128
+ -------
129
+ results: xarray.Dataset
130
+ The model results
131
+
132
+ """
133
+ # subset selection:
134
+ model_data, farm_data, point_data = self.select_subsets(
135
+ model_data, farm_data, point_data, sel=sel, isel=isel
136
+ )
137
+
138
+ # basic checks:
139
+ super().run_calculation(algo, model, model_data, farm_data, point_data)
140
+
141
+ # prepare:
142
+ n_states = model_data.sizes[FC.STATE]
143
+ out_coords = model.output_coords()
144
+ coords = {}
145
+ if FC.STATE in out_coords and FC.STATE in model_data.coords:
146
+ coords[FC.STATE] = model_data[FC.STATE].to_numpy()
147
+ if farm_data is None:
148
+ farm_data = xr.Dataset()
149
+ goal_data = farm_data if point_data is None else point_data
150
+
151
+ # DEBUG objec mem sizes:
152
+ # from foxes.utils import print_mem
153
+ # for m in [algo] + model.models:
154
+ # print_mem(m, pre_str="MULTIP CHECKING LARGE DATA", min_csize=9999)
155
+
156
+ # calculate chunk sizes:
157
+ n_targets = point_data.sizes[FC.TARGET] if point_data is not None else 0
158
+ chunk_sizes_states, chunk_sizes_targets = self.calc_chunk_sizes(
159
+ n_states, n_targets
160
+ )
161
+ n_chunks_states = len(chunk_sizes_states)
162
+ n_chunks_targets = len(chunk_sizes_targets)
163
+ self.print(
164
+ f"Selecting n_chunks_states = {n_chunks_states}, n_chunks_targets = {n_chunks_targets}",
165
+ level=2,
166
+ )
167
+
168
+ # prepare and submit chunks:
169
+ n_chunks_all = n_chunks_states * n_chunks_targets
170
+ self.print(
171
+ f"Submitting {n_chunks_all} chunks to {self.n_procs} processes", level=2
172
+ )
173
+ pbar = tqdm(total=n_chunks_all) if self.verbosity > 1 else None
174
+ jobs = {}
175
+ i0_states = 0
176
+ for chunki_states in range(n_chunks_states):
177
+ i1_states = i0_states + chunk_sizes_states[chunki_states]
178
+ i0_targets = 0
179
+ for chunki_points in range(n_chunks_targets):
180
+ i1_targets = i0_targets + chunk_sizes_targets[chunki_points]
181
+
182
+ # get this chunk's data:
183
+ data = self.get_chunk_input_data(
184
+ algo=algo,
185
+ model_data=model_data,
186
+ farm_data=farm_data,
187
+ point_data=point_data,
188
+ states_i0_i1=(i0_states, i1_states),
189
+ targets_i0_i1=(i0_targets, i1_targets),
190
+ out_vars=out_vars,
191
+ )
192
+
193
+ # submit model calculation:
194
+ jobs[(chunki_states, chunki_points)] = self._submit(
195
+ _run,
196
+ algo,
197
+ model,
198
+ data,
199
+ iterative,
200
+ chunk_store,
201
+ (i0_states, i0_targets),
202
+ **calc_pars,
203
+ )
204
+ del data
205
+
206
+ i0_targets = i1_targets
207
+
208
+ if pbar is not None:
209
+ pbar.update()
210
+
211
+ i0_states = i1_states
212
+
213
+ del calc_pars, farm_data, point_data
214
+ if pbar is not None:
215
+ pbar.close()
216
+
217
+ # wait for results:
218
+ if n_chunks_all > 1 or self.verbosity > 1:
219
+ self.print(
220
+ f"Computing {n_chunks_all} chunks using {self.n_procs} processes"
221
+ )
222
+ pbar = (
223
+ tqdm(total=n_chunks_all)
224
+ if n_chunks_all > 1 and self.verbosity > 0
225
+ else None
226
+ )
227
+ results = {}
228
+ for chunki_states in range(n_chunks_states):
229
+ for chunki_points in range(n_chunks_targets):
230
+ key = (chunki_states, chunki_points)
231
+ results[key] = self._result(jobs.pop((chunki_states, chunki_points)))
232
+ if pbar is not None:
233
+ pbar.update()
234
+ if pbar is not None:
235
+ pbar.close()
236
+
237
+ return self.combine_results(
238
+ algo=algo,
239
+ results=results,
240
+ model_data=model_data,
241
+ out_vars=out_vars,
242
+ out_coords=out_coords,
243
+ n_chunks_states=n_chunks_states,
244
+ n_chunks_targets=n_chunks_targets,
245
+ goal_data=goal_data,
246
+ iterative=iterative,
247
+ )
248
+
249
+ def finalize(self, *exit_args, **exit_kwargs):
250
+ """
251
+ Finalizes the engine.
252
+
253
+ Parameters
254
+ ----------
255
+ exit_args: tuple, optional
256
+ Arguments from the exit function
257
+ exit_kwargs: dict, optional
258
+ Arguments from the exit function
259
+
260
+ """
261
+ if self.initialized:
262
+ self._shutdown_pool()
263
+ super().finalize(*exit_args, **exit_kwargs)