rc-qlc 0.3.24__cp310-cp310-win32.whl → 0.3.26__cp310-cp310-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qlc/cli/__init__.py +100 -9
- qlc/cli/installer.py +23 -5
- qlc/cli/qlc_main.py +54 -32
- qlc/cli/qlc_py_main.py +43 -38
- qlc/config/json/qlc_config.json +94 -10
- qlc/config/nml/mars_A1_sfc.nml +4 -5
- qlc/config/nml/mars_A3_sfc.nml +0 -1
- qlc/config/nml/mars_B1_pl.nml +2 -2
- qlc/{examples/cams_case_1/config/nml/mars_A3_sfc.nml → config/nml/mars_B1_sfc.nml} +7 -8
- qlc/config/nml/mars_C1_pl.nml +1 -1
- qlc/{examples/cams_case_1/config/nml/mars_C1_pl.nml → config/nml/mars_C1_sfc.nml} +7 -8
- qlc/config/nml/mars_C2_pl.nml +1 -1
- qlc/{examples/cams_case_1/config/qlc_cams.conf → config/qlc.conf} +80 -18
- qlc/doc/README.md +98 -62
- qlc/doc/USAGE.md +68 -29
- qlc/examples/cams_case_1/mod/b2rn/2018/b2rn_20181201-20181221_B1_pl.grb +0 -0
- qlc/examples/cams_case_1/mod/b2rn/2018/b2rn_20181201-20181221_C1_sfc.grb +0 -0
- qlc/examples/cams_case_1/mod/b2ro/2018/b2ro_20181201-20181221_B1_pl.grb +0 -0
- qlc/examples/cams_case_1/mod/b2ro/2018/b2ro_20181201-20181221_C1_sfc.grb +0 -0
- qlc/install.py +260 -106
- qlc/py/__main__.cp310-win32.pyd +0 -0
- qlc/py/averaging.cp310-win32.pyd +0 -0
- qlc/py/bias_plots.cp310-win32.pyd +0 -0
- qlc/py/control.cp310-win32.pyd +0 -0
- qlc/py/io.cp310-win32.pyd +0 -0
- qlc/py/loadmod.cp310-win32.pyd +0 -0
- qlc/py/loadobs.cp310-win32.pyd +0 -0
- qlc/py/logging_utils.cp310-win32.pyd +0 -0
- qlc/py/map_plots.cp310-win32.pyd +0 -0
- qlc/py/matched.cp310-win32.pyd +0 -0
- qlc/py/plot_config.cp310-win32.pyd +0 -0
- qlc/py/plotting.cp310-win32.pyd +0 -0
- qlc/py/plugin_loader.cp310-win32.pyd +0 -0
- qlc/py/processing.cp310-win32.pyd +0 -0
- qlc/py/scatter_plots.cp310-win32.pyd +0 -0
- qlc/py/stations.cp310-win32.pyd +0 -0
- qlc/py/statistics.cp310-win32.pyd +0 -0
- qlc/py/style.cp310-win32.pyd +0 -0
- qlc/py/timeseries_plots.cp310-win32.pyd +0 -0
- qlc/py/utils.cp310-win32.pyd +0 -0
- qlc/py/version.cp310-win32.pyd +0 -0
- qlc/sh/qlc_A1.sh +29 -11
- qlc/sh/qlc_B1a.sh +1 -18
- qlc/sh/qlc_B2.sh +8 -1
- qlc/sh/qlc_C5.sh +19 -30
- qlc/sh/qlc_D1.sh +291 -51
- qlc/sh/qlc_Z1.sh +6 -6
- qlc/sh/qlc_batch.sh +61 -0
- qlc/sh/qlc_common_functions.sh +17 -29
- qlc/sh/qlc_main.sh +32 -26
- qlc/sh/tex_template/beamercolorthemeCAMS2_35.sty +51 -0
- qlc/sh/tex_template/beamerfontthemeCAMS2_35.sty +166 -0
- qlc/sh/tex_template/beamerthemeCAMS2_35.sty +25 -0
- qlc/sh/tex_template/subcaption.sty +170 -0
- qlc/sh/tex_template/template.tex +109 -0
- rc_qlc-0.3.26.dist-info/METADATA +178 -0
- rc_qlc-0.3.26.dist-info/RECORD +102 -0
- qlc/config/json/qlc_config_example_1a_all-obs.json +0 -237
- qlc/config/json/qlc_config_example_1b_all-mod.json +0 -353
- qlc/config/json/qlc_config_example_1c_all-coll.json +0 -266
- qlc/config/json/qlc_config_example_2a_all-obs.json +0 -237
- qlc/config/json/qlc_config_example_2b_all-mod.json +0 -353
- qlc/config/json/qlc_config_example_2c_all-coll.json +0 -265
- qlc/config/json/qlc_config_example_3a-us_obs.json +0 -82
- qlc/config/json/qlc_config_example_3b-us_mod.json +0 -122
- qlc/config/json/qlc_config_example_3c-us_coll.json +0 -46
- qlc/config/json/qlc_config_example_4a_eu-obs.json +0 -41
- qlc/config/json/qlc_config_example_4b_eu-mod.json +0 -122
- qlc/config/json/qlc_config_example_4c_eu-coll.json +0 -45
- qlc/config/qlc_cams.conf +0 -26
- qlc/config/qlc_test.conf +0 -26
- qlc/config/qlc_tex.conf +0 -107
- qlc/examples/cams_case_1/config/json/qlc_config.json +0 -41
- qlc/examples/cams_case_1/config/nml/mars_B1_pl.nml +0 -19
- qlc/examples/cams_case_1/mod/b2ro/2018/b2ro_20181215-20181231_A3_sfc.grb +0 -0
- qlc/examples/cams_case_1/mod/iqi9/2018/iqi9_20181215-20181231_A3_sfc.grb +0 -0
- qlc/sh/qlc_start.sh +0 -23
- qlc/sh/qlc_start_batch.sh +0 -46
- rc_qlc-0.3.24.dist-info/METADATA +0 -142
- rc_qlc-0.3.24.dist-info/RECORD +0 -113
- {rc_qlc-0.3.24.dist-info → rc_qlc-0.3.26.dist-info}/WHEEL +0 -0
- {rc_qlc-0.3.24.dist-info → rc_qlc-0.3.26.dist-info}/entry_points.txt +0 -0
- {rc_qlc-0.3.24.dist-info → rc_qlc-0.3.26.dist-info}/licenses/LICENSE +0 -0
- {rc_qlc-0.3.24.dist-info → rc_qlc-0.3.26.dist-info}/top_level.txt +0 -0
qlc/install.py
CHANGED
@@ -23,12 +23,13 @@ def read_version_json(version_json_path: Path) -> dict:
|
|
23
23
|
def get_bin_path():
|
24
24
|
return Path(sys.executable).resolve().parent
|
25
25
|
|
26
|
-
def copy_or_link(src, dst, symlink=False):
|
26
|
+
def copy_or_link(src, dst, symlink=False, relative=False):
|
27
27
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
28
28
|
if dst.exists() or dst.is_symlink():
|
29
29
|
dst.unlink()
|
30
30
|
if symlink:
|
31
|
-
|
31
|
+
link_target = os.path.relpath(src.resolve(), dst.parent) if relative else src.resolve()
|
32
|
+
dst.symlink_to(link_target, target_is_directory=src.is_dir())
|
32
33
|
else:
|
33
34
|
shutil.copy(src, dst)
|
34
35
|
|
@@ -49,41 +50,39 @@ def copytree_with_symlinks(src: Path, dst: Path):
|
|
49
50
|
else:
|
50
51
|
shutil.copy2(s, d)
|
51
52
|
|
52
|
-
def safe_move_and_link(
|
53
|
+
def safe_move_and_link(src: Path, dst: Path, relative: bool = False, backup: bool = True):
|
53
54
|
"""
|
54
|
-
|
55
|
-
If
|
55
|
+
Safely create a symlink from dst to src.
|
56
|
+
If dst exists, it's backed up (if backup=True) before the new link is created.
|
57
|
+
If dst is already a symlink pointing to src, do nothing.
|
56
58
|
"""
|
57
|
-
if
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
if dst.is_symlink():
|
60
|
+
try:
|
61
|
+
if dst.resolve() == src.resolve():
|
62
|
+
print(f"[SKIP] Link {dst} already points to {src}")
|
63
|
+
return
|
64
|
+
else:
|
65
|
+
print(f"[INFO] Unlinking existing symlink {dst} -> {dst.readlink()}")
|
66
|
+
dst.unlink()
|
67
|
+
except FileNotFoundError:
|
68
|
+
# This handles broken symlinks
|
69
|
+
print(f"[INFO] Removing broken symlink {dst}")
|
70
|
+
dst.unlink()
|
71
|
+
|
72
|
+
elif dst.exists():
|
73
|
+
if backup:
|
74
|
+
backup_dst = dst.with_name(f"{dst.name}_backup_link")
|
75
|
+
print(f"[BACKUP] Moving existing path {dst} -> {backup_dst}")
|
76
|
+
shutil.move(str(dst), str(backup_dst))
|
62
77
|
else:
|
63
|
-
print(f"[
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
backup = target.with_name(f"{target.name}_backup{count}")
|
72
|
-
count += 1
|
73
|
-
print(f"[BACKUP] Moving existing directory {target} → {backup}")
|
74
|
-
shutil.move(str(target), str(backup))
|
75
|
-
else:
|
76
|
-
# File or other type — always back up
|
77
|
-
backup = target.with_name(f"{target.name}_backup")
|
78
|
-
count = 1
|
79
|
-
while backup.exists():
|
80
|
-
backup = target.with_name(f"{target.name}_backup{count}")
|
81
|
-
count += 1
|
82
|
-
print(f"[BACKUP] Moving existing file {target} → {backup}")
|
83
|
-
shutil.move(str(target), str(backup))
|
84
|
-
|
85
|
-
print(f"[LINK] {target} → {source}")
|
86
|
-
target.symlink_to(source, target_is_directory=True)
|
78
|
+
print(f"[INFO] Removing existing path {dst}")
|
79
|
+
if dst.is_dir():
|
80
|
+
shutil.rmtree(dst)
|
81
|
+
else:
|
82
|
+
dst.unlink()
|
83
|
+
|
84
|
+
# Create the new link
|
85
|
+
copy_or_link(src, dst, symlink=True, relative=relative)
|
87
86
|
|
88
87
|
def update_qlc_version(config_path: Path, version: str):
|
89
88
|
if not config_path.exists():
|
@@ -106,7 +105,97 @@ def update_qlc_version(config_path: Path, version: str):
|
|
106
105
|
config_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
|
107
106
|
print(f"[UPDATED] QLC_VERSION set to {version} in {config_path}")
|
108
107
|
|
109
|
-
def
|
108
|
+
def setup_data_directories(root: Path, mode: str):
|
109
|
+
"""
|
110
|
+
Creates the two-stage symlink structure for data-heavy directories WITHIN a mode's root.
|
111
|
+
- Creates <root>/data
|
112
|
+
- Populates it with either real directories (test) or symlinks (cams)
|
113
|
+
- Creates top-level symlinks from <root>/* -> <root>/data/*
|
114
|
+
"""
|
115
|
+
print("[SETUP] Configuring data directories...")
|
116
|
+
data_dir = root / "data"
|
117
|
+
data_dir.mkdir(exist_ok=True)
|
118
|
+
|
119
|
+
# Define the mapping for CAMS mode
|
120
|
+
cams_env_map = {
|
121
|
+
"Results": "SCRATCH",
|
122
|
+
"Analysis": "HPCPERM",
|
123
|
+
"Plots": "PERM",
|
124
|
+
"Presentations": "PERM",
|
125
|
+
"log": "PERM",
|
126
|
+
"run": "PERM",
|
127
|
+
"output": "PERM"
|
128
|
+
}
|
129
|
+
|
130
|
+
data_heavy_dirs = ["Results", "Analysis", "Plots", "Presentations", "log", "run", "output"]
|
131
|
+
|
132
|
+
for d in data_heavy_dirs:
|
133
|
+
data_subdir = data_dir / d
|
134
|
+
|
135
|
+
# Robustly remove existing path before creating a new one
|
136
|
+
if data_subdir.is_symlink():
|
137
|
+
data_subdir.unlink()
|
138
|
+
elif data_subdir.is_dir():
|
139
|
+
shutil.rmtree(data_subdir)
|
140
|
+
elif data_subdir.exists():
|
141
|
+
data_subdir.unlink()
|
142
|
+
|
143
|
+
if mode == "cams":
|
144
|
+
env_var = cams_env_map.get(d)
|
145
|
+
target_base_path = os.environ.get(env_var)
|
146
|
+
|
147
|
+
if target_base_path:
|
148
|
+
target_path = Path(target_base_path) / d
|
149
|
+
target_path.mkdir(parents=True, exist_ok=True)
|
150
|
+
data_subdir.symlink_to(target_path, target_is_directory=True)
|
151
|
+
print(f"[LINK] {data_subdir} -> {target_path}")
|
152
|
+
else:
|
153
|
+
print(f"[WARN] Environment variable ${env_var} not set for {d}. Creating local directory.")
|
154
|
+
data_subdir.mkdir(exist_ok=True)
|
155
|
+
else: # test mode
|
156
|
+
data_subdir.mkdir(exist_ok=True)
|
157
|
+
print(f"[MKDIR] {data_subdir}")
|
158
|
+
|
159
|
+
# Create the top-level symlink, e.g., <root>/Results -> <root>/data/Results
|
160
|
+
top_level_link = root / d
|
161
|
+
if top_level_link.is_symlink() or top_level_link.exists():
|
162
|
+
top_level_link.unlink()
|
163
|
+
|
164
|
+
# --- Create a relative symlink ---
|
165
|
+
# The target is data_subdir, and the link is created at top_level_link.
|
166
|
+
# We need the path of the target relative to the link's parent directory.
|
167
|
+
relative_target = os.path.relpath(data_subdir, top_level_link.parent)
|
168
|
+
top_level_link.symlink_to(relative_target, target_is_directory=True)
|
169
|
+
print(f"[LINK] {top_level_link} -> {relative_target}")
|
170
|
+
|
171
|
+
def link_model_experiments(mod_data_src_root: Path, results_dst_root: Path, debug: bool = False):
|
172
|
+
"""
|
173
|
+
Links model experiment files from mod_data_src_root to results_dst_root.
|
174
|
+
Creates absolute symlinks for the model data files.
|
175
|
+
"""
|
176
|
+
if not mod_data_src_root.is_dir():
|
177
|
+
print(f"[WARN] Source directory not found for model experiments: {mod_data_src_root}")
|
178
|
+
return
|
179
|
+
|
180
|
+
for exp_dir in mod_data_src_root.iterdir():
|
181
|
+
if exp_dir.is_dir():
|
182
|
+
# This is an experiment dir, e.g., /path/to/test/mod/b2ro
|
183
|
+
results_exp_dir = results_dst_root / exp_dir.name
|
184
|
+
results_exp_dir.mkdir(exist_ok=True)
|
185
|
+
|
186
|
+
# Find all .grb files, searching recursively through year folders
|
187
|
+
for year_dir in exp_dir.iterdir():
|
188
|
+
if year_dir.is_dir():
|
189
|
+
for src_file in year_dir.glob('*.grb'):
|
190
|
+
dst_file = results_exp_dir / src_file.name
|
191
|
+
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
192
|
+
# Use absolute paths for these links as they point from the data dir to the mod dir,
|
193
|
+
# which can be far apart.
|
194
|
+
copy_or_link(src_file, dst_file, symlink=True, relative=False)
|
195
|
+
if debug:
|
196
|
+
print(f"[LINK] {dst_file} -> {src_file}")
|
197
|
+
|
198
|
+
def setup(mode: str, version: str, debug: bool = False, config_file: str = None):
|
110
199
|
|
111
200
|
# Define source root
|
112
201
|
qlc_root = Path(__file__).resolve().parent.parent
|
@@ -117,73 +206,92 @@ def setup(mode, config_file=None, version="latest"):
|
|
117
206
|
|
118
207
|
from qlc.py.version import QLC_VERSION as version
|
119
208
|
|
120
|
-
#
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
raise ValueError(f"Unsupported mode: {mode}")
|
134
|
-
|
135
|
-
source = user_home / f"qlc_v{version}"
|
209
|
+
# --- QLC paths are now consistently based on $HOME ---
|
210
|
+
install_base = Path.home()
|
211
|
+
user_home = install_base
|
212
|
+
print(f"[INFO] Using $HOME as installation base: {install_base}")
|
213
|
+
|
214
|
+
# The stable link path, always $HOME/qlc
|
215
|
+
qlc_stable_link = user_home / "qlc"
|
216
|
+
|
217
|
+
# The versioned installation directory, e.g., $HOME/qlc_v0.3.25
|
218
|
+
versioned_install_dir = user_home / f"qlc_v{version}"
|
219
|
+
|
220
|
+
# The mode-specific root, e.g., $HOME/qlc_v0.3.25/test
|
221
|
+
root = versioned_install_dir / mode
|
136
222
|
|
137
|
-
if
|
138
|
-
|
223
|
+
# --- Backup Logic: Back up the entire versioned directory if the specific mode being installed already exists ---
|
224
|
+
if root.exists():
|
225
|
+
backup_name = f"{versioned_install_dir.name}_backup"
|
226
|
+
backup = versioned_install_dir.with_name(backup_name)
|
139
227
|
count = 1
|
140
228
|
while backup.exists():
|
141
|
-
backup =
|
229
|
+
backup = versioned_install_dir.with_name(f"{backup_name}{count}")
|
142
230
|
count += 1
|
143
|
-
print(f"[BACKUP] Moving existing install root {
|
144
|
-
shutil.move(str(
|
145
|
-
|
146
|
-
root = source / mode
|
147
|
-
# safe_move_and_link(source, Path.home() / f"qlc_v{version}")
|
231
|
+
print(f"[BACKUP] Moving existing install root {versioned_install_dir} → {backup}")
|
232
|
+
shutil.move(str(versioned_install_dir), str(backup))
|
148
233
|
|
149
234
|
print(f"[SETUP] Mode: {mode}, Version: {version}")
|
150
|
-
print(f"[PATHS] QLC Root: {root}")
|
235
|
+
print(f"[PATHS] QLC Install Root: {root}")
|
236
|
+
|
237
|
+
# Create essential directories for the mode
|
238
|
+
root.mkdir(parents=True, exist_ok=True)
|
151
239
|
|
152
|
-
# Prepare paths
|
240
|
+
# Prepare paths inside the versioned, mode-specific directory
|
153
241
|
config_dst = root / "config"
|
154
242
|
example_dst = root / "examples"
|
155
243
|
bin_dst = root / "bin"
|
156
|
-
log_dst = root / "log"
|
157
244
|
mod_dst = root / "mod"
|
158
245
|
obs_dst = root / "obs"
|
159
246
|
doc_dst = root / "doc"
|
160
|
-
run_dst = root / "run"
|
161
|
-
out_dst = root / "output"
|
162
247
|
plug_dst = root / "plugin"
|
163
|
-
|
164
|
-
# Create
|
165
|
-
|
248
|
+
|
249
|
+
# Create non-data directories inside the mode-specific root
|
250
|
+
# NOTE: 'run' and 'output' are now handled by setup_data_directories
|
251
|
+
for path in [config_dst, example_dst, bin_dst, mod_dst, obs_dst, doc_dst, plug_dst]:
|
166
252
|
path.mkdir(parents=True, exist_ok=True)
|
167
253
|
|
254
|
+
# --- Setup the new data directory structure INSIDE the mode root ---
|
255
|
+
setup_data_directories(root, mode)
|
256
|
+
|
168
257
|
# Copy config files
|
169
|
-
|
170
|
-
|
258
|
+
shutil.copytree(config_src, config_dst, dirs_exist_ok=True)
|
259
|
+
print(f"[COPY] {config_src} -> {config_dst}")
|
171
260
|
|
172
|
-
#
|
261
|
+
# Link example directories instead of copying
|
173
262
|
if example_src.exists():
|
174
|
-
|
175
|
-
|
263
|
+
for item in example_src.iterdir():
|
264
|
+
if item.is_dir(): # Only link directories
|
265
|
+
dst_link = example_dst / item.name
|
266
|
+
if dst_link.exists() or dst_link.is_symlink():
|
267
|
+
if dst_link.is_dir() and not dst_link.is_symlink():
|
268
|
+
shutil.rmtree(dst_link)
|
269
|
+
else:
|
270
|
+
dst_link.unlink()
|
271
|
+
dst_link.symlink_to(item.resolve(), target_is_directory=True)
|
272
|
+
print(f"[LINK] {dst_link} -> {item}")
|
176
273
|
|
177
274
|
# Link all documentation files
|
178
275
|
# shutil.copytree(doc_src, doc_dst, dirs_exist_ok=True)
|
179
276
|
for doc_file in doc_src.glob("*"):
|
180
277
|
dst = doc_dst / doc_file.name
|
181
|
-
copy_or_link(doc_file, dst, symlink=True)
|
278
|
+
copy_or_link(doc_file, dst, symlink=True, relative=False)
|
279
|
+
print(f"[LINK] {dst} -> {doc_file.resolve()}")
|
182
280
|
|
183
281
|
# Link all *.sh files to bin_dst (helpers included)
|
184
282
|
for sh_file in sh_src.glob("*.sh"):
|
185
283
|
dst = bin_dst / sh_file.name
|
186
|
-
copy_or_link(sh_file, dst, symlink=True)
|
284
|
+
copy_or_link(sh_file, dst, symlink=True, relative=False)
|
285
|
+
print(f"[LINK] {dst} -> {sh_file.resolve()}")
|
286
|
+
|
287
|
+
# Copy the TeX template directory to bin_dst
|
288
|
+
tex_template_src = sh_src / "tex_template"
|
289
|
+
tex_template_dst = bin_dst / "tex_template"
|
290
|
+
if tex_template_src.is_dir():
|
291
|
+
if tex_template_dst.exists():
|
292
|
+
shutil.rmtree(tex_template_dst)
|
293
|
+
shutil.copytree(tex_template_src, tex_template_dst)
|
294
|
+
print(f"[COPY] {tex_template_src} -> {tex_template_dst}")
|
187
295
|
|
188
296
|
# Create shell tool links (now handled by entry_points in setup.py)
|
189
297
|
pass
|
@@ -198,17 +306,16 @@ def setup(mode, config_file=None, version="latest"):
|
|
198
306
|
obs_data_dest_dir = root / "obs/data/ver0d/ebas_daily"
|
199
307
|
obs_data_dest_dir.mkdir(parents=True, exist_ok=True)
|
200
308
|
|
201
|
-
# Create the v_20240216 symlink
|
309
|
+
# Create the v_20240216 symlink with an absolute path
|
202
310
|
link1_target = obs_data_dest_dir / "v_20240216"
|
203
|
-
|
204
|
-
|
205
|
-
link1_target.symlink_to(obs_data_source.resolve(), target_is_directory=True)
|
206
|
-
print(f"[LINK] {link1_target} -> {obs_data_source}")
|
311
|
+
copy_or_link(obs_data_source, link1_target, symlink=True, relative=False)
|
312
|
+
print(f"[LINK] {link1_target} -> {obs_data_source.resolve()}")
|
207
313
|
|
208
314
|
# Create the latest symlink, relative to its location
|
209
315
|
link2_target = obs_data_dest_dir / "latest"
|
210
316
|
if link2_target.exists() or link2_target.is_symlink():
|
211
317
|
link2_target.unlink()
|
318
|
+
# This one MUST be relative by its nature
|
212
319
|
link2_target.symlink_to("v_20240216", target_is_directory=True)
|
213
320
|
print(f"[LINK] {link2_target} -> v_20240216")
|
214
321
|
|
@@ -220,48 +327,95 @@ def setup(mode, config_file=None, version="latest"):
|
|
220
327
|
print(f"[COPY] {station_file_dest}")
|
221
328
|
|
222
329
|
|
223
|
-
# Link sample model data
|
330
|
+
# Link sample model data, ensuring absolute paths
|
224
331
|
mod_data_src_root = root / "examples" / "cams_case_1" / "mod"
|
225
332
|
mod_data_dst_root = root / "mod"
|
226
333
|
if mod_data_src_root.is_dir():
|
227
334
|
for model_dir in mod_data_src_root.iterdir():
|
228
335
|
if model_dir.is_dir():
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
336
|
+
dst_link = mod_data_dst_root / model_dir.name
|
337
|
+
# Use copy_or_link to create an absolute symlink
|
338
|
+
copy_or_link(model_dir, dst_link, symlink=True, relative=False)
|
339
|
+
print(f"[LINK] {dst_link} -> {model_dir.resolve()}")
|
340
|
+
|
341
|
+
# Link model experiment files to the 'Results' directory (relative)
|
342
|
+
results_dst_root = root / "Results"
|
343
|
+
results_dst_root.mkdir(exist_ok=True)
|
344
|
+
print(f"[SETUP] Linking model experiments to {results_dst_root}")
|
345
|
+
|
346
|
+
if mod_data_dst_root.is_dir():
|
347
|
+
for exp_dir in mod_data_dst_root.iterdir():
|
348
|
+
if exp_dir.is_dir():
|
349
|
+
# This is an experiment dir, e.g., /path/to/test/mod/b2ro
|
350
|
+
results_exp_dir = results_dst_root / exp_dir.name
|
351
|
+
results_exp_dir.mkdir(exist_ok=True)
|
352
|
+
|
353
|
+
# Find all .grb files, searching recursively through year folders
|
354
|
+
for year_dir in exp_dir.iterdir():
|
355
|
+
if year_dir.is_dir():
|
356
|
+
for src_file in year_dir.glob('*.grb'):
|
357
|
+
dst_file = results_exp_dir / src_file.name
|
358
|
+
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
359
|
+
# Use absolute paths for these links as they point from the data dir to the mod dir,
|
360
|
+
# which can be far apart.
|
361
|
+
copy_or_link(src_file, dst_file, symlink=True, relative=False)
|
362
|
+
if debug:
|
363
|
+
print(f"[LINK] {dst_file} -> {src_file}")
|
364
|
+
|
365
|
+
# In CAMS mode: link to operational directories
|
366
|
+
if mode == "cams":
|
367
|
+
# Link obs data directory
|
368
|
+
cams_obs_src = Path("/ec/vol/cams/qlc/obs")
|
369
|
+
if obs_dst.is_symlink() or obs_dst.is_symlink():
|
370
|
+
obs_dst.unlink()
|
371
|
+
elif obs_dst.is_dir():
|
372
|
+
shutil.rmtree(obs_dst)
|
373
|
+
obs_dst.symlink_to(cams_obs_src, target_is_directory=True)
|
374
|
+
print(f"[LINK] {obs_dst} -> {cams_obs_src}")
|
375
|
+
|
376
|
+
# Link mod directory to this mode's internal Results directory
|
377
|
+
results_link = root / "Results"
|
378
|
+
if mod_dst.is_symlink() or mod_dst.is_symlink():
|
379
|
+
mod_dst.unlink()
|
380
|
+
elif mod_dst.is_dir():
|
381
|
+
shutil.rmtree(mod_dst)
|
382
|
+
mod_dst.symlink_to(results_link, target_is_directory=True)
|
383
|
+
print(f"[LINK] {mod_dst} -> {results_link}")
|
384
|
+
|
385
|
+
# The config is now static, just need to update the version
|
386
|
+
generic_config_path = config_dst / "qlc.conf"
|
387
|
+
update_qlc_version(generic_config_path, version)
|
388
|
+
|
389
|
+
# --- Setup master symlinks to point to this installation (relative) ---
|
390
|
+
# Link qlc_latest to the new version-specific root
|
391
|
+
qlc_latest_link = Path(user_home) / "qlc_latest"
|
392
|
+
safe_move_and_link(root, qlc_latest_link, relative=True, backup=False)
|
393
|
+
|
394
|
+
# Ensure $HOME/qlc points to qlc_latest
|
395
|
+
qlc_stable_link = Path(user_home) / "qlc"
|
396
|
+
if not qlc_stable_link.exists() or not qlc_stable_link.is_symlink():
|
397
|
+
safe_move_and_link(qlc_latest_link, qlc_stable_link, relative=True, backup=False)
|
398
|
+
else:
|
399
|
+
try:
|
400
|
+
if not qlc_stable_link.resolve() == qlc_latest_link.resolve():
|
401
|
+
safe_move_and_link(qlc_latest_link, qlc_stable_link, relative=True, backup=False)
|
402
|
+
else:
|
403
|
+
print(f"[SKIP] {qlc_stable_link} already links to {qlc_latest_link.name}")
|
404
|
+
except FileNotFoundError: # Handle broken link
|
405
|
+
safe_move_and_link(qlc_latest_link, qlc_stable_link, relative=True, backup=False)
|
244
406
|
|
245
407
|
# Write install info
|
246
408
|
info = {
|
247
409
|
"version": version,
|
248
410
|
"mode": mode,
|
249
|
-
"config":
|
411
|
+
"config": "qlc.conf"
|
250
412
|
}
|
251
413
|
|
252
|
-
# Preemptively remove 'qlc_latest' symlink to ensure clean update, mimicking `ln -sf`.
|
253
|
-
qlc_latest_link = Path.home() / "qlc_latest"
|
254
|
-
if qlc_latest_link.is_symlink():
|
255
|
-
qlc_latest_link.unlink()
|
256
|
-
|
257
|
-
safe_move_and_link(root, qlc_latest_link)
|
258
|
-
safe_move_and_link(qlc_latest_link, Path.home() / "qlc")
|
259
|
-
|
260
414
|
(root / "VERSION.json").write_text(json.dumps(info, indent=2))
|
261
415
|
print(f"[WRITE] VERSION.json at {root}")
|
262
416
|
|
263
|
-
|
264
|
-
update_qlc_version(
|
417
|
+
# Final version update on the stable link path
|
418
|
+
update_qlc_version(qlc_stable_link / "config" / "qlc.conf", version)
|
265
419
|
|
266
420
|
print("\n[INFO] QLC installation complete.")
|
267
421
|
print("[INFO] To get started, you may need to open a new terminal or run 'rehash'.")
|
qlc/py/__main__.cp310-win32.pyd
CHANGED
Binary file
|
qlc/py/averaging.cp310-win32.pyd
CHANGED
Binary file
|
Binary file
|
qlc/py/control.cp310-win32.pyd
CHANGED
Binary file
|
qlc/py/io.cp310-win32.pyd
CHANGED
Binary file
|
qlc/py/loadmod.cp310-win32.pyd
CHANGED
Binary file
|
qlc/py/loadobs.cp310-win32.pyd
CHANGED
Binary file
|
Binary file
|
qlc/py/map_plots.cp310-win32.pyd
CHANGED
Binary file
|
qlc/py/matched.cp310-win32.pyd
CHANGED
Binary file
|
Binary file
|
qlc/py/plotting.cp310-win32.pyd
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
qlc/py/stations.cp310-win32.pyd
CHANGED
Binary file
|
Binary file
|
qlc/py/style.cp310-win32.pyd
CHANGED
Binary file
|
Binary file
|
qlc/py/utils.cp310-win32.pyd
CHANGED
Binary file
|
qlc/py/version.cp310-win32.pyd
CHANGED
Binary file
|
qlc/sh/qlc_A1.sh
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/bin/
|
1
|
+
#!/bin/bash -e
|
2
2
|
|
3
3
|
# Source the configuration file to load the settings
|
4
4
|
. "$CONFIG_FILE"
|
@@ -34,16 +34,34 @@ for exp in $exps ; do
|
|
34
34
|
mkdir -p $MARS_RETRIEVAL_DIRECTORY/$exp
|
35
35
|
fi
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
XCLASS="
|
44
|
-
|
45
|
-
|
46
|
-
|
37
|
+
# Map the experiment prefix to the corresponding ECMWF MARS class.
|
38
|
+
# This is required for retrieving data from the MARS archive.
|
39
|
+
EXPCLASS=$(echo "${exp}" | cut -c 1)
|
40
|
+
|
41
|
+
case "${EXPCLASS}" in
|
42
|
+
a) XCLASS="be" ;; # Belgium
|
43
|
+
b) XCLASS="nl" ;; # Netherlands
|
44
|
+
c) XCLASS="fr" ;; # France
|
45
|
+
d) XCLASS="de" ;; # Germany
|
46
|
+
e) XCLASS="es" ;; # Spain
|
47
|
+
f) XCLASS="fi" ;; # Finland
|
48
|
+
g) XCLASS="gr" ;; # Greece
|
49
|
+
h) XCLASS="hu" ;; # Hungary
|
50
|
+
i) XCLASS="it" ;; # Italy
|
51
|
+
k) XCLASS="dk" ;; # Denmark
|
52
|
+
l) XCLASS="pt" ;; # Portugal
|
53
|
+
m) XCLASS="at" ;; # Austria
|
54
|
+
n) XCLASS="no" ;; # Norway
|
55
|
+
s) XCLASS="se" ;; # Sweden
|
56
|
+
t) XCLASS="tr" ;; # Turkey
|
57
|
+
u) XCLASS="uk" ;; # United Kingdom
|
58
|
+
w) XCLASS="ch" ;; # Switzerland
|
59
|
+
*) XCLASS="rd" ;; # Default to Research Department
|
60
|
+
esac
|
61
|
+
log "${exp}" "${XCLASS}"
|
62
|
+
|
63
|
+
# --------------------------------------------------------------------
|
64
|
+
# 2. MARS request for sfc data
|
47
65
|
for name in "${MARS_RETRIEVALS[@]}"; do
|
48
66
|
nml_name="mars_${name}.nml"
|
49
67
|
log "Processing subscript: $nml_name"
|
qlc/sh/qlc_B1a.sh
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/bin/
|
1
|
+
#!/bin/bash -e
|
2
2
|
|
3
3
|
# Source the configuration file to load the settings
|
4
4
|
. "$CONFIG_FILE"
|
@@ -65,7 +65,6 @@ for exp in $exps ; do
|
|
65
65
|
|
66
66
|
for name in "${MARS_RETRIEVALS[@]}"; do
|
67
67
|
|
68
|
-
set +e
|
69
68
|
# List available GRIB files for selected exp and time period
|
70
69
|
# grbfiles=($(ls *${mDate}*_${name}_*.grb))
|
71
70
|
grbfiles=($(ls *${mDate}*_${name}*.grb))
|
@@ -93,21 +92,6 @@ for exp in $exps ; do
|
|
93
92
|
ls -lh $ncfile
|
94
93
|
# ncdump -h $ncfile
|
95
94
|
fi
|
96
|
-
# conversion of tine averaged grib files to netcdf (seasmean)
|
97
|
-
ncfil2="${gribfile%.grb}_tavg.nc"
|
98
|
-
if [ ! -f "$ncfil2" ]; then
|
99
|
-
log "cdo timavg ${gribfile} ${gribfile%.grb}.grbtavg"
|
100
|
-
cdo timavg ${gribfile} ${gribfile%.grb}.grbtavg
|
101
|
-
log "cdo -f nc copy ${gribfile%.grb}.grbtavg $ncfil2"
|
102
|
-
cdo -f nc copy ${gribfile%.grb}.grbtavg $ncfil2
|
103
|
-
# rm -f "$gribfile" # Clean up GRIB file
|
104
|
-
ls -lh $ncfil2
|
105
|
-
ncdump -h $ncfil2
|
106
|
-
else
|
107
|
-
log "Nothing to do! NC-file already exists: $ncfil2"
|
108
|
-
ls -lh $ncfil2
|
109
|
-
# ncdump -h $ncfil2
|
110
|
-
fi
|
111
95
|
done # file
|
112
96
|
log "----------------------------------------------------------------------------------------"
|
113
97
|
fi
|
@@ -120,4 +104,3 @@ log "End ${SCRIPT} at `date`"
|
|
120
104
|
log "________________________________________________________________________________________"
|
121
105
|
|
122
106
|
exit 0
|
123
|
-
|
qlc/sh/qlc_B2.sh
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/bin/
|
1
|
+
#!/bin/bash -e
|
2
2
|
|
3
3
|
# Source the configuration file to load the settings
|
4
4
|
. "$CONFIG_FILE"
|
@@ -64,6 +64,8 @@ for exp in $exps ; do
|
|
64
64
|
mkdir -p "$tpath"
|
65
65
|
fi
|
66
66
|
|
67
|
+
log "----------------------------------------------------------------------------------------"
|
68
|
+
|
67
69
|
for name in "${MARS_RETRIEVALS[@]}"; do
|
68
70
|
|
69
71
|
# Define the corresponding arrays based on the name
|
@@ -76,12 +78,16 @@ for exp in $exps ; do
|
|
76
78
|
ncvar=("${!ncvar_var}")
|
77
79
|
myvar=("${!myvar_var}")
|
78
80
|
|
81
|
+
log "ipath : ${ipath}"
|
82
|
+
log "tpath : ${tpath}"
|
83
|
+
|
79
84
|
log "name : ${name}"
|
80
85
|
log "param: ${param}"
|
81
86
|
log "ncvar: ${ncvar}"
|
82
87
|
log "myvar: ${myvar}"
|
83
88
|
|
84
89
|
cd $ipath
|
90
|
+
pwd -P
|
85
91
|
|
86
92
|
set +e
|
87
93
|
# List available NC-files
|
@@ -92,6 +98,7 @@ for exp in $exps ; do
|
|
92
98
|
log "ncfiles : ${ncfiles}"
|
93
99
|
|
94
100
|
cd $tpath
|
101
|
+
pwd -P
|
95
102
|
|
96
103
|
# Loop through the variables for this $name
|
97
104
|
for ((i = 0; i < ${#ncvar[@]}; i++)); do
|