Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions FLiESANN/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
DEFAULT_WORKING_DIRECTORY = "."
DEFAULT_FLIES_INTERMEDIATE = "FLiES_intermediate"

GEOS5FP_DIRECTORY = "~/data/GEOS5FP_download"

DEFAULT_MODEL_FILENAME = join(abspath(dirname(__file__)), "FLiESANN.h5")
SPLIT_ATYPES_CTYPES = True

Expand Down
19 changes: 17 additions & 2 deletions FLiESANN/prepare_FLiES_ANN_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def prepare_FLiES_ANN_inputs(
elevation_km_flat = np.array(elevation_km).flatten()
SZA_flat = np.array(SZA).flatten()

inputs = pd.DataFrame({
inputs_dict = {
"ctype": ctype_flat,
"atype": atype_flat,
"COT": COT_flat,
Expand All @@ -34,7 +34,22 @@ def prepare_FLiES_ANN_inputs(
"albedo": albedo_flat,
"elevation_km": elevation_km_flat,
"SZA": SZA_flat
})
}

# check all values in inputs_dict are numpy arrays and throw an exception if any are not

for key, value in inputs_dict.items():
if not isinstance(value, np.ndarray):
raise TypeError(f"input {key} is not a numpy array: {type(value)}")

# check the sizes of the input arrays and throw an exception if they mis-match

input_sizes = {key: np.array(value).size for key, value in inputs_dict.items()}

if len(set(input_sizes.values())) != 1:
raise ValueError(f"FLiES input size mis-match: {input_sizes}")

inputs = pd.DataFrame(inputs_dict)

if split_atypes_ctypes:
inputs["ctype0"] = np.float32(inputs.ctype == 0)
Expand Down
48 changes: 44 additions & 4 deletions FLiESANN/process_FLiES_ANN.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Union
from time import process_time

from datetime import datetime
import numpy as np
import rasters as rt
from rasters import Raster, RasterGeometry
from geos5fp import GEOS5FP
from solar_apparent_time import solar_day_of_year_for_area, solar_hour_of_day_for_area
from sun_angles import calculate_SZA_from_DOY_and_hour
from koppengeiger import load_koppen_geiger

Expand All @@ -14,8 +15,6 @@
from .run_FLiES_ANN_inference import run_FLiES_ANN_inference

def process_FLiES_ANN(
day_of_year: Union[Raster, np.ndarray],
hour_of_day: Union[Raster, np.ndarray],
albedo: Union[Raster, np.ndarray],
COT: Union[Raster, np.ndarray] = None,
AOT: Union[Raster, np.ndarray] = None,
Expand All @@ -25,8 +24,11 @@ def process_FLiES_ANN(
SZA: Union[Raster, np.ndarray] = None,
KG_climate: Union[Raster, np.ndarray] = None,
geometry: RasterGeometry = None,
time_UTC: datetime = None,
day_of_year: Union[Raster, np.ndarray] = None,
hour_of_day: Union[Raster, np.ndarray] = None,
GEOS5FP_connection: GEOS5FP = None,
GEOS5FP_directory: str = None,
resampling: str = "cubic",
ANN_model=None,
model_filename=DEFAULT_MODEL_FILENAME,
split_atypes_ctypes=SPLIT_ATYPES_CTYPES) -> dict:
Expand Down Expand Up @@ -82,6 +84,16 @@ def process_FLiES_ANN(
if geometry is None and isinstance(albedo, Raster):
geometry = albedo.geometry

if (day_of_year is None or hour_of_day is None) and time_UTC is not None and geometry is not None:
day_of_year = solar_day_of_year_for_area(time_UTC=time_UTC, geometry=geometry)
hour_of_day = solar_hour_of_day_for_area(time_UTC=time_UTC, geometry=geometry)

if time_UTC is None and day_of_year is None and hour_of_day is None:
raise ValueError("no time given between time_UTC, day_of_year, and hour_of_day")

if GEOS5FP_connection is None:
GEOS5FP_connection = GEOS5FP(working_directory=DEFAULT_WORKING_DIRECTORY, download_directory=GEOS5FP_DIRECTORY)

## FIXME need to fetch default values for parameters: COT, AOT, vapor_gccm, ozone_cm, elevation_km, SZA, KG_climate

if SZA is None and geometry is not None:
Expand All @@ -101,6 +113,34 @@ def process_FLiES_ANN(
if KG_climate is None:
raise ValueError("Koppen Geieger climate classification or geometry must be given")

if COT is None and geometry is not None and time_UTC is not None:
COT = GEOS5FP_connection.COT(
time_UTC=time_UTC,
geometry=geometry,
resampling=resampling
)

if AOT is None and geometry is not None and time_UTC is not None:
AOT = GEOS5FP_connection.AOT(
time_UTC=time_UTC,
geometry=geometry,
resampling=resampling
)

if vapor_gccm is None and geometry is not None and time_UTC is not None:
vapor_gccm = GEOS5FP_connection.vapor_gccm(
time_UTC=time_UTC,
geometry=geometry,
resampling=resampling
)

if ozone_cm is None and geometry is not None and time_UTC is not None:
ozone_cm = GEOS5FP_connection.ozone_cm(
time_UTC=time_UTC,
geometry=geometry,
resampling=resampling
)

# Preprocess COT and determine aerosol/cloud types
COT = np.clip(COT, 0, None) # Ensure COT is non-negative
COT = rt.where(COT < 0.001, 0, COT) # Set very small COT values to 0
Expand Down
2 changes: 1 addition & 1 deletion FLiESANN/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.1
1.3.0
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = ["setuptools>=60", "setuptools-scm>=8.0", "wheel"]

[project]
name = "FLiESANN"
version = "1.2.1"
version = "1.3.0"
description = "Forest Light Environmental Simulator (FLiES) Radiative Transfer Model Artificial Neural Network (ANN) Implementation in Python"
readme = "README.md"
authors = [
Expand Down