Skip to content

Commit 4e753aa

Browse files
authored
Merge pull request #3264 from samsrabin/plumber2_surf_wrapper-fixes
Fix plumber2_surf_wrapper
2 parents 426e977 + c325805 commit 4e753aa

15 files changed

+401
-129
lines changed

.git-blame-ignore-revs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,4 @@ cdf40d265cc82775607a1bf25f5f527bacc97405
6868
3dd489af7ebe06566e2c6a1c7ade18550f1eb4ba
6969
742cfa606039ab89602fde5fef46458516f56fd4
7070
4ad46f46de7dde753b4653c15f05326f55116b73
71+
75db098206b064b8b7b2a0604d3f0bf8fdb950cc

python/ctsm/pft_utils.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
Constants and functions relating to PFTs
3+
"""
4+
5+
MIN_PFT = 0 # bare ground
6+
MIN_NAT_PFT = 1 # minimum natural pft (not including bare ground)
7+
MAX_NAT_PFT = 14 # maximum natural pft
8+
MAX_PFT_GENERICCROPS = 16 # for runs with generic crops
9+
MAX_PFT_MANAGEDCROPS = 78 # for runs with explicit crops
10+
11+
12+
def is_valid_pft(pft_num, managed_crops):
13+
"""
14+
Given a number, check whether it represents a valid PFT (bare ground OK)
15+
"""
16+
if managed_crops:
17+
max_allowed_pft = MAX_PFT_MANAGEDCROPS
18+
else:
19+
max_allowed_pft = MAX_PFT_GENERICCROPS
20+
21+
return MIN_PFT <= pft_num <= max_allowed_pft
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
Things shared between plumber2 scripts
3+
"""
4+
5+
import os
6+
import pandas as pd
7+
from ctsm.path_utils import path_to_ctsm_root
8+
9+
PLUMBER2_SITES_CSV = os.path.join(
10+
path_to_ctsm_root(),
11+
"tools",
12+
"site_and_regional",
13+
"PLUMBER2_sites.csv",
14+
)
15+
16+
17+
def read_plumber2_sites_csv(file=PLUMBER2_SITES_CSV):
18+
"""
19+
Read PLUMBER2_sites.csv using pandas
20+
"""
21+
return pd.read_csv(file, skiprows=5)

python/ctsm/site_and_regional/plumber2_surf_wrapper.py

Lines changed: 100 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,18 @@
2222

2323
import argparse
2424
import logging
25-
import os
26-
import subprocess
25+
import sys
2726
import tqdm
2827

29-
import pandas as pd
28+
# pylint:disable=wrong-import-position
29+
from ctsm.site_and_regional.plumber2_shared import PLUMBER2_SITES_CSV, read_plumber2_sites_csv
30+
from ctsm import subset_data
31+
from ctsm.pft_utils import MAX_PFT_MANAGEDCROPS, is_valid_pft
3032

3133

32-
def get_parser():
34+
def get_args():
3335
"""
34-
Get parser object for this script.
36+
Get arguments for this script.
3537
"""
3638
parser = argparse.ArgumentParser(
3739
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
@@ -45,137 +47,148 @@ def get_parser():
4547
help="Verbose mode will print more information. ",
4648
action="store_true",
4749
dest="verbose",
48-
default=False,
4950
)
5051

5152
parser.add_argument(
52-
"--16pft",
53-
help="Create and/or modify 16-PFT surface datasets (e.g. for a FATES run) ",
53+
"--crop",
54+
help=f"Create and/or modify {MAX_PFT_MANAGEDCROPS}-PFT "
55+
"surface datasets (e.g. for a non-FATES run)",
5456
action="store_true",
55-
dest="pft_16",
56-
default=True,
57+
dest="use_managed_crops",
5758
)
5859

59-
return parser
60+
parser.add_argument(
61+
"--overwrite",
62+
help="Overwrite any existing files",
63+
action="store_true",
64+
)
65+
66+
parser.add_argument(
67+
"--plumber2-sites-csv",
68+
help=f"Comma-separated value (CSV) file with Plumber2 sites. Default: {PLUMBER2_SITES_CSV}",
69+
default=PLUMBER2_SITES_CSV,
70+
)
71+
72+
return parser.parse_args()
6073

6174

6275
def execute(command):
6376
"""
64-
Function for running a command on shell.
77+
Runs subset_data with given arguments.
6578
Args:
66-
command (str):
67-
command that we want to run.
79+
command (list):
80+
list of args for command that we want to run.
6881
Raises:
69-
Error with the return code from shell.
82+
Whatever error subset_data gives, if any.
7083
"""
7184
print("\n", " >> ", *command, "\n")
7285

73-
try:
74-
subprocess.check_call(command, stdout=open(os.devnull, "w"), stderr=subprocess.STDOUT)
75-
76-
except subprocess.CalledProcessError as err:
77-
# raise RuntimeError("command '{}' return with error
78-
# (code {}): {}".format(e.cmd, e.returncode, e.output))
79-
# print (e.ouput)
80-
print(err)
86+
sys.argv = command
87+
subset_data.main()
8188

8289

8390
def main():
8491
"""
8592
Read plumber2_sites from csv, iterate through sites, and add dominant PFT
8693
"""
8794

88-
args = get_parser().parse_args()
95+
args = get_args()
8996

9097
if args.verbose:
9198
logging.basicConfig(level=logging.DEBUG)
9299

93-
plumber2_sites = pd.read_csv("PLUMBER2_sites.csv", skiprows=4)
100+
plumber2_sites = read_plumber2_sites_csv(args.plumber2_sites_csv)
94101

