ECOv003-L2T-STARS 1.0.1__py3-none-any.whl → 1.2.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.
Files changed (75) hide show
  1. ECOv003_L2T_STARS/BRDF/BRDF.py +57 -0
  2. ECOv003_L2T_STARS/BRDF/SZA.py +65 -0
  3. ECOv003_L2T_STARS/BRDF/__init__.py +1 -0
  4. ECOv003_L2T_STARS/BRDF/statistical_radiative_transport.txt +90 -0
  5. ECOv003_L2T_STARS/BRDF/version.txt +1 -0
  6. ECOv003_L2T_STARS/ECOv003_DL.py +527 -0
  7. ECOv003_L2T_STARS/ECOv003_DL.xml +47 -0
  8. ECOv003_L2T_STARS/ECOv003_L2T_STARS.py +169 -0
  9. ECOv003_L2T_STARS/ECOv003_L2T_STARS.xml +47 -0
  10. ECOv003_L2T_STARS/L2TSTARSConfig.py +190 -0
  11. ECOv003_L2T_STARS/L2T_STARS.py +503 -0
  12. ECOv003_L2T_STARS/LPDAAC/LPDAACDataPool.py +444 -0
  13. ECOv003_L2T_STARS/LPDAAC/__init__.py +9 -0
  14. ECOv003_L2T_STARS/LPDAAC/version.txt +1 -0
  15. ECOv003_L2T_STARS/Manifest.toml +2332 -0
  16. ECOv003_L2T_STARS/Project.toml +14 -0
  17. ECOv003_L2T_STARS/VIIRS/VIIRSDataPool.py +294 -0
  18. ECOv003_L2T_STARS/VIIRS/VIIRSDownloader.py +26 -0
  19. ECOv003_L2T_STARS/VIIRS/VIIRS_CMR_LOGIN.py +36 -0
  20. ECOv003_L2T_STARS/VIIRS/VNP09GA.py +1278 -0
  21. ECOv003_L2T_STARS/VIIRS/VNP43IA4.py +288 -0
  22. ECOv003_L2T_STARS/VIIRS/VNP43MA3.py +323 -0
  23. ECOv003_L2T_STARS/VIIRS/__init__.py +9 -0
  24. ECOv003_L2T_STARS/VIIRS/version.txt +1 -0
  25. ECOv003_L2T_STARS/VNP43NRT/VNP43NRT.py +863 -0
  26. ECOv003_L2T_STARS/VNP43NRT/__init__.py +1 -0
  27. ECOv003_L2T_STARS/VNP43NRT/process_VNP43NRT.jl +169 -0
  28. ECOv003_L2T_STARS/VNP43NRT/version.txt +1 -0
  29. ECOv003_L2T_STARS/VNP43NRT_jl/Manifest.toml +995 -0
  30. ECOv003_L2T_STARS/VNP43NRT_jl/Project.toml +15 -0
  31. ECOv003_L2T_STARS/VNP43NRT_jl/__init__.py +0 -0
  32. ECOv003_L2T_STARS/VNP43NRT_jl/instantiate.jl +25 -0
  33. ECOv003_L2T_STARS/VNP43NRT_jl/instantiate.py +13 -0
  34. ECOv003_L2T_STARS/VNP43NRT_jl/src/VNP43NRT.jl +411 -0
  35. ECOv003_L2T_STARS/VNP43NRT_jl/src/__init__.py +0 -0
  36. ECOv003_L2T_STARS/__init__.py +3 -0
  37. ECOv003_L2T_STARS/calibrate_fine_to_coarse.py +60 -0
  38. ECOv003_L2T_STARS/constants.py +38 -0
  39. ECOv003_L2T_STARS/daterange/__init__.py +1 -0
  40. ECOv003_L2T_STARS/daterange/daterange.py +35 -0
  41. ECOv003_L2T_STARS/generate_L2T_STARS_runconfig.py +249 -0
  42. ECOv003_L2T_STARS/generate_NDVI_coarse_directory.py +21 -0
  43. ECOv003_L2T_STARS/generate_NDVI_coarse_image.py +30 -0
  44. ECOv003_L2T_STARS/generate_NDVI_fine_directory.py +14 -0
  45. ECOv003_L2T_STARS/generate_NDVI_fine_image.py +28 -0
  46. ECOv003_L2T_STARS/generate_STARS_inputs.py +231 -0
  47. ECOv003_L2T_STARS/generate_albedo_coarse_directory.py +18 -0
  48. ECOv003_L2T_STARS/generate_albedo_coarse_image.py +30 -0
  49. ECOv003_L2T_STARS/generate_albedo_fine_directory.py +17 -0
  50. ECOv003_L2T_STARS/generate_albedo_fine_image.py +30 -0
  51. ECOv003_L2T_STARS/generate_filename.py +37 -0
  52. ECOv003_L2T_STARS/generate_input_staging_directory.py +23 -0
  53. ECOv003_L2T_STARS/generate_model_state_tile_date_directory.py +28 -0
  54. ECOv003_L2T_STARS/generate_output_directory.py +28 -0
  55. ECOv003_L2T_STARS/install_STARS_jl.py +43 -0
  56. ECOv003_L2T_STARS/instantiate_STARS_jl.py +38 -0
  57. ECOv003_L2T_STARS/load_prior.py +250 -0
  58. ECOv003_L2T_STARS/prior.py +56 -0
  59. ECOv003_L2T_STARS/process_ECOSTRESS_data_fusion_distributed_bias.jl +420 -0
  60. ECOv003_L2T_STARS/process_STARS_product.py +507 -0
  61. ECOv003_L2T_STARS/process_julia_data_fusion.py +110 -0
  62. ECOv003_L2T_STARS/retrieve_STARS_sources.py +101 -0
  63. ECOv003_L2T_STARS/runconfig.py +70 -0
  64. ECOv003_L2T_STARS/timer/__init__.py +1 -0
  65. ECOv003_L2T_STARS/timer/timer.py +77 -0
  66. ECOv003_L2T_STARS/version.py +8 -0
  67. ECOv003_L2T_STARS/version.txt +1 -0
  68. {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.2.0.dist-info}/METADATA +31 -24
  69. ecov003_l2t_stars-1.2.0.dist-info/RECORD +73 -0
  70. {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.2.0.dist-info}/WHEEL +1 -1
  71. ecov003_l2t_stars-1.2.0.dist-info/entry_points.txt +3 -0
  72. ecov003_l2t_stars-1.2.0.dist-info/top_level.txt +1 -0
  73. ECOv003_L2T_STARS-1.0.1.dist-info/RECORD +0 -5
  74. ECOv003_L2T_STARS-1.0.1.dist-info/top_level.txt +0 -1
  75. {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.2.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,169 @@
1
+ import sys
2
+ import argparse
3
+ import logging
4
+
5
+ from .version import __version__
6
+ from .constants import *
7
+ from .L2T_STARS import L2T_STARS
8
+
9
+ # Initialize the logger for the module
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ def main():
14
+ """
15
+ Main function for parsing command-line arguments and running the L2T_STARS PGE.
16
+
17
+ This function uses `argparse` for robust command-line argument parsing,
18
+ providing a clear interface for users to specify the run-configuration file
19
+ and other optional parameters.
20
+ """
21
+ parser = argparse.ArgumentParser(
22
+ description="ECOSTRESS Collection 3 L2T_STARS PGE for generating Tiled Auxiliary NDVI and Albedo products.",
23
+ formatter_class=argparse.RawTextHelpFormatter, # Allows for more flexible help text formatting
24
+ epilog=f"L2T_STARS PGE Version: {__version__}\n\n"
25
+ "Example usage:\n"
26
+ " python {sys.argv[0]} --runconfig /path/to/RunConfig.xml\n"
27
+ " python {sys.argv[0]} --runconfig /path/to/RunConfig.xml --date 2023-01-15\n"
28
+ " python {sys.argv[0]} --runconfig /path/to/RunConfig.xml --sources-only\n"
29
+ " python {sys.argv[0]} --runconfig /path/to/RunConfig.xml --overwrite\n" # Added example usage
30
+ )
31
+
32
+ # Positional argument for the runconfig file
33
+ parser.add_argument(
34
+ "runconfig",
35
+ type=str,
36
+ help="Path to the XML run-configuration file.",
37
+ )
38
+
39
+ # Optional arguments
40
+ parser.add_argument(
41
+ "--date",
42
+ type=str,
43
+ help="Target UTC date for product generation (YYYY-MM-DD). Overrides date in runconfig.",
44
+ metavar="YYYY-MM-DD"
45
+ )
46
+ parser.add_argument(
47
+ "--spinup-days",
48
+ type=int,
49
+ default=DEFAULT_SPINUP_DAYS,
50
+ help=f"Number of days for the VIIRS time-series spin-up. Defaults to {DEFAULT_SPINUP_DAYS} days.",
51
+ metavar="DAYS"
52
+ )
53
+ parser.add_argument(
54
+ "--target-resolution",
55
+ type=int,
56
+ default=DEFAULT_TARGET_RESOLUTION,
57
+ help=f"Desired output product resolution in meters. Defaults to {DEFAULT_TARGET_RESOLUTION}m.",
58
+ metavar="METERS"
59
+ )
60
+ parser.add_argument(
61
+ "--ndvi-resolution",
62
+ type=int,
63
+ default=DEFAULT_NDVI_RESOLUTION,
64
+ help=f"Resolution of coarse NDVI data in meters. Defaults to {DEFAULT_NDVI_RESOLUTION}m.",
65
+ metavar="METERS"
66
+ )
67
+ parser.add_argument(
68
+ "--albedo-resolution",
69
+ type=int,
70
+ default=DEFAULT_ALBEDO_RESOLUTION,
71
+ help=f"Resolution of coarse albedo data in meters. Defaults to {DEFAULT_ALBEDO_RESOLUTION}m.",
72
+ metavar="METERS"
73
+ )
74
+ parser.add_argument(
75
+ "--use-vnp43nrt",
76
+ action="store_true",
77
+ default=DEFAULT_USE_VNP43NRT,
78
+ help=f"Use VNP43NRT for VIIRS products. Defaults to {'True' if DEFAULT_USE_VNP43NRT else 'False'}.",
79
+ )
80
+ parser.add_argument(
81
+ "--no-vnp43nrt",
82
+ action="store_false",
83
+ dest="use_vnp43nrt", # This argument sets use_vnp43nrt to False
84
+ help="Do NOT use VNP43NRT for VIIRS products. Use VNP43IA4/VNP43MA3 instead.",
85
+ )
86
+ parser.add_argument(
87
+ "--calibrate-fine",
88
+ action="store_true",
89
+ default=DEFAULT_CALIBRATE_FINE,
90
+ help=f"Calibrate fine resolution HLS data to coarse resolution VIIRS data. Defaults to {'True' if DEFAULT_CALIBRATE_FINE else 'False'}.",
91
+ )
92
+ parser.add_argument(
93
+ "--sources-only",
94
+ action="store_true",
95
+ help="Only retrieve and stage source data (HLS, VIIRS); do not perform data fusion or generate final product.",
96
+ )
97
+ parser.add_argument(
98
+ "--no-remove-input-staging",
99
+ action="store_false",
100
+ dest="remove_input_staging",
101
+ default=True,
102
+ help="Do NOT remove the input staging directory after processing.",
103
+ )
104
+ parser.add_argument(
105
+ "--no-remove-prior",
106
+ action="store_false",
107
+ dest="remove_prior",
108
+ default=True,
109
+ help="Do NOT remove prior intermediate files after use.",
110
+ )
111
+ parser.add_argument(
112
+ "--no-remove-posterior",
113
+ action="store_false",
114
+ dest="remove_posterior",
115
+ default=True,
116
+ help="Do NOT remove posterior intermediate files after product generation.",
117
+ )
118
+ parser.add_argument(
119
+ "--threads",
120
+ type=str,
121
+ default="auto",
122
+ help='Number of Julia threads to use, or "auto". Defaults to "auto".',
123
+ metavar="COUNT"
124
+ )
125
+ parser.add_argument(
126
+ "--num-workers",
127
+ type=int,
128
+ default=4,
129
+ help=f"Number of Julia workers for distributed processing. Defaults to 4.",
130
+ metavar="COUNT"
131
+ )
132
+ parser.add_argument(
133
+ "--overwrite", # New argument for overwrite option
134
+ action="store_true",
135
+ help="Reproduce the output files even if they already exist.",
136
+ )
137
+ parser.add_argument(
138
+ "--version",
139
+ action="version",
140
+ version=f"%(prog)s {__version__}",
141
+ help="Show program's version number and exit.",
142
+ )
143
+
144
+ args = parser.parse_args()
145
+
146
+ # Call the main L2T_STARS processing function with parsed arguments
147
+ exit_code = L2T_STARS(
148
+ runconfig_filename=args.runconfig,
149
+ date_UTC=args.date,
150
+ spinup_days=args.spinup_days,
151
+ target_resolution=args.target_resolution,
152
+ NDVI_resolution=args.ndvi_resolution,
153
+ albedo_resolution=args.albedo_resolution,
154
+ use_VNP43NRT=args.use_vnp43nrt,
155
+ calibrate_fine=args.calibrate_fine,
156
+ sources_only=args.sources_only,
157
+ remove_input_staging=args.remove_input_staging,
158
+ remove_prior=args.remove_prior,
159
+ remove_posterior=args.remove_posterior,
160
+ threads=args.threads,
161
+ num_workers=args.num_workers,
162
+ overwrite=args.overwrite, # Pass the new overwrite argument
163
+ )
164
+
165
+ sys.exit(exit_code)
166
+
167
+
168
+ if __name__ == "__main__":
169
+ main()
@@ -0,0 +1,47 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <input xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xmlns:xalan="http://xml.apache.org/xslt"
4
+ xmlns:cas="http://oodt.jpl.nasa.gov/1.0/cas"
5
+ xsi:noNamespaceSchemaLocation="">
6
+ <group name="LogMetadata">
7
+ <vector name="CommandLineParameters">
8
+ <element>executable_filename</element>
9
+ <element>runconfig_filename</element>
10
+ <element>log_filename</element>
11
+ </vector>
12
+ </group>
13
+ <group name="JobIdentification">
14
+ <scalar name="ProductionLocation">ECOSTRESS Science Computing Facility</scalar>
15
+ <scalar name="TaskId">urn:ecostress:L2GL2TLSTETask</scalar>
16
+ <scalar name="ProcessingNode">processing_node</scalar>
17
+ <scalar name="ProductionDateTime">production_datetime</scalar>
18
+ <scalar name="JobId">job_ID</scalar>
19
+ <scalar name="WorkflowInstanceId">instance_ID</scalar>
20
+ </group>
21
+ <group name="InputFileGroup">
22
+ <scalar name="L2T_LSTE">L2T_LSTE_filename</scalar>
23
+ <scalar name="L2T_STARS_PRIOR">prior_L2T_STARS_filename</scalar>
24
+ </group>
25
+ <group name="StaticAncillaryFileGroup">
26
+ <scalar name="L2T_STARS_MODEL">model_directory</scalar>
27
+ <scalar name="L2T_STARS_INDICES">indices_directory</scalar>
28
+ <scalar name="L2T_STARS_SOURCES">sources_directory</scalar>
29
+ <scalar name="L2T_STARS_WORKING">working_directory</scalar>
30
+ </group>
31
+ <group name="PrimaryExecutable">
32
+ <scalar name="BuildID">build_ID</scalar>
33
+ <scalar name="FullPathname">executable_filename</scalar>
34
+ </group>
35
+ <group name="Geometry">
36
+ <scalar name="TileId">tile_ID</scalar>
37
+ <scalar name="SceneId">scene_ID</scalar>
38
+ <scalar name="OrbitNumber">orbit_number</scalar>
39
+ </group>
40
+ <group name="ProductPathGroup">
41
+ <scalar name="ProductCounter">product_counter</scalar>
42
+ <scalar name="ProductPath">output_directory</scalar>
43
+ </group>
44
+ <group name="PGENameGroup">
45
+ <scalar name="PGEName">L2T_STARS_PGE</scalar>
46
+ </group>
47
+ </input>
@@ -0,0 +1,190 @@
1
+ from os.path import exists
2
+ import logging
3
+
4
+ from dateutil import parser
5
+
6
+ import colored_logging as cl
7
+
8
+ from ECOv003_exit_codes import *
9
+ from ECOv003_granules import L2TLSTE
10
+
11
+ from .constants import *
12
+ from .runconfig import ECOSTRESSRunConfig
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class L2TSTARSConfig(ECOSTRESSRunConfig):
17
+ """
18
+ Parses and validates the L2T_STARS specific parameters from an XML run-configuration file.
19
+
20
+ This class extends the base ECOSTRESSRunConfig to extract paths, IDs,
21
+ and processing parameters relevant to the L2T_STARS product generation.
22
+ It performs validation to ensure all critical parameters are present.
23
+ """
24
+
25
+ def __init__(self, filename: str):
26
+ """
27
+ Initializes the L2TSTARSConfig by parsing the provided run-config XML file.
28
+
29
+ Args:
30
+ filename (str): The path to the L2T_STARS run-configuration XML file.
31
+
32
+ Raises:
33
+ MissingRunConfigValue: If a required value is missing from the run-config.
34
+ UnableToParseRunConfig: If the run-config file cannot be parsed due to other errors.
35
+ """
36
+ logger.info(f"Loading L2T_STARS run-config: {cl.file(filename)}")
37
+ # Read the run-config XML into a dictionary
38
+ runconfig = self.read_runconfig(filename)
39
+
40
+ # reverting to StaticAncillaryFileGroup for now instead of StaticAuxiliaryFileGroup
41
+
42
+ try:
43
+ # Validate and extract working directory from StaticAncillaryFileGroup
44
+ if "StaticAncillaryFileGroup" not in runconfig:
45
+ raise MissingRunConfigValue(
46
+ f"Missing StaticAncillaryFileGroup in L2T_STARS run-config: {filename}"
47
+ )
48
+ if "L2T_STARS_WORKING" not in runconfig["StaticAncillaryFileGroup"]:
49
+ raise MissingRunConfigValue(
50
+ f"Missing StaticAncillaryFileGroup/L2T_STARS_WORKING in L2T_STARS run-config: {filename}"
51
+ )
52
+ self.working_directory = abspath(
53
+ runconfig["StaticAncillaryFileGroup"]["L2T_STARS_WORKING"]
54
+ )
55
+ logger.info(f"Working directory: {cl.dir(self.working_directory)}")
56
+
57
+ # Validate and extract sources directory
58
+ if "L2T_STARS_SOURCES" not in runconfig["StaticAncillaryFileGroup"]:
59
+ raise MissingRunConfigValue(
60
+ f"Missing StaticAncillaryFileGroup/L2T_STARS_SOURCES in L2T_STARS run-config: {filename}"
61
+ )
62
+ self.sources_directory = abspath(
63
+ runconfig["StaticAncillaryFileGroup"]["L2T_STARS_SOURCES"]
64
+ )
65
+ logger.info(f"Sources directory: {cl.dir(self.sources_directory)}")
66
+
67
+ # Validate and extract indices directory
68
+ if "L2T_STARS_INDICES" not in runconfig["StaticAncillaryFileGroup"]:
69
+ raise MissingRunConfigValue(
70
+ f"Missing StaticAncillaryFileGroup/L2T_STARS_INDICES in L2T_STARS run-config: {filename}"
71
+ )
72
+ self.indices_directory = abspath(
73
+ runconfig["StaticAncillaryFileGroup"]["L2T_STARS_INDICES"]
74
+ )
75
+ logger.info(f"Indices directory: {cl.dir(self.indices_directory)}")
76
+
77
+ # Validate and extract model directory
78
+ if "L2T_STARS_MODEL" not in runconfig["StaticAncillaryFileGroup"]:
79
+ raise MissingRunConfigValue(
80
+ f"Missing StaticAncillaryFileGroup/L2T_STARS_MODEL in L2T_STARS run-config: {filename}"
81
+ )
82
+ self.model_directory = abspath(
83
+ runconfig["StaticAncillaryFileGroup"]["L2T_STARS_MODEL"]
84
+ )
85
+ logger.info(f"Model directory: {cl.dir(self.model_directory)}")
86
+
87
+ # Validate and extract output directory from ProductPathGroup
88
+ if "ProductPathGroup" not in runconfig:
89
+ raise MissingRunConfigValue(
90
+ f"Missing ProductPathGroup in L2T_STARS run-config: {filename}"
91
+ )
92
+ if "ProductPath" not in runconfig["ProductPathGroup"]:
93
+ raise MissingRunConfigValue(
94
+ f"Missing ProductPathGroup/ProductPath in L2T_STARS run-config: {filename}"
95
+ )
96
+ self.output_directory = abspath(
97
+ runconfig["ProductPathGroup"]["ProductPath"]
98
+ )
99
+ logger.info(f"Output directory: {cl.dir(self.output_directory)}")
100
+
101
+ # Validate and extract input L2T_LSTE filename
102
+ if "InputFileGroup" not in runconfig:
103
+ raise MissingRunConfigValue(
104
+ f"Missing InputFileGroup in L2G_L2T_LSTE run-config: {filename}"
105
+ )
106
+ if "L2T_LSTE" not in runconfig["InputFileGroup"]:
107
+ raise MissingRunConfigValue(
108
+ f"Missing InputFileGroup/L2T_LSTE in L2T_STARS run-config: {filename}"
109
+ )
110
+ self.L2T_LSTE_filename = abspath(runconfig["InputFileGroup"]["L2T_LSTE"])
111
+ logger.info(f"L2T_LSTE file: {cl.file(self.L2T_LSTE_filename)}")
112
+
113
+ # Extract optional prior L2T_STARS filename
114
+ self.L2T_STARS_prior_filename = None
115
+ if "L2T_STARS_PRIOR" in runconfig["InputFileGroup"]:
116
+ prior_filename = runconfig["InputFileGroup"]["L2T_STARS_PRIOR"]
117
+ if prior_filename != "" and exists(prior_filename):
118
+ self.L2T_STARS_prior_filename = abspath(prior_filename)
119
+ logger.info(
120
+ f"L2T_STARS prior file: {cl.file(self.L2T_STARS_prior_filename)}"
121
+ )
122
+
123
+ # Extract geometry parameters (orbit, scene, tile)
124
+ self.orbit = int(runconfig["Geometry"]["OrbitNumber"])
125
+ logger.info(f"Orbit: {cl.val(self.orbit)}")
126
+ if "SceneId" not in runconfig["Geometry"]:
127
+ raise MissingRunConfigValue(
128
+ f"Missing Geometry/SceneId in L2T_STARS run-config: {filename}"
129
+ )
130
+ self.scene = int(runconfig["Geometry"]["SceneId"])
131
+ logger.info(f"Scene: {cl.val(self.scene)}")
132
+ if "TileId" not in runconfig["Geometry"]:
133
+ raise MissingRunConfigValue(
134
+ f"Missing Geometry/TileId in L2T_STARS run-config: {filename}"
135
+ )
136
+ self.tile = str(runconfig["Geometry"]["TileId"])
137
+ logger.info(f"Tile: {cl.val(self.tile)}")
138
+
139
+ # Extract production details
140
+ if "ProductionDateTime" not in runconfig["JobIdentification"]:
141
+ raise MissingRunConfigValue(
142
+ f"Missing JobIdentification/ProductionDateTime in L2T_STARS run-config {filename}"
143
+ )
144
+ self.production_datetime = parser.parse(
145
+ runconfig["JobIdentification"]["ProductionDateTime"]
146
+ )
147
+ logger.info(f"Production time: {cl.time(self.production_datetime)}")
148
+
149
+ # Extract build ID
150
+ if "BuildID" not in runconfig["PrimaryExecutable"]:
151
+ raise MissingRunConfigValue(
152
+ f"Missing PrimaryExecutable/BuildID in L2T_STARS run-config {filename}"
153
+ )
154
+ self.build = str(runconfig["PrimaryExecutable"]["BuildID"])
155
+
156
+ # Extract product counter
157
+ if "ProductCounter" not in runconfig["ProductPathGroup"]:
158
+ raise MissingRunConfigValue(
159
+ f"Missing ProductPathGroup/ProductCounter in L2T_STARS run-config {filename}"
160
+ )
161
+ self.product_counter = int(runconfig["ProductPathGroup"]["ProductCounter"])
162
+
163
+ # Get UTC time from the L2T_LSTE granule itself
164
+ l2t_lste_granule_obj = L2TLSTE(self.L2T_LSTE_filename)
165
+ time_UTC = l2t_lste_granule_obj.time_UTC
166
+
167
+ # Construct the full granule ID and paths for the output product
168
+ granule_ID = (
169
+ f"ECOv003_L2T_STARS_{self.tile}_{time_UTC:%Y%m%d}_{self.build}_"
170
+ f"{self.product_counter:02d}"
171
+ )
172
+ self.granule_ID = granule_ID
173
+ self.L2T_STARS_granule_directory = join(self.output_directory, granule_ID)
174
+ self.L2T_STARS_zip_filename = f"{self.L2T_STARS_granule_directory}.zip"
175
+ self.L2T_STARS_browse_filename = (
176
+ f"{self.L2T_STARS_granule_directory}.png"
177
+ )
178
+
179
+ except MissingRunConfigValue as e:
180
+ # Re-raise specific missing value errors
181
+ raise e
182
+ except ECOSTRESSExitCodeException as e:
183
+ # Re-raise custom ECOSTRESS exit code exceptions
184
+ raise e
185
+ except Exception as e:
186
+ # Catch any other parsing errors and raise a generic UnableToParseRunConfig
187
+ logger.exception(e)
188
+ raise UnableToParseRunConfig(
189
+ f"Unable to parse run-config file: {filename}"
190
+ )