Urban-Plumber2: UK-Kin site CLMU5 simulation

[1]:
from pyclmuapp import usp_clmu
import matplotlib.pyplot as plt
import xarray as xr
import numpy as np
[2]:
%%time
# initialize
usp = usp_clmu()

# here we use the default surface data, which is the london uk-kin data
# lat = 51.5116, lon = -0.1167
# this forcing derived from urban-plumber forcing data

usp_london = usp.run(
            case_name = "UK_Kin_default",
            SURF="surfdata.nc",
            FORCING="forcing.nc",
            RUN_STARTDATE = "2002-01-01",
            STOP_OPTION = "nyears",
            STOP_N = "12",
        )
usp_london
Copying the file forcing.nc to the /Users/user/Documents/GitHub/pyclmuapp/docs/notebooks/val/workdir/inputfolder/usp
CPU times: user 1.18 s, sys: 493 ms, total: 1.68 s
Wall time: 16min 47s
[2]:
['/Users/user/Documents/GitHub/pyclmuapp/docs/notebooks/val/workdir/outputfolder/lnd/hist/UK_Kin_default_clm0_2024-12-10_13-49-57_clm.nc']
[3]:
london_nc = usp.nc_view(usp_london[0]).sel(time=slice('2012-01-01', '2014-12-31'))
london_nc
[3]:
<xarray.Dataset> Size: 25MB
Dimensions:             (levgrnd: 25, levlak: 10, levdcmp: 1, time: 35089,
                         hist_interval: 2, lndgrid: 1, column: 6, gridcell: 1,
                         landunit: 2, pft: 6, levsoi: 20)
Coordinates:
  * levgrnd             (levgrnd) float32 100B 0.01 0.04 0.09 ... 28.87 42.0
  * levlak              (levlak) float32 40B 0.05 0.6 2.1 ... 25.6 34.33 44.78
  * levdcmp             (levdcmp) float32 4B 1.0
  * time                (time) datetime64[ns] 281kB 2012-01-01 ... 2014-01-01
Dimensions without coordinates: hist_interval, lndgrid, column, gridcell,
                                landunit, pft, levsoi
Data variables: (12/129)
    mcdate              (time) int32 140kB ...
    mcsec               (time) int32 140kB ...
    mdcur               (time) int32 140kB ...
    mscur               (time) int32 140kB ...
    nstep               (time) int32 140kB ...
    time_bounds         (time, hist_interval) datetime64[ns] 561kB ...
    ...                  ...
    URBAN_AC            (time, gridcell) float32 140kB ...
    URBAN_HEAT          (time, gridcell) float32 140kB ...
    WASTEHEAT           (time, gridcell) float32 140kB ...
    WBT                 (time, gridcell) float32 140kB ...
    Wind                (time, gridcell) float32 140kB ...
    ZWT                 (time, gridcell) float32 140kB ...
Attributes: (12/38)
    title:                                     CLM History file information
    comment:                                   NOTE: None of the variables ar...
    Conventions:                               CF-1.0
    history:                                   created on 12/10/24 13:35:15
    source:                                    Community Land Model CLM4.0
    hostname:                                  clmu-app
    ...                                        ...
    ctype_urban_shadewall:                     73
    ctype_urban_impervious_road:               74
    ctype_urban_pervious_road:                 75
    cft_c3_crop:                               1
    cft_c3_irrigated:                          2
    time_period_freq:                          minute_30

import the observation data for comparison

[4]:
ds = xr.open_dataset('/Users/user/Documents/GitHub/pyclmuapp/inputfolder/Urban-PLUMBER/datm_files/UK-Kin/CLM1PT_data/UK-KingsCollege_clean_observations_v1.nc')
ds
[4]:
<xarray.Dataset> Size: 2MB
Dimensions:    (time: 30577)
Coordinates:
  * time       (time) datetime64[ns] 245kB 2012-04-04 ... 2014-01-01
Data variables: (12/28)
    SWdown     (time) float32 122kB ...
    LWdown     (time) float32 122kB ...
    Tair       (time) float32 122kB ...
    Qair       (time) float32 122kB ...
    PSurf      (time) float32 122kB ...
    Rainf      (time) float32 122kB ...
    ...         ...
    Wind_E_qc  (time) int8 31kB ...
    SWup_qc    (time) int8 31kB ...
    LWup_qc    (time) int8 31kB ...
    Qle_qc     (time) int8 31kB ...
    Qh_qc      (time) int8 31kB ...
    Qtau_qc    (time) int8 31kB ...