95102
for _, row in tqdm.tqdm(plumber2_sites.iterrows()):
96103
lat = row["Lat"]
97104
lon = row["Lon"]
98105
site = row["Site"]
106+
107+
clmsite = "1x1_PLUMBER2_" + site
108+
print("Now processing site :", site)
109+
110+
# Set up part of subset_data command that is shared among all options
111+
subset_command = [
112+
"./subset_data",
113+
"point",
114+
"--lat",
115+
str(lat),
116+
"--lon",
117+
str(lon),
118+
"--site",
119+
clmsite,
120+
"--create-surface",
121+
"--uniform-snowpack",
122+
"--cap-saturation",
123+
"--lon-type",
124+
"180",
125+
]
126+
127+
# Read info for first PFT
99128
pft1 = row["pft1"]
129+
if not is_valid_pft(pft1, args.use_managed_crops):
130+
raise RuntimeError(f"pft1 must be a valid PFT; got {pft1}")
100131
pctpft1 = row["pft1-%"]
101132
cth1 = row["pft1-cth"]
102133
cbh1 = row["pft1-cbh"]
103-
pft2 = row["pft2"]
104-
pctpft2 = row["pft2-%"]
105-
cth2 = row["pft2-cth"]
106-
cbh2 = row["pft2-cbh"]
107-
# overwrite missing values from .csv file
108-
if pft1 == -999:
109-
pft1 = 0
110-
pctpft1 = 0
111-
cth1 = 0
112-
cbh1 = 0
113-
if pft2 == -999:
114-
pft2 = 0
115-
pctpft2 = 0
116-
cth2 = 0
117-
cbh2 = 0
118-
clmsite = "1x1_PLUMBER2_" + site
119-
print("Now processing site :", site)
120134

121-
if args.pft_16:
122-
# use surface dataset with 16 pfts, but overwrite to 100% 1 dominant PFT
123-
# don't set crop flag
124-
# set dominant pft
125-
subset_command = [
126-
"./subset_data",
127-
"point",
128-
"--lat",
129-
str(lat),
130-
"--lon",
131-
str(lon),
132-
"--site",
133-
clmsite,
135+
# Read info for second PFT, if a valid one is given in the .csv file
136+
pft2 = row["pft2"]
137+
if is_valid_pft(pft2, args.use_managed_crops):
138+
pctpft2 = row["pft2-%"]
139+
cth2 = row["pft2-cth"]
140+
cbh2 = row["pft2-cbh"]
141+
142+
# Set dominant PFT(s)
143+
if is_valid_pft(pft2, args.use_managed_crops):
144+
subset_command += [
134145
"--dompft",
135146
str(pft1),
136147
str(pft2),
137148
"--pctpft",
138149
str(pctpft1),
139150
str(pctpft2),
140-
"--cth",
141-
str(cth1),
142-
str(cth2),
143-
"--cbh",
144-
str(cbh1),
145-
str(cbh2),
146-
"--create-surface",
147-
"--uniform-snowpack",
148-
"--cap-saturation",
149-
"--verbose",
150-
"--overwrite",
151151
]
152152
else:
153-
# use surface dataset with 78 pfts, and overwrite to 100% 1 dominant PFT
154-
# NOTE: FATES will currently not run with a 78-PFT surface dataset
155-
# set crop flag
156-
# set dominant pft
157-
subset_command = [
158-
"./subset_data",
159-
"point",
160-
"--lat",
161-
str(lat),
162-
"--lon",
163-
str(lon),
164-
"--site",
165-
clmsite,
166-
"--crop",
153+
subset_command += [
167154
"--dompft",
168155
str(pft1),
169-
str(pft2),
170156
"--pctpft",
171157
str(pctpft1),
172-
str(pctpft2),
173-
"--create-surface",
174-
"--uniform-snowpack",
175-
"--cap-saturation",
176-
"--verbose",
177-
"--overwrite",
178158
]
159+
160+
if not args.use_managed_crops:
161+
# use surface dataset with 78 pfts, but overwrite to 100% 1 dominant PFT
162+
# don't set crop flag
163+
# set canopy top and bottom heights
164+
if is_valid_pft(pft2, args.use_managed_crops):
165+
subset_command += [
166+
"--cth",
167+
str(cth1),
168+
str(cth2),
169+
"--cbh",
170+
str(cbh1),
171+
str(cbh2),
172+
]
173+
else:
174+
subset_command += [
175+
"--cth",
176+
str(cth1),
177+
"--cbh",
178+
str(cbh1),
179+
]
180+
else:
181+
# use surface dataset with 78 pfts, and overwrite to 100% 1 dominant PFT
182+
# NOTE: FATES will currently not run with a 78-PFT surface dataset
183+
# set crop flag
184+
subset_command += ["--crop"]
185+
# don't set canopy top and bottom heights
186+
187+
if args.verbose:
188+
subset_command += ["--verbose"]
189+
if args.overwrite:
190+
subset_command += ["--overwrite"]
191+
179192
execute(subset_command)
180193

181194

python/ctsm/site_and_regional/plumber2_usermods.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import os
1414
import tqdm
1515

16-
import pandas as pd
16+
# pylint:disable=wrong-import-position
17+
from ctsm.site_and_regional.plumber2_shared import read_plumber2_sites_csv
1718

1819

1920
# Big ugly function to create usermod_dirs for each site
@@ -155,7 +156,7 @@ def main():
155156
"""
156157

157158
# For now we can just run the 'main' program as a loop
158-
plumber2_sites = pd.read_csv("PLUMBER2_sites.csv", skiprows=4)
159+
plumber2_sites = read_plumber2_sites_csv()
159160

160161
for _, row in tqdm.tqdm(plumber2_sites.iterrows()):
161162
lat = row["Lat"]

0 commit comments

Comments
 (0)