cars 1.0.0rc1__cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.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 cars might be problematic. Click here for more details.

Files changed (200) hide show
  1. cars/__init__.py +74 -0
  2. cars/applications/__init__.py +37 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +104 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +630 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +655 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1460 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-312-i386-linux-gnu.so +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  31. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  32. cars/applications/dense_matching/cpp/meson.build +9 -0
  33. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  34. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  35. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  36. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  37. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  38. cars/applications/dense_matching/disparity_grid_algo.py +588 -0
  39. cars/applications/dense_matching/loaders/__init__.py +23 -0
  40. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  46. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  47. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  48. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  49. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  50. cars/applications/dsm_filling/__init__.py +32 -0
  51. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  52. cars/applications/dsm_filling/border_interpolation_app.py +270 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +279 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +333 -0
  56. cars/applications/grid_generation/__init__.py +30 -0
  57. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  58. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  59. cars/applications/grid_generation/grid_correction_app.py +496 -0
  60. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  61. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  62. cars/applications/grid_generation/transform_grid.py +88 -0
  63. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  64. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  65. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  66. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  67. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  68. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  69. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  70. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  71. cars/applications/point_cloud_outlier_removal/small_components_app.py +527 -0
  72. cars/applications/point_cloud_outlier_removal/statistical_app.py +531 -0
  73. cars/applications/rasterization/__init__.py +30 -0
  74. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  75. cars/applications/rasterization/rasterization_algo.py +534 -0
  76. cars/applications/rasterization/rasterization_constants.py +38 -0
  77. cars/applications/rasterization/rasterization_wrappers.py +634 -0
  78. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  79. cars/applications/resampling/__init__.py +28 -0
  80. cars/applications/resampling/abstract_resampling_app.py +187 -0
  81. cars/applications/resampling/bicubic_resampling_app.py +762 -0
  82. cars/applications/resampling/resampling_algo.py +614 -0
  83. cars/applications/resampling/resampling_constants.py +36 -0
  84. cars/applications/resampling/resampling_wrappers.py +309 -0
  85. cars/applications/sparse_matching/__init__.py +30 -0
  86. cars/applications/sparse_matching/abstract_sparse_matching_app.py +498 -0
  87. cars/applications/sparse_matching/sift_app.py +735 -0
  88. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  89. cars/applications/sparse_matching/sparse_matching_constants.py +68 -0
  90. cars/applications/sparse_matching/sparse_matching_wrappers.py +238 -0
  91. cars/applications/triangulation/__init__.py +32 -0
  92. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  93. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  94. cars/applications/triangulation/pc_transform.py +552 -0
  95. cars/applications/triangulation/triangulation_algo.py +371 -0
  96. cars/applications/triangulation/triangulation_constants.py +38 -0
  97. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  98. cars/bundleadjustment.py +757 -0
  99. cars/cars.py +177 -0
  100. cars/conf/__init__.py +23 -0
  101. cars/conf/geoid/egm96.grd +0 -0
  102. cars/conf/geoid/egm96.grd.hdr +15 -0
  103. cars/conf/input_parameters.py +156 -0
  104. cars/conf/mask_cst.py +35 -0
  105. cars/core/__init__.py +23 -0
  106. cars/core/cars_logging.py +402 -0
  107. cars/core/constants.py +191 -0
  108. cars/core/constants_disparity.py +50 -0
  109. cars/core/datasets.py +140 -0
  110. cars/core/geometry/__init__.py +27 -0
  111. cars/core/geometry/abstract_geometry.py +1119 -0
  112. cars/core/geometry/shareloc_geometry.py +598 -0
  113. cars/core/inputs.py +568 -0
  114. cars/core/outputs.py +176 -0
  115. cars/core/preprocessing.py +722 -0
  116. cars/core/projection.py +843 -0
  117. cars/core/roi_tools.py +215 -0
  118. cars/core/tiling.py +774 -0
  119. cars/core/utils.py +164 -0
  120. cars/data_structures/__init__.py +23 -0
  121. cars/data_structures/cars_dataset.py +1541 -0
  122. cars/data_structures/cars_dict.py +74 -0
  123. cars/data_structures/corresponding_tiles_tools.py +186 -0
  124. cars/data_structures/dataframe_converter.py +185 -0
  125. cars/data_structures/format_transformation.py +297 -0
  126. cars/devibrate.py +689 -0
  127. cars/extractroi.py +264 -0
  128. cars/orchestrator/__init__.py +23 -0
  129. cars/orchestrator/achievement_tracker.py +125 -0
  130. cars/orchestrator/cluster/__init__.py +37 -0
  131. cars/orchestrator/cluster/abstract_cluster.py +244 -0
  132. cars/orchestrator/cluster/abstract_dask_cluster.py +375 -0
  133. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  134. cars/orchestrator/cluster/dask_config/README.md +94 -0
  135. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  136. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  137. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  138. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  139. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  140. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  141. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  142. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  143. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  144. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  145. cars/orchestrator/cluster/log_wrapper.py +1075 -0
  146. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  147. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  148. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  149. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  150. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  151. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +873 -0
  152. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  153. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  154. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  155. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  156. cars/orchestrator/orchestrator.py +905 -0
  157. cars/orchestrator/orchestrator_constants.py +29 -0
  158. cars/orchestrator/registry/__init__.py +23 -0
  159. cars/orchestrator/registry/abstract_registry.py +143 -0
  160. cars/orchestrator/registry/compute_registry.py +106 -0
  161. cars/orchestrator/registry/id_generator.py +116 -0
  162. cars/orchestrator/registry/replacer_registry.py +213 -0
  163. cars/orchestrator/registry/saver_registry.py +363 -0
  164. cars/orchestrator/registry/unseen_registry.py +118 -0
  165. cars/orchestrator/tiles_profiler.py +279 -0
  166. cars/pipelines/__init__.py +26 -0
  167. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  168. cars/pipelines/conf_resolution/conf_first_resolution.yaml +2 -0
  169. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  170. cars/pipelines/default/__init__.py +26 -0
  171. cars/pipelines/default/default_pipeline.py +786 -0
  172. cars/pipelines/parameters/__init__.py +0 -0
  173. cars/pipelines/parameters/advanced_parameters.py +417 -0
  174. cars/pipelines/parameters/advanced_parameters_constants.py +69 -0
  175. cars/pipelines/parameters/application_parameters.py +71 -0
  176. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  177. cars/pipelines/parameters/dsm_inputs.py +918 -0
  178. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  179. cars/pipelines/parameters/output_constants.py +52 -0
  180. cars/pipelines/parameters/output_parameters.py +454 -0
  181. cars/pipelines/parameters/sensor_inputs.py +842 -0
  182. cars/pipelines/parameters/sensor_inputs_constants.py +49 -0
  183. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  184. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  185. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  186. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  187. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  188. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  189. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  190. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  191. cars/pipelines/pipeline.py +119 -0
  192. cars/pipelines/pipeline_constants.py +31 -0
  193. cars/pipelines/pipeline_template.py +139 -0
  194. cars/pipelines/unit/__init__.py +26 -0
  195. cars/pipelines/unit/unit_pipeline.py +2850 -0
  196. cars/starter.py +167 -0
  197. cars-1.0.0rc1.dist-info/METADATA +292 -0
  198. cars-1.0.0rc1.dist-info/RECORD +200 -0
  199. cars-1.0.0rc1.dist-info/WHEEL +6 -0
  200. cars-1.0.0rc1.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,905 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf8