Attributes: (12/24)
    title:                      Flux tower observations from UK-KingsCollege ...
    summary:                    Quality controlled flux tower observations fo...
    sitename:                   UK-KingsCollege
    long_sitename:              Kings College, London, United Kingdom
    version:                    v1
    keywords:                   urban, flux tower, eddy covariance, observations
    ...                         ...
    observations_contact:       Simone Kotthaus (kotthaus@ipsl.polytechnique....
    observations_reference:     Bjorkegren et al. (2015): https://doi.org/10....
    date_created:               2022-09-22 16:27:09
    source:                     https://github.com/matlipson/urban-plumber_pi...
    comment:                    Observations from KSSW tower
    history:                    v0.9 (2021-09-08): beta issue; v1 (2022-09-15...
[5]:
#round the time to the nearest minute
london_nc['time'] = london_nc['time'].dt.round('min')
# shift the time by 30 minutes to match the observations
london_nc['time'] = london_nc['time'] - np.timedelta64(1800, 's')
df = london_nc[['Qle','Qh','Qtau','SWup','LWup']].to_dataframe()
df_ds = ds[['Qle','Qh','Qtau','SWup','LWup']].to_dataframe()
df['Qtau'] = -df['Qtau']
df = df.merge(df_ds, on='time', suffixes=('_usp', '_obs'))
df.head(1)
[5]:
Qle_usp Qh_usp Qtau_usp SWup_usp LWup_usp Qle_obs Qh_obs Qtau_obs SWup_obs LWup_obs
time
2012-04-04 31.69558 16.175985 0.030384 0.0 353.656372 NaN NaN NaN NaN NaN
[6]:
%%time
# modify the surface

# tree_area_fraction=0.03
# grass_area_fraction=0.04
# bare_soil_area_fraction=0.0
# water_area_fraction=0.14
# impervious_area_fraction=0.79
# roof fraction = 0.4
# raod fraction = 0.39
# topsoil_clay_fraction=0.26
# topsoil_sand_fraction=0.45
# PCT_CLAY = 0.26
# PCT_SAND = 0.45
action = {

    #"ALB_ROOF_DIR": 0.109,
    #"ALB_ROOF_DIF": 0.109,
    "CANYON_HWR": 1.13,
    "HT_ROOF": 21.3,
    "WTLUNIT_ROOF": 0.4/(0.03+0.04+0.79),
    "WTROAD_PERV": 1 - 0.39/(0.03+0.04+0.39),
    #"ALB_IMPROAD_DIR": 0.109,
    #"ALB_IMPROAD_DIF": 0.109,
    #"ALB_PERROAD_DIR": 0.109,
    #"ALB_PERROAD_DIF": 0.109,
    #"WIND_HGT_CANYON": 50
}
usp.modify_surf(action=action, surfata_name="surface_modfied_UK_Kin.nc", mode="replace", urban_type=2)
CPU times: user 229 ms, sys: 82.7 ms, total: 311 ms
Wall time: 491 ms
[7]:
usp_london_detail = usp.run(
            case_name = "UK_Kin_detail",
            RUN_STARTDATE = "2002-01-01",
            STOP_OPTION = "nyears",
            STOP_N = "12",
            RUN_TYPE= "coldstart",
        )
usp_london_detail
[7]:
['/Users/user/Documents/GitHub/pyclmuapp/docs/notebooks/val/workdir/outputfolder/lnd/hist/UK_Kin_detail_clm0_2024-12-10_14-06-56_clm.nc']
[8]:
ludon_detail_nc = usp.nc_view(usp_london_detail[0]).sel(time=slice('2012-01-01', '2014-12-31'))
ludon_detail_nc['time'] = ludon_detail_nc['time'].dt.round('min')
ludon_detail_nc['time'] = ludon_detail_nc['time'] - np.timedelta64(1800, 's')
df_ds = ds[['Qle','Qh','Qtau','SWup','LWup']].to_dataframe()
df_detail = ludon_detail_nc[['Qle','Qh','Qtau','SWup','LWup']].to_dataframe()
df_detail['Qtau'] = -df_detail['Qtau']
df_detail = df_detail.merge(df_ds, on='time', suffixes=('_usp', '_obs'))
df_detail.head(1)
[8]:
Qle_usp Qh_usp Qtau_usp SWup_usp LWup_usp Qle_obs Qh_obs Qtau_obs SWup_obs LWup_obs
time
2012-04-04 35.300236 16.307856 0.029534 0.0 352.476349 NaN NaN NaN NaN NaN
[9]:
from sklearn.metrics import mean_squared_error, mean_absolute_error
import seaborn as sns
import warnings
from datetime import datetime
from scipy.stats import pearsonr
warnings.filterwarnings('ignore')

def plotting(df, save_path):
    var_list = ['SWup','LWup','Qle','Qh','Qtau']
    unit = ['W/m$\mathrm{^{2}}$', 'W/m$\mathrm{^{2}}$', 'W/m$\mathrm{^{2}}$', 'W/m$\mathrm{^{2}}$', 'N/m$\mathrm{^{2}}$']
    lim = [[-10, 130], [280, 600], [-120, 350], [-150, 500], [-0.1, 1.3]]


    for var in var_list:
        fig = plt.figure(figsize=(12, 5))

        df_plot = df[[f'{var}_usp', f'{var}_obs']].dropna()
        df_mean = df_plot[[f'{var}_usp', f'{var}_obs']].groupby(df_plot.index.hour).mean()
        df_std = df_plot[[f'{var}_usp', f'{var}_obs']].groupby(df_plot.index.hour).std()

        mae = mean_absolute_error(df_plot[f'{var}_obs'],df_plot[f'{var}_usp'])
                            #mean_absolute_percentage_error(df_plot[f'{var}_usp'], df_plot[f'{var}_obs'])#mean_squared_error(df_plot[f'{var}_usp'], df_plot[f'{var}_obs'])
        R, _ = pearsonr(df_plot[f'{var}_usp'], df_plot[f'{var}_obs'])

        ax = fig.add_subplot(1, 2, 1)

        if var == 'SWup':
            for h in range(0,24):
                if h not in df_mean.index:
                    df_mean.loc[h] = [0,0]
                    df_std.loc[h] = [0,0]
        df_mean = df_mean.sort_index()
        df_std = df_std.sort_index()

        ax.plot(df_mean.index, df_mean[f'{var}_usp'], color="#E02927", label='Simulated')
        ax.fill_between(df_mean.index, df_mean[f'{var}_usp']-df_std[f'{var}_usp'], df_mean[f'{var}_usp']+df_std[f'{var}_usp'], color="#E02927", alpha=0.2)
        ax.plot(df_mean.index, df_mean[f'{var}_obs'], color="#3964DF", label='Observed')
        ax.fill_between(df_mean.index, df_mean[f'{var}_obs']-df_std[f'{var}_obs'], df_mean[f'{var}_obs']+df_std[f'{var}_obs'], color="#3964DF", alpha=0.2)
        ax.set_ylabel(f'{var} [{unit[var_list.index(var)]}]', fontsize=14)
        ax.set_xlabel('Local hour of day', fontsize=14)
        ax.set_title(f'MAE: {mae:.2f}, R: {R:.2f}', fontsize=14, loc='left')
        ax.tick_params(axis='both', which='major', labelsize=12)
        ax.legend(frameon=False, fontsize=14)

        ax = fig.add_subplot(1, 2, 2)
        h = sns.histplot(
            x=f'{var}_usp',
            y=f'{var}_obs',
            data=df_plot,
            ax=ax,
            cmap='rainbow',
            bins=120,
            #cbar=True,
            #cbar_kws={'label': 'Counts'},
        )
        cbar = h.figure.colorbar(h.collections[0], ax=ax)
        cbar.set_label('Counts', fontsize=14)
        cbar.ax.tick_params(labelsize=12)
        ax.plot(lim[var_list.index(var)], lim[var_list.index(var)], color='black', linestyle='--')
        ax.tick_params(axis='both', which='major', labelsize=12)
        ax.set_xlabel(f'Simulated {var} [{unit[var_list.index(var)]}]', fontsize=14)
        ax.set_ylabel(f'Observed {var} [{unit[var_list.index(var)]}]',  fontsize=14)
        ax.set_xlim(lim[var_list.index(var)])
        ax.set_ylim(lim[var_list.index(var)])
        print(f'{var} MAE: {mae:.2f}')

        fig.tight_layout()
        fig.savefig(save_path + f'_{var}.pdf', dpi=300, bbox_inches='tight')
        plt.show()
[10]:
plotting(df, 'figs/CLMU5_UK-Kin_default')
plotting(df_detail, 'figs/CLMU5_UK-Kin_detail')
SWup MAE: 4.69
../../_images/notebooks_val_urban_plumber_usp_11_1.png
LWup MAE: 5.50
../../_images/notebooks_val_urban_plumber_usp_11_3.png
Qle MAE: 23.39
../../_images/notebooks_val_urban_plumber_usp_11_5.png
Qh MAE: 52.93
../../_images/notebooks_val_urban_plumber_usp_11_7.png
Qtau MAE: 0.16
../../_images/notebooks_val_urban_plumber_usp_11_9.png
SWup MAE: 7.48
../../_images/notebooks_val_urban_plumber_usp_11_11.png
LWup MAE: 7.79
../../_images/notebooks_val_urban_plumber_usp_11_13.png
Qle MAE: 18.42
../../_images/notebooks_val_urban_plumber_usp_11_15.png
Qh MAE: 50.15
../../_images/notebooks_val_urban_plumber_usp_11_17.png
Qtau MAE: 0.15
../../_images/notebooks_val_urban_plumber_usp_11_19.png