2Ctypes wrapper for LeafOptics C++ bindings.
4This module provides low-level ctypes bindings to interface with
5the native Helios LeafOptics plugin (PROSPECT model) via the C++ wrapper layer.
9from typing
import List, Tuple
11from ..plugins
import helios_lib
12from ..exceptions
import check_helios_error
16 """Opaque structure for LeafOptics C++ class"""
20from .UContextWrapper
import UContext
25LEAF_OPTICS_PROPERTIES_COUNT = 11
28LEAF_OPTICS_SPECTRAL_POINTS = 2101
34LEAF_OPTICS_NAUTO_FLOAT_COUNT = 14
38 """Automatic error checking for all leaf optics functions"""
39 check_helios_error(helios_lib.getLastErrorCode, helios_lib.getLastErrorMessage, helios_lib.clearError)
45 helios_lib.createLeafOptics.argtypes = [ctypes.POINTER(UContext)]
46 helios_lib.createLeafOptics.restype = ctypes.POINTER(ULeafOptics)
47 helios_lib.createLeafOptics.errcheck = _check_error
49 helios_lib.destroyLeafOptics.argtypes = [ctypes.POINTER(ULeafOptics)]
50 helios_lib.destroyLeafOptics.restype =
None
53 helios_lib.leafOpticsRun.argtypes = [
54 ctypes.POINTER(ULeafOptics),
55 ctypes.POINTER(ctypes.c_uint),
57 ctypes.POINTER(ctypes.c_float),
60 helios_lib.leafOpticsRun.restype =
None
61 helios_lib.leafOpticsRun.errcheck = _check_error
63 helios_lib.leafOpticsRunNoUUIDs.argtypes = [
64 ctypes.POINTER(ULeafOptics),
65 ctypes.POINTER(ctypes.c_float),
68 helios_lib.leafOpticsRunNoUUIDs.restype =
None
69 helios_lib.leafOpticsRunNoUUIDs.errcheck = _check_error
72 helios_lib.leafOpticsGetLeafSpectra.argtypes = [
73 ctypes.POINTER(ULeafOptics),
74 ctypes.POINTER(ctypes.c_float),
75 ctypes.POINTER(ctypes.c_float),
76 ctypes.POINTER(ctypes.c_float),
77 ctypes.POINTER(ctypes.c_float),
78 ctypes.POINTER(ctypes.c_uint)
80 helios_lib.leafOpticsGetLeafSpectra.restype =
None
81 helios_lib.leafOpticsGetLeafSpectra.errcheck = _check_error
84 helios_lib.leafOpticsSetProperties.argtypes = [
85 ctypes.POINTER(ULeafOptics),
86 ctypes.POINTER(ctypes.c_uint),
88 ctypes.POINTER(ctypes.c_float)
90 helios_lib.leafOpticsSetProperties.restype =
None
91 helios_lib.leafOpticsSetProperties.errcheck = _check_error
93 helios_lib.leafOpticsGetPropertiesFromSpectrum.argtypes = [
94 ctypes.POINTER(ULeafOptics),
95 ctypes.POINTER(ctypes.c_uint),
98 helios_lib.leafOpticsGetPropertiesFromSpectrum.restype =
None
99 helios_lib.leafOpticsGetPropertiesFromSpectrum.errcheck = _check_error
101 helios_lib.leafOpticsGetPropertiesFromSpectrumSingle.argtypes = [
102 ctypes.POINTER(ULeafOptics),
105 helios_lib.leafOpticsGetPropertiesFromSpectrumSingle.restype =
None
106 helios_lib.leafOpticsGetPropertiesFromSpectrumSingle.errcheck = _check_error
109 helios_lib.leafOpticsGetPropertiesFromLibrary.argtypes = [
110 ctypes.POINTER(ULeafOptics),
112 ctypes.POINTER(ctypes.c_float)
114 helios_lib.leafOpticsGetPropertiesFromLibrary.restype =
None
115 helios_lib.leafOpticsGetPropertiesFromLibrary.errcheck = _check_error
118 helios_lib.leafOpticsDisableMessages.argtypes = [ctypes.POINTER(ULeafOptics)]
119 helios_lib.leafOpticsDisableMessages.restype =
None
120 helios_lib.leafOpticsDisableMessages.errcheck = _check_error
122 helios_lib.leafOpticsEnableMessages.argtypes = [ctypes.POINTER(ULeafOptics)]
123 helios_lib.leafOpticsEnableMessages.restype =
None
124 helios_lib.leafOpticsEnableMessages.errcheck = _check_error
127 helios_lib.leafOpticsOptionalOutputPrimitiveData.argtypes = [ctypes.POINTER(ULeafOptics), ctypes.c_char_p]
128 helios_lib.leafOpticsOptionalOutputPrimitiveData.restype =
None
129 helios_lib.leafOpticsOptionalOutputPrimitiveData.errcheck = _check_error
131 _LEAFOPTICS_FUNCTIONS_AVAILABLE =
True
133except AttributeError:
134 _LEAFOPTICS_FUNCTIONS_AVAILABLE =
False
139 helios_lib.leafOpticsRunNitrogen.argtypes = [
140 ctypes.POINTER(ULeafOptics),
141 ctypes.POINTER(ctypes.c_uint),
143 ctypes.POINTER(ctypes.c_float),
146 helios_lib.leafOpticsRunNitrogen.restype =
None
147 helios_lib.leafOpticsRunNitrogen.errcheck = _check_error
149 helios_lib.leafOpticsUpdateNitrogenBasedSpectra.argtypes = [ctypes.POINTER(ULeafOptics)]
150 helios_lib.leafOpticsUpdateNitrogenBasedSpectra.restype =
None
151 helios_lib.leafOpticsUpdateNitrogenBasedSpectra.errcheck = _check_error
153 _LEAFOPTICS_NITROGEN_FUNCTIONS_AVAILABLE =
True
155except AttributeError:
156 _LEAFOPTICS_NITROGEN_FUNCTIONS_AVAILABLE =
False
158_NITROGEN_NOT_AVAILABLE_MSG = (
159 "LeafOptics nitrogen-mode functions not available in current Helios library. "
160 "Rebuild PyHelios to include them:\n"
161 " build_scripts/build_helios"
167def createLeafOptics(context: ctypes.POINTER(UContext)) -> ctypes.POINTER(ULeafOptics):
168 """Create LeafOptics instance
171 context: Pointer to the Helios Context
174 Pointer to the created LeafOptics instance
177 NotImplementedError: If LeafOptics functions not available
178 ValueError: If context is None
180 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
181 raise NotImplementedError(
182 "LeafOptics functions not available in current Helios library. "
183 "Rebuild PyHelios with 'leafoptics' enabled:\n"
184 " build_scripts/build_helios --plugins leafoptics"
188 raise ValueError(
"Context instance is None.")
190 return helios_lib.createLeafOptics(context)
194 """Destroy LeafOptics instance"""
195 if leafoptics
and _LEAFOPTICS_FUNCTIONS_AVAILABLE:
196 helios_lib.destroyLeafOptics(leafoptics)
201 properties: List[float],
203 """Run LeafOptics model and assign spectra to primitives
206 leafoptics: Pointer to LeafOptics instance
207 uuids: List of primitive UUIDs
208 properties: List of 9 floats [numberlayers, brownpigments, chlorophyllcontent,
209 carotenoidcontent, anthocyancontent, watermass, drymass, protein, carbonconstituents]
210 label: Label for the spectra (appended to "leaf_reflectivity_" and "leaf_transmissivity_")
212 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
213 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
215 raise ValueError(
"LeafOptics instance is None.")
217 raise ValueError(
"UUIDs list cannot be empty.")
218 if len(properties) != LEAF_OPTICS_PROPERTIES_COUNT:
219 raise ValueError(f
"Properties must have exactly {LEAF_OPTICS_PROPERTIES_COUNT} elements.")
221 raise ValueError(
"Label cannot be empty.")
223 uuid_array = (ctypes.c_uint * len(uuids))(*uuids)
224 properties_array = (ctypes.c_float * LEAF_OPTICS_PROPERTIES_COUNT)(*properties)
225 label_bytes = label.encode(
'utf-8')
227 helios_lib.leafOpticsRun(leafoptics, uuid_array, len(uuids), properties_array, label_bytes)
231 properties: List[float],
233 """Run LeafOptics model without assigning to primitives
236 leafoptics: Pointer to LeafOptics instance
237 properties: List of 9 floats [numberlayers, brownpigments, chlorophyllcontent,
238 carotenoidcontent, anthocyancontent, watermass, drymass, protein, carbonconstituents]
239 label: Label for the spectra
241 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
242 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
244 raise ValueError(
"LeafOptics instance is None.")
245 if len(properties) != LEAF_OPTICS_PROPERTIES_COUNT:
246 raise ValueError(f
"Properties must have exactly {LEAF_OPTICS_PROPERTIES_COUNT} elements.")
248 raise ValueError(
"Label cannot be empty.")
250 properties_array = (ctypes.c_float * LEAF_OPTICS_PROPERTIES_COUNT)(*properties)
251 label_bytes = label.encode(
'utf-8')
253 helios_lib.leafOpticsRunNoUUIDs(leafoptics, properties_array, label_bytes)
257 properties: List[float]) -> Tuple[List[float], List[float], List[float]]:
258 """Get leaf reflectance and transmittance spectra
261 leafoptics: Pointer to LeafOptics instance
262 properties: List of 9 floats [numberlayers, brownpigments, chlorophyllcontent,
263 carotenoidcontent, anthocyancontent, watermass, drymass, protein, carbonconstituents]
266 Tuple of (wavelengths, reflectivities, transmissivities) as lists of floats
267 - wavelengths: 400-2500 nm at 1nm resolution (2101 points)
268 - reflectivities: reflectance values at each wavelength
269 - transmissivities: transmittance values at each wavelength
271 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
272 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
274 raise ValueError(
"LeafOptics instance is None.")
275 if len(properties) != LEAF_OPTICS_PROPERTIES_COUNT:
276 raise ValueError(f
"Properties must have exactly {LEAF_OPTICS_PROPERTIES_COUNT} elements.")
278 properties_array = (ctypes.c_float * LEAF_OPTICS_PROPERTIES_COUNT)(*properties)
279 reflectivities = (ctypes.c_float * LEAF_OPTICS_SPECTRAL_POINTS)()
280 transmissivities = (ctypes.c_float * LEAF_OPTICS_SPECTRAL_POINTS)()
281 wavelengths = (ctypes.c_float * LEAF_OPTICS_SPECTRAL_POINTS)()
282 size = ctypes.c_uint()
284 helios_lib.leafOpticsGetLeafSpectra(
285 leafoptics, properties_array, reflectivities, transmissivities, wavelengths, ctypes.byref(size)
291 list(wavelengths[:n]),
292 list(reflectivities[:n]),
293 list(transmissivities[:n])
299 properties: List[float]) ->
None:
300 """Set leaf optical properties for primitives
303 leafoptics: Pointer to LeafOptics instance
304 uuids: List of primitive UUIDs
305 properties: List of 9 floats [numberlayers, brownpigments, chlorophyllcontent,
306 carotenoidcontent, anthocyancontent, watermass, drymass, protein, carbonconstituents]
308 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
309 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
311 raise ValueError(
"LeafOptics instance is None.")
313 raise ValueError(
"UUIDs list cannot be empty.")
314 if len(properties) != LEAF_OPTICS_PROPERTIES_COUNT:
315 raise ValueError(f
"Properties must have exactly {LEAF_OPTICS_PROPERTIES_COUNT} elements.")
317 uuid_array = (ctypes.c_uint * len(uuids))(*uuids)
318 properties_array = (ctypes.c_float * LEAF_OPTICS_PROPERTIES_COUNT)(*properties)
320 helios_lib.leafOpticsSetProperties(leafoptics, uuid_array, len(uuids), properties_array)
324 uuids: List[int]) ->
None:
325 """Get PROSPECT parameters from reflectivity spectrum for primitives
328 leafoptics: Pointer to LeafOptics instance
329 uuids: List of primitive UUIDs to query
332 Primitives without matching spectra are silently skipped
334 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
335 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
337 raise ValueError(
"LeafOptics instance is None.")
339 raise ValueError(
"UUIDs list cannot be empty.")
341 uuid_array = (ctypes.c_uint * len(uuids))(*uuids)
343 helios_lib.leafOpticsGetPropertiesFromSpectrum(leafoptics, uuid_array, len(uuids))
348 """Get PROSPECT parameters from reflectivity spectrum for a single primitive
351 leafoptics: Pointer to LeafOptics instance
352 uuid: Single primitive UUID to query
355 If no matching spectrum is found, the primitive is silently skipped
357 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
358 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
360 raise ValueError(
"LeafOptics instance is None.")
362 raise ValueError(
"UUID must be non-negative.")
364 helios_lib.leafOpticsGetPropertiesFromSpectrumSingle(leafoptics, ctypes.c_uint(uuid))
368 species: str) -> List[float]:
369 """Get leaf optical properties from the built-in species library
372 leafoptics: Pointer to LeafOptics instance
373 species: Name of the species (case-insensitive). Available species:
374 "default", "garden_lettuce", "alfalfa", "corn", "sunflower",
375 "english_walnut", "rice", "soybean", "wine_grape", "tomato",
376 "common_bean", "cowpea"
379 List of 9 floats [numberlayers, brownpigments, chlorophyllcontent,
380 carotenoidcontent, anthocyancontent, watermass, drymass, protein, carbonconstituents]
383 If species is not found, default properties are used and a warning is issued
385 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
386 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
388 raise ValueError(
"LeafOptics instance is None.")
390 raise ValueError(
"Species name cannot be empty.")
392 properties_array = (ctypes.c_float * LEAF_OPTICS_PROPERTIES_COUNT)()
393 species_bytes = species.encode(
'utf-8')
395 helios_lib.leafOpticsGetPropertiesFromLibrary(leafoptics, species_bytes, properties_array)
397 return list(properties_array)
401 """Disable command-line output messages from LeafOptics"""
402 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
403 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
405 raise ValueError(
"LeafOptics instance is None.")
407 helios_lib.leafOpticsDisableMessages(leafoptics)
411 """Enable command-line output messages from LeafOptics"""
412 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
413 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
415 raise ValueError(
"LeafOptics instance is None.")
417 helios_lib.leafOpticsEnableMessages(leafoptics)
421 """Selectively output primitive data for specific biochemical properties
423 By default, all biochemical properties are written as primitive data. Use this method
424 to select only needed properties for better performance.
427 leafoptics: LeafOptics instance pointer
428 label: Property label - one of: "chlorophyll", "carotenoid", "anthocyanin",
429 "brown", "water", "drymass", "protein", "cellulose"
432 Added in helios-core v1.3.59
434 if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
435 raise NotImplementedError(
"LeafOptics functions not available. Rebuild with leafoptics enabled.")
437 raise ValueError(
"LeafOptics instance is None.")
439 raise ValueError(
"Label cannot be empty.")
441 label_encoded = label.encode(
'utf-8')
442 helios_lib.leafOpticsOptionalOutputPrimitiveData(leafoptics, label_encoded)
448 num_bins: int) ->
None:
449 """Run nitrogen-based automatic leaf optics for leaf primitives
452 leafoptics: Pointer to LeafOptics instance
453 uuids: List of leaf primitive UUIDs (must belong to objects with "leaf_nitrogen_gN_m2" data)
454 params: List of LEAF_OPTICS_NAUTO_FLOAT_COUNT floats [f_photosynthetic, N_to_Cab_coefficient,
455 Car_to_Cab_ratio, numberlayers, anthocyancontent, brownpigments, watermass, drymass,
456 protein, carbonconstituents, V2Z, fqe, reassignment_threshold, min_reassignment_change]
457 num_bins: Number of spectrum bins
459 if not _LEAFOPTICS_NITROGEN_FUNCTIONS_AVAILABLE:
460 raise NotImplementedError(_NITROGEN_NOT_AVAILABLE_MSG)
462 raise ValueError(
"LeafOptics instance is None.")
464 raise ValueError(
"UUIDs list cannot be empty.")
465 if len(params) != LEAF_OPTICS_NAUTO_FLOAT_COUNT:
466 raise ValueError(f
"Nitrogen parameters must have exactly {LEAF_OPTICS_NAUTO_FLOAT_COUNT} elements.")
468 raise ValueError(
"num_bins must be at least 1.")
470 uuid_array = (ctypes.c_uint * len(uuids))(*uuids)
471 params_array = (ctypes.c_float * LEAF_OPTICS_NAUTO_FLOAT_COUNT)(*params)
473 helios_lib.leafOpticsRunNitrogen(leafoptics, uuid_array, len(uuids), params_array, ctypes.c_uint(num_bins))
477 """Re-read leaf nitrogen for tracked primitives and reassign spectra where it changed"""
478 if not _LEAFOPTICS_NITROGEN_FUNCTIONS_AVAILABLE:
479 raise NotImplementedError(_NITROGEN_NOT_AVAILABLE_MSG)
481 raise ValueError(
"LeafOptics instance is None.")
483 helios_lib.leafOpticsUpdateNitrogenBasedSpectra(leafoptics)
487 """Check if the LeafOptics nitrogen-mode functions are available in the current library"""
488 return _LEAFOPTICS_NITROGEN_FUNCTIONS_AVAILABLE
492 """Check if LeafOptics functions are available in the current library"""
493 return _LEAFOPTICS_FUNCTIONS_AVAILABLE
497if not _LEAFOPTICS_FUNCTIONS_AVAILABLE:
500 "Mock mode: LeafOptics not available. "
501 "This would create a plugin instance with native library."
506 "Mock mode: LeafOptics method not available. "
507 "This would execute plugin computation with native library."
511 createLeafOptics = mock_create
512 leafOpticsRun = mock_method
513 leafOpticsRunNoUUIDs = mock_method
514 leafOpticsGetLeafSpectra = mock_method
515 leafOpticsSetProperties = mock_method
516 leafOpticsGetPropertiesFromSpectrum = mock_method
517 leafOpticsGetPropertiesFromSpectrumSingle = mock_method
518 leafOpticsGetPropertiesFromLibrary = mock_method
519 leafOpticsRunNitrogen = mock_method
520 leafOpticsUpdateNitrogenBasedSpectra = mock_method
Opaque structure for LeafOptics C++ class.
mock_method(*args, **kwargs)
None leafOpticsOptionalOutputPrimitiveData(ctypes.POINTER(ULeafOptics) leafoptics, str label)
Selectively output primitive data for specific biochemical properties.
leafOpticsGetPropertiesFromLibrary
None destroyLeafOptics(ctypes.POINTER(ULeafOptics) leafoptics)
Destroy LeafOptics instance.
leafOpticsGetPropertiesFromSpectrumSingle
bool is_leafoptics_nitrogen_available()
Check if the LeafOptics nitrogen-mode functions are available in the current library.
None leafOpticsEnableMessages(ctypes.POINTER(ULeafOptics) leafoptics)
Enable command-line output messages from LeafOptics.
None leafOpticsDisableMessages(ctypes.POINTER(ULeafOptics) leafoptics)
Disable command-line output messages from LeafOptics.
_check_error(result, func, args)
Automatic error checking for all leaf optics functions.
mock_create(*args, **kwargs)
leafOpticsGetPropertiesFromSpectrum
bool is_leafoptics_available()
Check if LeafOptics functions are available in the current library.
leafOpticsUpdateNitrogenBasedSpectra