3
+ #
4
+ # Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
5
+ #
6
+ # This file is part of CARS
7
+ # (see https://github.com/CNES/cars).
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ """
22
+ this module contains the orchestrator class
23
+ """
24
+
25
+ # pylint: disable=C0302
26
+
27
+ import collections
28
+ import logging
29
+
30
+ # Standard imports
31
+ import multiprocessing
32
+ import os
33
+ import platform
34
+ import re
35
+ import shutil
36
+ import subprocess
37
+ import sys
38
+
39
+ # Third party imports
40
+ import tempfile
41
+ import threading
42
+ import time
43
+ import traceback
44
+
45
+ import pandas
46
+ import psutil
47
+ import xarray
48
+ from tqdm import tqdm
49
+
50
+ from cars.core import constants as cst
51
+
52
+ # CARS imports
53
+ from cars.core.cars_logging import add_progress_message
54
+ from cars.core.utils import safe_makedirs
55
+ from cars.data_structures import cars_dataset
56
+ from cars.orchestrator import achievement_tracker
57
+ from cars.orchestrator.cluster.abstract_cluster import AbstractCluster
58
+ from cars.orchestrator.cluster.log_wrapper import cars_profile
59
+ from cars.orchestrator.orchestrator_constants import (
60
+ CARS_DATASET_KEY,
61
+ CARS_DS_COL,
62
+ CARS_DS_ROW,
63
+ )
64
+ from cars.orchestrator.registry import compute_registry
65
+ from cars.orchestrator.registry import id_generator as id_gen
66
+ from cars.orchestrator.registry import replacer_registry, saver_registry
67
+ from cars.orchestrator.tiles_profiler import TileProfiler
68
+
69
+ SYS_PLATFORM = platform.system().lower()
70
+ IS_WIN = "windows" == SYS_PLATFORM
71
+ RAM_THRESHOLD_MB = 500
72
+ RAM_CHECK_SLEEP_TIME = 5
73
+
74
+
75
+ class Orchestrator:
76
+ """
77
+ Orchestrator
78
+ """
79
+
80
+ # pylint: disable=too-many-instance-attributes
81
+
82
+ # flake8: noqa: C901
83
+ def __init__( # pylint: disable=too-many-positional-arguments
84
+ self,
85
+ orchestrator_conf=None,
86
+ out_dir=None,
87
+ log_dir=None,
88
+ launch_worker=True,
89
+ out_yaml_path=None,
90
+ ):
91
+ """
92
+ Init function of Orchestrator.
93
+ Creates Cluster and Registry for CarsDatasets
94
+
95
+ :param orchestrator_conf: configuration of distribution
96
+
97
+ """
98
+ # init list of path to clean at the end
99
+ self.tmp_dir_list = []
100
+
101
+ # out_dir
102
+ if out_dir is not None:
103
+ self.out_dir = out_dir
104
+ else:
105
+ self.out_dir = tempfile.mkdtemp()
106
+ self.add_to_clean(self.out_dir)
107
+ logging.debug("No out_dir defined")
108
+
109
+ if log_dir is not None:
110
+ self.log_dir = log_dir
111
+ else:
112
+ self.log_dir = os.path.join(self.out_dir, "logs")
113
+
114
+ self.launch_worker = launch_worker
115
+
116
+ # overload orchestrator_conf
117
+ if orchestrator_conf is None or (
118
+ "mode" in orchestrator_conf and orchestrator_conf["mode"] == "auto"
119
+ ):
120
+ if orchestrator_conf is None:
121
+ logging.info(
122
+ "No orchestrator configuration given: auto mode is used"
123
+ )
124
+ logging.info(
125
+ "Auto mode is used for orchestrator: "
126
+ "number of workers and memory allocated per worker "
127
+ "will be set automatically"
128
+ )
129
+ if orchestrator_conf is not None and len(orchestrator_conf) > 1:
130
+ logging.warning(
131
+ "Auto mode is used for orchestator: "
132
+ "parameters set by user are ignored"
133
+ )
134
+ # Compute parameters for auto mode
135
+ nb_workers, max_ram_per_worker = compute_conf_auto_mode(IS_WIN)
136
+ orchestrator_conf = {
137
+ "mode": "multiprocessing",
138
+ "nb_workers": nb_workers,
139
+ "max_ram_per_worker": max_ram_per_worker,
140
+ }
141
+
142
+ self.orchestrator_conf = orchestrator_conf
143
+
144
+ # init cluster
145
+ self.cluster = AbstractCluster( # pylint: disable=E0110
146
+ orchestrator_conf,
147
+ self.out_dir,
148
+ self.log_dir,
149
+ launch_worker=self.launch_worker,
150
+ )
151
+ self.conf = self.cluster.get_conf()
152
+
153
+ self.task_timeout = self.conf.get("task_timeout", 600)
154
+
155
+ # Init IdGenerator
156
+ self.id_generator = id_gen.IdGenerator()
157
+ # init CarsDataset savers registry
158
+ self.cars_ds_savers_registry = saver_registry.CarsDatasetsRegistrySaver(
159
+ self.id_generator
160
+ )
161
+ # init CarsDataset replacement registry
162
+ self.cars_ds_replacer_registry = (
163
+ replacer_registry.CarsDatasetRegistryReplacer(self.id_generator)
164
+ )
165
+ # init CarsDataset compute registry
166
+ self.cars_ds_compute_registry = (
167
+ compute_registry.CarsDatasetRegistryCompute(self.id_generator)
168
+ )
169
+
170
+ # Achievement tracker
171
+ self.achievement_tracker = achievement_tracker.AchievementTracker()
172
+
173
+ # init tile profiler
174
+ self.dir_tile_profiling = os.path.join(
175
+ self.out_dir, "dump_dir", "tile_processing"
176
+ )
177
+ if not os.path.exists(self.dir_tile_profiling):
178
+ os.makedirs(self.dir_tile_profiling)
179
+ self.tile_profiler = TileProfiler(
180
+ self.dir_tile_profiling,
181
+ self.cars_ds_savers_registry,
182
+ self.cars_ds_replacer_registry,
183
+ )
184
+
185
+ # init cars_ds_names_info for pbar printing
186
+ self.cars_ds_names_info = []
187
+
188
+ # outjson
189
+ self.out_yaml_path = out_yaml_path
190
+ if self.out_yaml_path is None:
191
+ os.path.join(self.out_dir, "metadata.yaml")
192
+ self.out_yaml = {}
193
+
194
+ # product index file
195
+ self.product_index = {}
196
+
197
+ # Start tread used in ram check
198
+ ram_check_thread = threading.Thread(target=check_ram_usage)
199
+ ram_check_thread.daemon = True
200
+ ram_check_thread.start()
201
+
202
+ def add_to_clean(self, tmp_dir):
203
+ self.tmp_dir_list.append(tmp_dir)
204
+
205
+ def get_conf(self):
206
+ """
207
+ Get orchestrator conf
208
+
209
+ :return: orchestrator conf
210
+ """
211
+
212
+ return self.conf
213
+
214
+ @cars_profile(name="Add to save lists", interval=0.5)
215
+ def add_to_save_lists( # pylint: disable=too-many-positional-arguments
216
+ self,
217
+ file_name,
218
+ tag,
219
+ cars_ds,
220
+ dtype="float32",
221
+ nodata=0,
222
+ cars_ds_name=None,
223
+ optional_data=False,
224
+ save_by_pair=False,
225
+ ):
226
+ """
227
+ Save file to list in order to be saved later
228
+
229
+ :param file_name: file name
230
+ :param tag: tag
231
+ :param cars_ds: cars dataset to register
232
+ :param cars_ds_name: name corresponding to CarsDataset,
233
+ for information during logging
234
+ :param optional_data: True if the data is optionnal
235
+ :type optional_data: bool
236
+ :param save_by_pair: True if data by pair
237
+ :type save_by_pair: bool
238
+ """
239
+
240
+ self.cars_ds_savers_registry.add_file_to_save(
241
+ file_name,
242
+ cars_ds,
243
+ tag=tag,
244
+ dtype=dtype,
245
+ nodata=nodata,
246
+ optional_data=optional_data,
247
+ save_by_pair=save_by_pair,
248
+ )
249
+
250
+ # add name if exists
251
+ if cars_ds_name is not None:
252
+ self.cars_ds_names_info.append(cars_ds_name)
253
+
254
+ # add to tracking
255
+ self.achievement_tracker.track(
256
+ cars_ds, self.get_saving_infos([cars_ds])[0][CARS_DATASET_KEY]
257
+ )
258
+
259
+ def add_to_replace_lists(self, cars_ds, cars_ds_name=None):
260
+ """
261
+ Add CarsDataset to replacing Registry
262
+
263
+ :param cars_ds: CarsDataset to replace
264
+ :type cars_ds: CarsDataset
265
+ :param cars_ds_name: name corresponding to CarsDataset,
266
+ for information during logging
267
+ """
268
+
269
+ self.cars_ds_replacer_registry.add_cars_ds_to_replace(cars_ds)
270
+
271
+ # add name if exists
272
+ if cars_ds_name is not None:
273
+ self.cars_ds_names_info.append(cars_ds_name)
274
+
275
+ # add to tracking
276
+ self.achievement_tracker.track(
277
+ cars_ds, self.get_saving_infos([cars_ds])[0][CARS_DATASET_KEY]
278
+ )
279
+
280
+ def add_to_compute_lists(self, cars_ds, cars_ds_name=None):
281
+ """
282
+ Add CarsDataset to compute Registry: computed, but not used
283
+ in main process
284
+
285
+ :param cars_ds: CarsDataset to comput
286
+ :type cars_ds: CarsDataset
287
+ :param cars_ds_name: name corresponding to CarsDataset,
288
+ for information during logging
289
+ """
290
+
291
+ self.cars_ds_compute_registry.add_cars_ds_to_compute(cars_ds)
292
+
293
+ # add name if exists
294
+ if cars_ds_name is not None:
295
+ self.cars_ds_names_info.append(cars_ds_name)
296
+
297
+ # add to tracking
298
+ self.achievement_tracker.track(
299
+ cars_ds, self.get_saving_infos([cars_ds])[0][CARS_DATASET_KEY]
300
+ )
301
+
302
+ def save_out_yaml(self):
303
+ """
304
+ Check out_file and save it to file
305
+ """
306
+
307
+ # TODO check schema ?
308
+
309
+ # dump file
310
+ if self.out_yaml_path is not None:
311
+ cars_dataset.save_dict(self.out_yaml, self.out_yaml_path)
312
+
313
+ def save_index(self):
314
+ """
315
+ Save all product index files
316
+ """
317
+
318
+ for product, index in self.product_index.items():
319
+ index_directory = os.path.join(self.out_dir, product)
320
+ safe_makedirs(index_directory)
321
+ cars_dataset.save_dict(
322
+ index,
323
+ os.path.join(index_directory, "index.yaml"),
324
+ )
325
+
326
+ def update_out_info(self, new_dict):
327
+ """
328
+ Update self.out_file with new dict
329
+
330
+ :param new_dict: dict to merge
331
+ :type new_dict: dict
332
+ """
333
+
334
+ # TODO merge with safe creation of new keys of application
335
+ # when 2 same applications are used
336
+
337
+ merge_dicts(self.out_yaml, new_dict)
338
+
339
+ def update_index(self, new_dict):
340
+ """
341
+ Update self.product_index with new dict
342
+
343
+ :param new_dict: dict to merge
344
+ :type new_dict: dict
345
+ """
346
+
347
+ merge_dicts(self.product_index, new_dict)
348
+
349
+ def get_saving_infos(self, cars_ds_list):
350
+ """
351
+ Get saving infos of given cars datasets
352
+
353
+ :param cars_ds_list: list of cars datasets
354
+ :type cars_ds_list: list[CarsDataset]
355
+
356
+ :return : list of saving infos
357
+ :rtype: list[dict]
358
+ """
359
+
360
+ saving_infos = []
361
+
362
+ for cars_ds in cars_ds_list:
363
+ saving_infos.append(self.id_generator.get_saving_infos(cars_ds))
364
+
365
+ return saving_infos
366
+
367
+ def get_data(self, tag, future_object):
368
+ """
369
+ Get data already on disk corresponding to window of object
370
+
371
+ :param tag: tag
372
+ :type tag: str
373
+ :param future_object: object
374
+ :type future_object: xarray Dataset
375
+
376
+ :return: data on disk corresponding to tag
377
+ :rtype: np.ndarray
378
+ """
379
+ data = None
380
+
381
+ # Get descriptor if exists
382
+ obj_id = self.cars_ds_savers_registry.get_future_cars_dataset_id(
383
+ future_object
384
+ )
385
+ cars_ds_saver = (
386
+ self.cars_ds_savers_registry.get_cars_ds_saver_corresponding_id(
387
+ obj_id
388
+ )
389
+ )
390
+
391
+ if len(cars_ds_saver.descriptors) == 0 or tag not in cars_ds_saver.tags:
392
+ # nothing is written yet
393
+ return data, None
394
+
395
+ index = cars_ds_saver.tags.index(tag)
396
+ descriptor = cars_ds_saver.descriptors[index]
397
+ nodata = cars_ds_saver.nodatas[index]
398
+
399
+ # Get window
400
+ window = cars_dataset.get_window_dataset(future_object)
401
+ rio_window = cars_dataset.generate_rasterio_window(window)
402
+
403
+ # Read data window
404
+ # Read data window
405
+ data = descriptor.read(window=rio_window)
406
+
407
+ return data, nodata
408
+
409
+ def compute_futures(self, only_remaining_delayed=None):
410
+ """
411
+ Compute all futures from regitries
412
+
413
+ :param only_remaining_delayed: list of delayed if second run
414
+
415
+ """
416
+
417
+ # save json
418
+ if self.launch_worker:
419
+ self.save_out_yaml()
420
+ self.save_index()
421
+
422
+ # run compute and save files
423
+ logging.info("Compute delayed ...")
424
+ # Flatten to list
425
+ if only_remaining_delayed is None:
426
+ delayed_objects = flatten_object(
427
+ self.cars_ds_savers_registry.get_cars_datasets_list()
428
+ + self.cars_ds_replacer_registry.get_cars_datasets_list()
429
+ + self.cars_ds_compute_registry.get_cars_datasets_list(),
430
+ self.cluster.get_delayed_type(),
431
+ )
432
+ else:
433
+ delayed_objects = only_remaining_delayed
434
+
435
+ if len(delayed_objects) == 0:
436
+ logging.info("No Object to compute")
437
+ return
438
+ # Compute delayed
439
+ future_objects = self.cluster.start_tasks(delayed_objects)
440
+
441
+ # Save objects when they are computed
442
+ logging.info("Wait for futures results ...")
443
+ add_progress_message(
444
+ "Data list to process: [ {} ] ...".format(
445
+ " , ".join(list(set(self.cars_ds_names_info)))
446
+ )
447
+ )
448
+ tqdm_message = "Tiles processing: "
449
+ # if loglevel > PROGRESS level tqdm display the data list
450
+ if logging.getLogger().getEffectiveLevel() > 21:
451
+ tqdm_message = "Processing Tiles: [ {} ] ...".format(
452
+ " , ".join(list(set(self.cars_ds_names_info)))
453
+ )
454
+ pbar = tqdm(
455
+ total=len(future_objects),
456
+ desc=tqdm_message,
457
+ position=0,
458
+ leave=True,
459
+ file=sys.stdout,
460
+ )
461
+ nb_tiles_computed = 0
462
+
463
+ interval_was_cropped = False
464
+ try:
465
+ for future_obj in self.cluster.future_iterator(
466
+ future_objects, timeout=self.task_timeout
467
+ ):
468
+ # get corresponding CarsDataset and save tile
469
+ if future_obj is not None:
470
+ if get_disparity_range_cropped(future_obj):
471
+ interval_was_cropped = True
472
+ # Apply function if exists
473
+ final_function = None
474
+ current_cars_ds = (
475
+ self.cars_ds_savers_registry.get_cars_ds(future_obj)
476
+ )
477
+ if current_cars_ds is None:
478
+ self.cars_ds_replacer_registry.get_cars_ds(
479
+ future_obj
480
+ )
481
+ if current_cars_ds is not None:
482
+ final_function = current_cars_ds.final_function
483
+ if final_function is not None:
484
+ future_obj = final_function(self, future_obj)
485
+ # Save future if needs to
486
+ self.cars_ds_savers_registry.save(future_obj)
487
+ # Replace future in cars_ds if needs to
488
+ self.cars_ds_replacer_registry.replace(future_obj)
489
+ # notify tile profiler for new tile
490
+ self.tile_profiler.add_tile(future_obj)
491
+ # update achievement
492
+ self.achievement_tracker.add_tile(future_obj)
493
+ nb_tiles_computed += 1
494
+ else:
495
+ logging.debug("None tile: not saved")
496
+ pbar.update()
497
+
498
+ except TimeoutError:
499
+ logging.error("TimeOut")
500
+
501
+ if interval_was_cropped:
502
+ logging.warning(
503
+ "Disparity range was cropped in DenseMatching, "
504
+ "due to a lack of available memory for estimated"
505
+ " disparity range"
506
+ )
507
+
508
+ remaining_tiles = self.achievement_tracker.get_remaining_tiles()
509
+ if len(remaining_tiles) > 0:
510
+ # Some tiles have not been computed
511
+ logging.info(
512
+ "{} tiles have not been computed".format(
513
+ len(remaining_tiles)
514
+ )
515
+ )
516
+ if only_remaining_delayed is None:
517
+ # First try
518
+ logging.info("Retry failed tasks ...")
519
+ self.reset_cluster()
520
+ del pbar
521
+ self.compute_futures(only_remaining_delayed=remaining_tiles)
522
+ else:
523
+ # Second try
524
+ logging.error("Pipeline will pursue without failed tiles")
525
+ self.cars_ds_replacer_registry.replace_lasting_jobs(
526
+ self.cluster.get_delayed_type()
527
+ )
528
+ self.reset_registries()
529
+
530
+ if nb_tiles_computed == 0:
531
+ logging.warning(
532
+ "Result have not been saved because all tiles are None"
533
+ )
534
+
535
+ # close files
536
+ logging.info("Close files ...")
537
+ self.cars_ds_savers_registry.cleanup()
538
+ else:
539
+ logging.debug(
540
+ "orchestrator launch_worker is False, no metadata.json saved"
541
+ )
542
+
543
+ def reset_cluster(self):
544
+ """
545
+ Reset Cluster
546
+
547
+ """
548
+
549
+ data_to_propagate = self.cluster.data_to_propagate
550
+
551
+ if self.launch_worker:
552
+ self.cluster.cleanup(keep_shared_dir=True)
553
+ self.cluster = AbstractCluster( # pylint: disable=E0110
554
+ self.orchestrator_conf,
555
+ self.out_dir,
556
+ self.log_dir,
557
+ launch_worker=self.launch_worker,
558
+ data_to_propagate=data_to_propagate,
559
+ )
560
+
561
+ def reset_registries(self):
562
+ """
563
+ Reset registries
564
+ """
565
+
566
+ # cleanup the current registry before replacing it, to save files
567
+ self.cars_ds_savers_registry.cleanup()
568
+
569
+ # reset registries
570
+ # CarsDataset savers registry
571
+ self.cars_ds_savers_registry = saver_registry.CarsDatasetsRegistrySaver(
572
+ self.id_generator
573
+ )
574
+
575
+ # CarsDataset replacement registry
576
+ self.cars_ds_replacer_registry = (
577
+ replacer_registry.CarsDatasetRegistryReplacer(self.id_generator)
578
+ )
579
+ # Compute registry
580
+ self.cars_ds_compute_registry = (
581
+ compute_registry.CarsDatasetRegistryCompute(self.id_generator)
582
+ )
583
+
584
+ # tile profiler
585
+ self.tile_profiler = TileProfiler(
586
+ self.dir_tile_profiling,
587
+ self.cars_ds_savers_registry,
588
+ self.cars_ds_replacer_registry,
589
+ )
590
+
591
+ # achievement tracker
592
+ self.achievement_tracker = achievement_tracker.AchievementTracker()
593
+
594
+ # reset cars_ds names infos
595
+ self.cars_ds_names_info = []
596
+
597
+ @cars_profile(name="Compute futures")
598
+ def breakpoint(self):
599
+ """
600
+ Breakpoint : compute all delayed, save and replace data
601
+ in CarsDatasets
602
+
603
+ """
604
+
605
+ # Compute futures
606
+ try:
607
+ self.compute_futures()
608
+ except Exception as exc:
609
+ # reset registries
610
+ self.reset_registries()
611
+ raise RuntimeError(traceback.format_exc()) from exc
612
+
613
+ # reset registries
614
+ self.reset_registries()
615
+
616
+ def cleanup(self):
617
+ """
618
+ Cleanup orchestrator
619
+
620
+ """
621
+
622
+ # close cluster
623
+ logging.info("Close cluster ...")
624
+ if self.launch_worker:
625
+ self.cluster.cleanup()
626
+
627
+ # # clean tmp dir
628
+ for tmp_dir in self.tmp_dir_list:
629
+ if tmp_dir is not None and os.path.exists(tmp_dir):
630
+ shutil.rmtree(tmp_dir)
631
+
632
+ def __enter__(self):
633
+ """
634
+ Function run on enter
635
+
636
+ """
637
+
638
+ return self
639
+
640
+ def __exit__(self, exc_type, exc_value, traceback_msg):
641
+ """
642
+ Function run on exit.
643
+
644
+ Compute cluster tasks, save futures to be saved, and cleanup cluster
645
+ and files
646
+
647
+ """
648
+
649
+ # Compute futures
650
+ self.breakpoint()
651
+
652
+ # save outjson
653
+ # TODO
654
+
655
+ # TODO : check_json
656
+
657
+ # cleanup
658
+ self.cleanup()
659
+
660
+
661
+ def merge_dicts(dict1, dict2):
662
+ """
663
+ Merge dict2 into dict 1
664
+
665
+ :param dict1: dict 1
666
+ :type dict1: dict
667
+ :param dict2: dict 2
668
+ :type dict2: dict
669
+
670
+ """
671
+
672
+ for key, value2 in dict2.items():
673
+ value1 = dict1.get(key)
674
+ if isinstance(value1, collections.abc.Mapping) and isinstance(
675
+ value2, collections.abc.Mapping
676
+ ):
677
+ merge_dicts(value1, value2)
678
+ else:
679
+ dict1[key] = value2
680
+
681
+
682
+ def flatten_object(cars_ds_list, delayed_type):
683
+ """
684
+ Flatten list of CarsDatasets to list of delayed
685
+
686
+ :param cars_ds_list: list of cars datasets flatten
687
+ :type cars_ds_list: list[CarsDataset]
688
+ :param delayed_type: type of delayed
689
+
690
+ :return: list of delayed
691
+ :rtype: list[Delayed]
692
+ """
693
+
694
+ # remove duplicates
695
+ cleaned_cars_ds_list = list(dict.fromkeys(cars_ds_list))
696
+
697
+ # flatten datasets
698
+ flattened_objects = []
699
+
700
+ if len(cleaned_cars_ds_list) == 1 and cleaned_cars_ds_list[0] is None:
701
+ return []
702
+
703
+ # add obj flattened
704
+ for cars_ds in cleaned_cars_ds_list:
705
+ flattened_objects += [
706
+ obj
707
+ for obj_list in cars_ds.tiles
708
+ for obj in obj_list
709
+ if isinstance(obj, delayed_type) and obj is not None
710
+ ]
711
+
712
+ return flattened_objects
713
+
714
+
715
+ def update_saving_infos(saving_info_left, row=None, col=None):
716
+ """
717
+ Update saving infos dict with row and col arguments
718
+
719
+ :param saving_info_left: saving infos
720
+ :type saving_info_left: dict
721
+ :param row: row
722
+ :type row: int
723
+ :param col: col
724
+ :type col: int
725
+
726
+ :return: updated saving infos dict
727
+ :rtype: dict
728
+ """
729
+
730
+ full_saving_infos = saving_info_left.copy()
731
+
732
+ if row is not None:
733
+ full_saving_infos[CARS_DS_ROW] = row
734
+
735
+ if col is not None:
736
+ full_saving_infos[CARS_DS_COL] = col
737
+
738
+ return full_saving_infos
739
+
740
+
741
+ def get_disparity_range_cropped(obj):
742
+ """
743
+ Get CROPPED_DISPARITY_RANGE value in attributes
744
+
745
+ :param obj: object to look in
746
+
747
+ :rtype bool
748
+ """
749
+
750
+ value = False
751
+
752
+ if isinstance(obj, (xarray.Dataset, pandas.DataFrame)):
753
+ obj_attributes = cars_dataset.get_attributes(obj)
754
+ if obj_attributes is not None:
755
+ value = obj_attributes.get(cst.CROPPED_DISPARITY_RANGE, False)
756
+
757
+ return value
758
+
759
+
760
+ def get_slurm_data():
761
+ """
762
+ Get slurm data
763
+ """
764
+
765
+ def get_data(chain, pattern):
766
+ """
767
+ Get data from pattern
768
+
769
+ :param chain: chain of character to parse
770
+ :param pattern: pattern to find
771
+
772
+ :return: found data
773
+ """
774
+
775
+ match = re.search(pattern, chain)
776
+ value = None
777
+ if match:
778
+ value = match.group(1)
779
+ return int(value)
780
+
781
+ on_slurm = False
782
+ slurm_nb_cpu = None
783
+ slurm_max_ram = None
784
+ try:
785
+ sub_res = subprocess.run(
786
+ "scontrol show job $SLURM_JOB_ID",
787
+ shell=True,
788
+ capture_output=True,
789
+ text=True,
790
+ check=False,
791
+ )
792
+ slurm_infos = sub_res.stdout
793
+
794
+ slurm_nb_cpu = get_data(slurm_infos, r"ReqTRES=cpu=(\d+)")
795
+ slurm_max_ram = get_data(slurm_infos, r"ReqTRES=cpu=.*?mem=(\d+)")
796
+ # convert to Mb
797
+ slurm_max_ram *= 1024
798
+ logging.info("Available CPUs in SLURM : {}".format(slurm_nb_cpu))
799
+ logging.info("Available RAM in SLURM : {}".format(slurm_max_ram))
800
+
801
+ except Exception as _:
802
+ logging.debug("Not on Slurm cluster")
803
+
804
+ if slurm_nb_cpu is not None and slurm_max_ram is not None:
805
+ on_slurm = True
806
+
807
+ return on_slurm, slurm_nb_cpu, slurm_max_ram
808
+
809
+
810
+ def compute_conf_auto_mode(is_windows):
811
+ """
812
+ Compute confuration to use in auto mode
813
+
814
+ :param is_windows: True if runs on windows
815
+ :type is_windows: bool
816
+ """
817
+
818
+ on_slurm, nb_cpu_slurm, max_ram_slurm = get_slurm_data()
819
+
820
+ if on_slurm:
821
+ available_cpu = nb_cpu_slurm
822
+ else:
823
+ available_cpu = (
824
+ multiprocessing.cpu_count()
825
+ if is_windows
826
+ else len(os.sched_getaffinity(0))
827
+ )
828
+ logging.info("available cpu : {}".format(available_cpu))
829
+
830
+ if available_cpu == 1:
831
+ logging.warning("Only one CPU detected.")
832
+ available_cpu = 2
833
+ elif available_cpu == 0:
834
+ logging.warning("No CPU detected.")
835
+ available_cpu = 2
836
+
837
+ if on_slurm:
838
+ ram_to_use = max_ram_slurm
839
+ else:
840
+ ram_to_use = get_total_ram()
841
+ logging.info("total ram : {}".format(ram_to_use))
842
+
843
+ # use 50% of total ram
844
+ ram_to_use *= 0.5
845
+
846
+ # non configurable
847
+ max_ram_per_worker = 2000
848
+ possible_workers = int(ram_to_use // max_ram_per_worker)
849
+ if possible_workers == 0:
850
+ logging.warning("Not enough memory available : failure might occur")
851
+ nb_workers_to_use = max(1, min(possible_workers, available_cpu - 1))
852
+
853
+ logging.info("Number of workers : {}".format(nb_workers_to_use))
854
+ logging.info("Max memory per worker : {} MB".format(max_ram_per_worker))
855
+
856
+ # Check with available ram
857
+ available_ram = get_available_ram()
858
+ if int(nb_workers_to_use) * int(max_ram_per_worker) > available_ram:
859
+ logging.warning(
860
+ "CARS will use 50% of total RAM, "
861
+ " more than currently available RAM"
862
+ )
863
+
864
+ return int(nb_workers_to_use), int(max_ram_per_worker)
865
+
866
+
867
+ def get_available_ram():
868
+ """
869
+ Get available ram
870
+
871
+ :return : available ram in Mb
872
+ """
873
+ ram = psutil.virtual_memory()
874
+ available_ram_mb = ram.available / (1024 * 1024)
875
+ return available_ram_mb
876
+
877
+
878
+ def get_total_ram():
879
+ """
880
+ Get total ram
881
+
882
+ :return : available ram in Mb
883
+ """
884
+ ram = psutil.virtual_memory()
885
+ total_ram_mb = ram.available / (1024 * 1024)
886
+ return total_ram_mb
887
+
888
+
889
+ def check_ram_usage():
890
+ """
891
+ Check RAM usage
892
+ """
893
+ while True:
894
+ # Get Ram information
895
+ available_ram_mb = get_available_ram()
896
+
897
+ if available_ram_mb < RAM_THRESHOLD_MB:
898
+ logging.warning(
899
+ "RAM available < {} Mb, available ram: {} Mb."
900
+ " Freeze might ocure".format(
901
+ RAM_THRESHOLD_MB, int(available_ram_mb)
902
+ )
903
+ )
904
+
905
+ time.sleep(RAM_CHECK_SLEEP_TIME)