Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write L2 to file and leave all additional variable derivation for the L2toL3 step #255

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
64b6aa2
Revert "revert"
BaptisteVandecrux Jun 4, 2024
df5a9c5
correcting typo
BaptisteVandecrux Jun 4, 2024
8b4d430
aws process handling modified
PennyHow Jun 7, 2024
f8de75c
Level 2 processing capabilities added to get_l3
PennyHow Jun 7, 2024
17245f7
get_l3 renamed to l2_to_l3
PennyHow Jun 7, 2024
cc6bfb2
l2_to_l3 functionality from pypromice
PennyHow Jun 7, 2024
09f12a0
minor de-bug for process test to run
PennyHow Jun 7, 2024
17b647c
Update .gitignore
BaptisteVandecrux Jun 10, 2024
5927dbb
L2 split from L3 CLI processing
PennyHow Jun 10, 2024
b9a2e70
unit tests moved to separate module
PennyHow Jun 10, 2024
bfb1603
file writing functions moved to separate module
PennyHow Jun 10, 2024
eb58371
Loading functions moved to separate module
PennyHow Jun 10, 2024
f536e74
Handling and reformating functions moved
PennyHow Jun 10, 2024
9b2d8f4
resampling functions moved
PennyHow Jun 10, 2024
2e98bfd
aws module updated with structure changes
PennyHow Jun 10, 2024
526f0f0
Logging package typo
PennyHow Jun 10, 2024
de7e5d8
de-bugging
PennyHow Jun 10, 2024
e9bcc8c
get_l2 and l2_to_l3 process test added
PennyHow Jun 10, 2024
496ef03
data prep and write function moved out of AWS class
PennyHow Jun 10, 2024
facf0ee
actions de-bug
PennyHow Jun 10, 2024
bd8d8a5
stations for testing changed
PennyHow Jun 10, 2024
679aa4f
l2 action de-bug
PennyHow Jun 10, 2024
e5db5d6
loop de-bug in l2 test action
PennyHow Jun 10, 2024
a6911f0
out filepath changed
PennyHow Jun 10, 2024
ba1a424
Path alteration 2
PennyHow Jun 10, 2024
0ae13cd
poutput directory alteration 3
PennyHow Jun 10, 2024
6a9b66b
action stages merged
PennyHow Jun 10, 2024
eaa49fd
get_l2tol3 action test skipped
PennyHow Jun 10, 2024
e0d93b5
typo fix in join_levels.py
BaptisteVandecrux Jun 11, 2024
a6e8807
further debugging
BaptisteVandecrux Jun 11, 2024
1a406f4
creating folder before writing files, writing hourly daily monthly fi…
BaptisteVandecrux Jun 11, 2024
697fa60
typo fix
BaptisteVandecrux Jun 12, 2024
b9fb365
fixed daily output file name
BaptisteVandecrux Jun 12, 2024
9d0cb14
resampling frequency specified
PennyHow Jun 12, 2024
59cfbf0
modified file filter added
PennyHow Jun 12, 2024
fdb6b02
reverts changes on get_l2.py to write to file both 10min and hourly data
BaptisteVandecrux Jun 12, 2024
3bd0435
renamed join_levels to join_l2 because join_l3 will have different me…
BaptisteVandecrux Jun 12, 2024
2cebff4
skipping resample after join_l2, fixed setup.py for join_l2
BaptisteVandecrux Jun 12, 2024
cb1f6a3
fixing test
BaptisteVandecrux Jun 13, 2024
d1d46b7
fixed function names
BaptisteVandecrux Jun 13, 2024
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
52 changes: 52 additions & 0 deletions .github/workflows/process_l2_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
on:
pull_request:
types: [opened, reopened, synchronize, edited]
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
name: process_l2_test
steps:
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: "3.8"
- name: Checkout repo
uses: actions/checkout@v3
with:
path: "main"
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
shell: bash
run: |
python -m pip install --upgrade pip
pip install wheel
python3 -m pip install --upgrade setuptools
cd $GITHUB_WORKSPACE/main
pip install .
- name: Clone AWS Level 0 data repo for testing
env:
GITLAB_TOKEN : ${{ secrets.GITLAB_TOKEN }}
run: |
cd $GITHUB_WORKSPACE
git clone --depth 1 https://oauth2:${{ env.GITLAB_TOKEN }}@geusgitlab.geus.dk/glaciology-and-climate/promice/aws-l0.git
- name: Run L0 to L3 processing
env:
TEST_STATION: KAN_U HUM
shell: bash
run: |
mkdir $GITHUB_WORKSPACE/out/
mkdir $GITHUB_WORKSPACE/out/L2/
for i in $(echo ${{ env.TEST_STATION }} | tr ' ' '\n'); do
python3 $GITHUB_WORKSPACE/main/src/pypromice/process/get_l2.py -c $GITHUB_WORKSPACE/aws-l0/tx/config/$i.toml -i $GITHUB_WORKSPACE/aws-l0/tx -o $GITHUB_WORKSPACE/out/L2/
done
# mkdir $GITHUB_WORKSPACE/out/L3/
# for i in $(echo ${{ env.TEST_STATION }} | tr ' ' '\n'); do
# python3 $GITHUB_WORKSPACE/main/src/pypromice/process/get_l2tol3.py -i $GITHUB_WORKSPACE/out/L2/$i/$i_hour.nc -o $GITHUB_WORKSPACE/out/ -t 60min
# done
- name: Upload test output
uses: actions/upload-artifact@v3
with:
name: result
path: out
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
name: process_test
name: process_l3_test
steps:
- name: Install Python
uses: actions/setup-python@v4
Expand All @@ -33,12 +33,12 @@ jobs:
git clone --depth 1 https://oauth2:${{ env.GITLAB_TOKEN }}@geusgitlab.geus.dk/glaciology-and-climate/promice/aws-l0.git
- name: Run data processing
env:
TEST_STATION: KPC_U CEN2 JAR
TEST_STATION: KPC_U CEN2
shell: bash
run: |
mkdir $GITHUB_WORKSPACE/out/
for i in $(echo ${{ env.TEST_STATION }} | tr ' ' '\n'); do
python3 $GITHUB_WORKSPACE/main/src/pypromice/process/get_l3.py -v $GITHUB_WORKSPACE/main/src/pypromice/process/variables.csv -m $GITHUB_WORKSPACE/main/src/pypromice/process/metadata.csv -c $GITHUB_WORKSPACE/aws-l0/raw/config/$i.toml -i $GITHUB_WORKSPACE/aws-l0/raw -o $GITHUB_WORKSPACE/out/
python3 $GITHUB_WORKSPACE/main/src/pypromice/process/get_l3.py -c $GITHUB_WORKSPACE/aws-l0/raw/config/$i.toml -i $GITHUB_WORKSPACE/aws-l0/raw -o $GITHUB_WORKSPACE/out/
done
- name: Upload test output
uses: actions/upload-artifact@v3
Expand Down
8 changes: 5 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="pypromice",
version="1.3.6",
version="1.4.0",
author="GEUS Glaciology and Climate",
description="PROMICE/GC-Net data processing toolbox",
long_description=long_description,
Expand Down Expand Up @@ -36,14 +36,16 @@
"pypromice.qc.percentiles": ["thresholds.csv"],
"pypromice.postprocess": ["station_configurations.toml", "positions_seed.csv"],
},
install_requires=['numpy>=1.23.0', 'pandas>=1.5.0', 'xarray>=2022.6.0', 'toml', 'scipy>=1.9.0', 'Bottleneck', 'netcdf4', 'pyDataverse', 'eccodes','scikit-learn>=1.1.0'],
install_requires=['numpy>=1.23.0', 'pandas>=1.5.0', 'xarray>=2022.6.0', 'toml', 'scipy>=1.9.0', 'Bottleneck', 'netcdf4', 'pyDataverse==0.3.1', 'eccodes', 'scikit-learn>=1.1.0'],
# extras_require={'postprocess': ['eccodes','scikit-learn>=1.1.0']},
entry_points={
'console_scripts': [
'get_promice_data = pypromice.get.get_promice_data:get_promice_data',
'get_l0tx = pypromice.tx.get_l0tx:get_l0tx',
'join_l2 = pypromice.process.join_l2:join_l2',
'get_l2 = pypromice.process.get_l2:get_l2',
'get_l3 = pypromice.process.get_l3:get_l3',
'join_l3 = pypromice.process.join_l3:join_l3',
BaptisteVandecrux marked this conversation as resolved.
Show resolved Hide resolved
'get_l2tol3 = pypromice.process.get_l2tol3:get_l2tol3',
'get_watsontx = pypromice.tx.get_watsontx:get_watsontx',
'get_bufr = pypromice.postprocess.get_bufr:main',
'get_msg = pypromice.tx.get_msg:get_msg'
Expand Down
67 changes: 59 additions & 8 deletions src/pypromice/process/L1toL2.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,18 @@ def toL2(
eps_clear=9.36508e-6,
emissivity=0.97,
) -> xr.Dataset:
'''Process one Level 1 (L1) product to Level 2
'''Process one Level 1 (L1) product to Level 2.
In this step we do:
- manual flagging and adjustments
- automated QC: persistence, percentile
- custom filter: gps_alt filter, NaN t_rad removed from dlr & ulr
- smoothing of tilt and rot
- calculation of rh with regards to ice in subfreezin conditions
- calculation of cloud coverage
- correction of dsr and usr for tilt
- filtering of dsr based on a theoritical TOA irradiance and grazing light
- calculation of albedo
- calculation of directional wind speed

Parameters
----------
Expand Down Expand Up @@ -85,10 +96,20 @@ def toL2(
ds['dlr'] = ds.dlr.where(ds.t_rad.notnull())
ds['ulr'] = ds.ulr.where(ds.t_rad.notnull())

# calculating realtive humidity with regard to ice
T_100 = _getTempK(T_0)
ds['rh_u_cor'] = correctHumidity(ds['rh_u'], ds['t_u'],
T_0, T_100, ews, ei0)

if ds.attrs['number_of_booms']==2:
ds['rh_l_cor'] = correctHumidity(ds['rh_l'], ds['t_l'],
T_0, T_100, ews, ei0)

if hasattr(ds,'t_i'):
if ~ds['t_i'].isnull().all():
ds['rh_i_cor'] = correctHumidity(ds['rh_i'], ds['t_i'],
T_0, T_100, ews, ei0)

# Determiune cloud cover for on-ice stations
cc = calcCloudCoverage(ds['t_u'], T_0, eps_overcast, eps_clear, # Calculate cloud coverage
ds['dlr'], ds.attrs['station_id'])
Expand Down Expand Up @@ -176,22 +197,52 @@ def toL2(
ds['precip_u_cor'], ds['precip_u_rate'] = correctPrecip(ds['precip_u'],
ds['wspd_u'])
if ds.attrs['number_of_booms']==2:
ds['rh_l_cor'] = correctHumidity(ds['rh_l'], ds['t_l'], # Correct relative humidity
T_0, T_100, ews, ei0)

if ~ds['precip_l'].isnull().all() and precip_flag: # Correct precipitation
ds['precip_l_cor'], ds['precip_l_rate']= correctPrecip(ds['precip_l'],
ds['wspd_l'])

if hasattr(ds,'t_i'):
if ~ds['t_i'].isnull().all(): # Instantaneous msg processing
ds['rh_i_cor'] = correctHumidity(ds['rh_i'], ds['t_i'], # Correct relative humidity
T_0, T_100, ews, ei0)
# Get directional wind speed
ds['wdir_u'] = ds['wdir_u'].where(ds['wspd_u'] != 0)
ds['wspd_x_u'], ds['wspd_y_u'] = calcDirWindSpeeds(ds['wspd_u'], ds['wdir_u'])

if ds.attrs['number_of_booms']==2:
ds['wdir_l'] = ds['wdir_l'].where(ds['wspd_l'] != 0)
ds['wspd_x_l'], ds['wspd_y_l'] = calcDirWindSpeeds(ds['wspd_l'], ds['wdir_l'])

if hasattr(ds, 'wdir_i'):
if ~ds['wdir_i'].isnull().all() and ~ds['wspd_i'].isnull().all():
ds['wdir_i'] = ds['wdir_i'].where(ds['wspd_i'] != 0)
ds['wspd_x_i'], ds['wspd_y_i'] = calcDirWindSpeeds(ds['wspd_i'], ds['wdir_i'])


ds = clip_values(ds, vars_df)
return ds


def calcDirWindSpeeds(wspd, wdir, deg2rad=np.pi/180):
'''Calculate directional wind speed from wind speed and direction

Parameters
----------
wspd : xr.Dataarray
Wind speed data array
wdir : xr.Dataarray
Wind direction data array
deg2rad : float
Degree to radians coefficient. The default is np.pi/180

Returns
-------
wspd_x : xr.Dataarray
Wind speed in X direction
wspd_y : xr.Datarray
Wind speed in Y direction
'''
wspd_x = wspd * np.sin(wdir * deg2rad)
wspd_y = wspd * np.cos(wdir * deg2rad)
return wspd_x, wspd_y


def calcCloudCoverage(T, T_0, eps_overcast, eps_clear, dlr, station_id):
'''Calculate cloud cover from T and T_0

Expand Down
40 changes: 4 additions & 36 deletions src/pypromice/process/L2toL3.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

def toL3(L2, T_0=273.15, z_0=0.001, R_d=287.05, eps=0.622, es_0=6.1071,
es_100=1013.246):
'''Process one Level 2 (L2) product to Level 3 (L3)
'''Process one Level 2 (L2) product to Level 3 (L3) meaning calculating all
derived variables:
- Sensible fluxes


Parameters
----------
Expand All @@ -32,9 +35,6 @@ def toL3(L2, T_0=273.15, z_0=0.001, R_d=287.05, eps=0.622, es_0=6.1071,

T_100 = _getTempK(T_0) # Get steam point temperature as K

ds['wdir_u'] = ds['wdir_u'].where(ds['wspd_u'] != 0) # Get directional wind speed
ds['wspd_x_u'], ds['wspd_y_u'] = calcDirWindSpeeds(ds['wspd_u'], ds['wdir_u'])

# Upper boom bulk calculation
T_h_u = ds['t_u'].copy() # Copy for processing
p_h_u = ds['p_u'].copy()
Expand Down Expand Up @@ -85,41 +85,9 @@ def toL3(L2, T_0=273.15, z_0=0.001, R_d=287.05, eps=0.622, es_0=6.1071,
q_h_l = cleanSpHumid(q_h_l, T_h_l, Tsurf_h, p_h_l, RH_cor_h_l) # Clean sp.humid values
ds['qh_l'] = (('time'), q_h_l.data)

ds['wdir_l'] = ds['wdir_l'].where(ds['wspd_l'] != 0) # Get directional wind speed
ds['wspd_x_l'], ds['wspd_y_l'] = calcDirWindSpeeds(ds['wspd_l'], ds['wdir_l'])

if hasattr(ds, 'wdir_i'):
if ~ds['wdir_i'].isnull().all() and ~ds['wspd_i'].isnull().all(): # Instantaneous msg processing
ds['wdir_i'] = ds['wdir_i'].where(ds['wspd_i'] != 0) # Get directional wind speed
ds['wspd_x_i'], ds['wspd_y_i'] = calcDirWindSpeeds(ds['wspd_i'], ds['wdir_i'])

return ds


def calcDirWindSpeeds(wspd, wdir, deg2rad=np.pi/180):
'''Calculate directional wind speed from wind speed and direction

Parameters
----------
wspd : xr.Dataarray
Wind speed data array
wdir : xr.Dataarray
Wind direction data array
deg2rad : float
Degree to radians coefficient. The default is np.pi/180

Returns
-------
wspd_x : xr.Dataarray
Wind speed in X direction
wspd_y : xr.Datarray
Wind speed in Y direction
'''
wspd_x = wspd * np.sin(wdir * deg2rad)
wspd_y = wspd * np.cos(wdir * deg2rad)
return wspd_x, wspd_y


def calcHeatFlux(T_0, T_h, Tsurf_h, rho_atm, WS_h, z_WS, z_T, nu, q_h, p_h,
kappa=0.4, WS_lim=1., z_0=0.001, g=9.82, es_0=6.1071, eps=0.622,
gamma=16., L_sub=2.83e6, L_dif_max=0.01, c_pd=1005., aa=0.7,
Expand Down
Loading
Loading