ECOv003-L2T-STARS 1.0.0__py3-none-any.whl → 1.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.
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 +162 -0
  9. ECOv003_L2T_STARS/ECOv003_L2T_STARS.xml +47 -0
  10. ECOv003_L2T_STARS/L2TSTARSConfig.py +188 -0
  11. ECOv003_L2T_STARS/L2T_STARS.py +489 -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 +1277 -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 +248 -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.0.dist-info → ecov003_l2t_stars-1.1.0.dist-info}/METADATA +30 -23
  69. ecov003_l2t_stars-1.1.0.dist-info/RECORD +73 -0
  70. {ECOv003_L2T_STARS-1.0.0.dist-info → ecov003_l2t_stars-1.1.0.dist-info}/WHEEL +1 -1
  71. ecov003_l2t_stars-1.1.0.dist-info/entry_points.txt +3 -0
  72. ecov003_l2t_stars-1.1.0.dist-info/top_level.txt +1 -0
  73. ECOv003_L2T_STARS-1.0.0.dist-info/RECORD +0 -5
  74. ECOv003_L2T_STARS-1.0.0.dist-info/top_level.txt +0 -1
  75. {ECOv003_L2T_STARS-1.0.0.dist-info → ecov003_l2t_stars-1.1.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,162 @@
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
+ )
30
+
31
+ # Positional argument for the runconfig file
32
+ parser.add_argument(
33
+ "runconfig",
34
+ type=str,
35
+ help="Path to the XML run-configuration file.",
36
+ )
37
+
38
+ # Optional arguments
39
+ parser.add_argument(
40
+ "--date",
41
+ type=str,
42
+ help="Target UTC date for product generation (YYYY-MM-DD). Overrides date in runconfig.",
43
+ metavar="YYYY-MM-DD"
44
+ )
45
+ parser.add_argument(
46
+ "--spinup-days",
47
+ type=int,
48
+ default=DEFAULT_SPINUP_DAYS,
49
+ help=f"Number of days for the VIIRS time-series spin-up. Defaults to {DEFAULT_SPINUP_DAYS} days.",
50
+ metavar="DAYS"
51
+ )
52
+ parser.add_argument(
53
+ "--target-resolution",
54
+ type=int,
55
+ default=DEFAULT_TARGET_RESOLUTION,
56
+ help=f"Desired output product resolution in meters. Defaults to {DEFAULT_TARGET_RESOLUTION}m.",
57
+ metavar="METERS"
58
+ )
59
+ parser.add_argument(
60
+ "--ndvi-resolution",
61
+ type=int,
62
+ default=DEFAULT_NDVI_RESOLUTION,
63
+ help=f"Resolution of coarse NDVI data in meters. Defaults to {DEFAULT_NDVI_RESOLUTION}m.",
64
+ metavar="METERS"
65
+ )
66
+ parser.add_argument(
67
+ "--albedo-resolution",
68
+ type=int,
69
+ default=DEFAULT_ALBEDO_RESOLUTION,
70
+ help=f"Resolution of coarse albedo data in meters. Defaults to {DEFAULT_ALBEDO_RESOLUTION}m.",
71
+ metavar="METERS"
72
+ )
73
+ parser.add_argument(
74
+ "--use-vnp43nrt",
75
+ action="store_true",
76
+ default=DEFAULT_USE_VNP43NRT,
77
+ help=f"Use VNP43NRT for VIIRS products. Defaults to {'True' if DEFAULT_USE_VNP43NRT else 'False'}.",
78
+ )
79
+ parser.add_argument(
80
+ "--no-vnp43nrt",
81
+ action="store_false",
82
+ dest="use_vnp43nrt", # This argument sets use_vnp43nrt to False
83
+ help="Do NOT use VNP43NRT for VIIRS products. Use VNP43IA4/VNP43MA3 instead.",
84
+ )
85
+ parser.add_argument(
86
+ "--calibrate-fine",
87
+ action="store_true",
88
+ default=DEFAULT_CALIBRATE_FINE,
89
+ help=f"Calibrate fine resolution HLS data to coarse resolution VIIRS data. Defaults to {'True' if DEFAULT_CALIBRATE_FINE else 'False'}.",
90
+ )
91
+ parser.add_argument(
92
+ "--sources-only",
93
+ action="store_true",
94
+ help="Only retrieve and stage source data (HLS, VIIRS); do not perform data fusion or generate final product.",
95
+ )
96
+ parser.add_argument(
97
+ "--no-remove-input-staging",
98
+ action="store_false",
99
+ dest="remove_input_staging",
100
+ default=True,
101
+ help="Do NOT remove the input staging directory after processing.",
102
+ )
103
+ parser.add_argument(
104
+ "--no-remove-prior",
105
+ action="store_false",
106
+ dest="remove_prior",
107
+ default=True,
108
+ help="Do NOT remove prior intermediate files after use.",
109
+ )
110
+ parser.add_argument(
111
+ "--no-remove-posterior",
112
+ action="store_false",
113
+ dest="remove_posterior",
114
+ default=True,
115
+ help="Do NOT remove posterior intermediate files after product generation.",
116
+ )
117
+ parser.add_argument(
118
+ "--threads",
119
+ type=str,
120
+ default="auto",
121
+ help='Number of Julia threads to use, or "auto". Defaults to "auto".',
122
+ metavar="COUNT"
123
+ )
124
+ parser.add_argument(
125
+ "--num-workers",
126
+ type=int,
127
+ default=4,
128
+ help=f"Number of Julia workers for distributed processing. Defaults to 4.",
129
+ metavar="COUNT"
130
+ )
131
+ parser.add_argument(
132
+ "--version",
133
+ action="version",
134
+ version=f"%(prog)s {__version__}",
135
+ help="Show program's version number and exit.",
136
+ )
137
+
138
+ args = parser.parse_args()
139
+
140
+ # Call the main L2T_STARS processing function with parsed arguments
141
+ exit_code = L2T_STARS(
142
+ runconfig_filename=args.runconfig,
143
+ date_UTC=args.date,
144
+ spinup_days=args.spinup_days,
145
+ target_resolution=args.target_resolution,
146
+ NDVI_resolution=args.ndvi_resolution,
147
+ albedo_resolution=args.albedo_resolution,
148
+ use_VNP43NRT=args.use_vnp43nrt,
149
+ calibrate_fine=args.calibrate_fine,
150
+ sources_only=args.sources_only,
151
+ remove_input_staging=args.remove_input_staging,
152
+ remove_prior=args.remove_prior,
153
+ remove_posterior=args.remove_posterior,
154
+ threads=args.threads,
155
+ num_workers=args.num_workers,
156
+ )
157
+
158
+ sys.exit(exit_code)
159
+
160
+
161
+ if __name__ == "__main__":
162
+ 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="StaticAuxiliaryFileGroup">
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,188 @@
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
+ try:
41
+ # Validate and extract working directory from StaticAuxiliaryFileGroup
42
+ if "StaticAuxiliaryFileGroup" not in runconfig:
43
+ raise MissingRunConfigValue(
44
+ f"Missing StaticAuxiliaryFileGroup in L2T_STARS run-config: {filename}"
45
+ )
46
+ if "L2T_STARS_WORKING" not in runconfig["StaticAuxiliaryFileGroup"]:
47
+ raise MissingRunConfigValue(
48
+ f"Missing StaticAuxiliaryFileGroup/L2T_STARS_WORKING in L2T_STARS run-config: {filename}"
49
+ )
50
+ self.working_directory = abspath(
51
+ runconfig["StaticAuxiliaryFileGroup"]["L2T_STARS_WORKING"]
52
+ )
53
+ logger.info(f"Working directory: {cl.dir(self.working_directory)}")
54
+
55
+ # Validate and extract sources directory
56
+ if "L2T_STARS_SOURCES" not in runconfig["StaticAuxiliaryFileGroup"]:
57
+ raise MissingRunConfigValue(
58
+ f"Missing StaticAuxiliaryFileGroup/L2T_STARS_SOURCES in L2T_STARS run-config: {filename}"
59
+ )
60
+ self.sources_directory = abspath(
61
+ runconfig["StaticAuxiliaryFileGroup"]["L2T_STARS_SOURCES"]
62
+ )
63
+ logger.info(f"Sources directory: {cl.dir(self.sources_directory)}")
64
+
65
+ # Validate and extract indices directory
66
+ if "L2T_STARS_INDICES" not in runconfig["StaticAuxiliaryFileGroup"]:
67
+ raise MissingRunConfigValue(
68
+ f"Missing StaticAuxiliaryFileGroup/L2T_STARS_INDICES in L2T_STARS run-config: {filename}"
69
+ )
70
+ self.indices_directory = abspath(
71
+ runconfig["StaticAuxiliaryFileGroup"]["L2T_STARS_INDICES"]
72
+ )
73
+ logger.info(f"Indices directory: {cl.dir(self.indices_directory)}")
74
+
75
+ # Validate and extract model directory
76
+ if "L2T_STARS_MODEL" not in runconfig["StaticAuxiliaryFileGroup"]:
77
+ raise MissingRunConfigValue(
78
+ f"Missing StaticAuxiliaryFileGroup/L2T_STARS_MODEL in L2T_STARS run-config: {filename}"
79
+ )
80
+ self.model_directory = abspath(
81
+ runconfig["StaticAuxiliaryFileGroup"]["L2T_STARS_MODEL"]
82
+ )
83
+ logger.info(f"Model directory: {cl.dir(self.model_directory)}")
84
+
85
+ # Validate and extract output directory from ProductPathGroup
86
+ if "ProductPathGroup" not in runconfig:
87
+ raise MissingRunConfigValue(
88
+ f"Missing ProductPathGroup in L2T_STARS run-config: {filename}"
89
+ )
90
+ if "ProductPath" not in runconfig["ProductPathGroup"]:
91
+ raise MissingRunConfigValue(
92
+ f"Missing ProductPathGroup/ProductPath in L2T_STARS run-config: {filename}"
93
+ )
94
+ self.output_directory = abspath(
95
+ runconfig["ProductPathGroup"]["ProductPath"]
96
+ )
97
+ logger.info(f"Output directory: {cl.dir(self.output_directory)}")
98
+
99
+ # Validate and extract input L2T_LSTE filename
100
+ if "InputFileGroup" not in runconfig:
101
+ raise MissingRunConfigValue(
102
+ f"Missing InputFileGroup in L2G_L2T_LSTE run-config: {filename}"
103
+ )
104
+ if "L2T_LSTE" not in runconfig["InputFileGroup"]:
105
+ raise MissingRunConfigValue(
106
+ f"Missing InputFileGroup/L2T_LSTE in L2T_STARS run-config: {filename}"
107
+ )
108
+ self.L2T_LSTE_filename = abspath(runconfig["InputFileGroup"]["L2T_LSTE"])
109
+ logger.info(f"L2T_LSTE file: {cl.file(self.L2T_LSTE_filename)}")
110
+
111
+ # Extract optional prior L2T_STARS filename
112
+ self.L2T_STARS_prior_filename = None
113
+ if "L2T_STARS_PRIOR" in runconfig["InputFileGroup"]:
114
+ prior_filename = runconfig["InputFileGroup"]["L2T_STARS_PRIOR"]
115
+ if prior_filename != "" and exists(prior_filename):
116
+ self.L2T_STARS_prior_filename = abspath(prior_filename)
117
+ logger.info(
118
+ f"L2T_STARS prior file: {cl.file(self.L2T_STARS_prior_filename)}"
119
+ )
120
+
121
+ # Extract geometry parameters (orbit, scene, tile)
122
+ self.orbit = int(runconfig["Geometry"]["OrbitNumber"])
123
+ logger.info(f"Orbit: {cl.val(self.orbit)}")
124
+ if "SceneId" not in runconfig["Geometry"]:
125
+ raise MissingRunConfigValue(
126
+ f"Missing Geometry/SceneId in L2T_STARS run-config: {filename}"
127
+ )
128
+ self.scene = int(runconfig["Geometry"]["SceneId"])
129
+ logger.info(f"Scene: {cl.val(self.scene)}")
130
+ if "TileId" not in runconfig["Geometry"]:
131
+ raise MissingRunConfigValue(
132
+ f"Missing Geometry/TileId in L2T_STARS run-config: {filename}"
133
+ )
134
+ self.tile = str(runconfig["Geometry"]["TileId"])
135
+ logger.info(f"Tile: {cl.val(self.tile)}")
136
+
137
+ # Extract production details
138
+ if "ProductionDateTime" not in runconfig["JobIdentification"]:
139
+ raise MissingRunConfigValue(
140
+ f"Missing JobIdentification/ProductionDateTime in L2T_STARS run-config {filename}"
141
+ )
142
+ self.production_datetime = parser.parse(
143
+ runconfig["JobIdentification"]["ProductionDateTime"]
144
+ )
145
+ logger.info(f"Production time: {cl.time(self.production_datetime)}")
146
+
147
+ # Extract build ID
148
+ if "BuildID" not in runconfig["PrimaryExecutable"]:
149
+ raise MissingRunConfigValue(
150
+ f"Missing PrimaryExecutable/BuildID in L2T_STARS run-config {filename}"
151
+ )
152
+ self.build = str(runconfig["PrimaryExecutable"]["BuildID"])
153
+
154
+ # Extract product counter
155
+ if "ProductCounter" not in runconfig["ProductPathGroup"]:
156
+ raise MissingRunConfigValue(
157
+ f"Missing ProductPathGroup/ProductCounter in L2T_STARS run-config {filename}"
158
+ )
159
+ self.product_counter = int(runconfig["ProductPathGroup"]["ProductCounter"])
160
+
161
+ # Get UTC time from the L2T_LSTE granule itself
162
+ l2t_lste_granule_obj = L2TLSTE(self.L2T_LSTE_filename)
163
+ time_UTC = l2t_lste_granule_obj.time_UTC
164
+
165
+ # Construct the full granule ID and paths for the output product
166
+ granule_ID = (
167
+ f"ECOv003_L2T_STARS_{self.tile}_{time_UTC:%Y%m%d}_{self.build}_"
168
+ f"{self.product_counter:02d}"
169
+ )
170
+ self.granule_ID = granule_ID
171
+ self.L2T_STARS_granule_directory = join(self.output_directory, granule_ID)
172
+ self.L2T_STARS_zip_filename = f"{self.L2T_STARS_granule_directory}.zip"
173
+ self.L2T_STARS_browse_filename = (
174
+ f"{self.L2T_STARS_granule_directory}.png"
175
+ )
176
+
177
+ except MissingRunConfigValue as e:
178
+ # Re-raise specific missing value errors
179
+ raise e
180
+ except ECOSTRESSExitCodeException as e:
181
+ # Re-raise custom ECOSTRESS exit code exceptions
182
+ raise e
183
+ except Exception as e:
184
+ # Catch any other parsing errors and raise a generic UnableToParseRunConfig
185
+ logger.exception(e)
186
+ raise UnableToParseRunConfig(
187
+ f"Unable to parse run-config file: {filename}"
188
+ )