Skip to content

Commit 7eded0f

Browse files
authored
Merge pull request #426 from jtwhite79/develop
general bug fixes
2 parents f44b38f + 7c052f7 commit 7eded0f

14 files changed

Lines changed: 226 additions & 86 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,4 @@ pest++/sweep/sweep
8282
docs/_build/
8383
docs/_doxygen/
8484
docs/_manual_assets/
85+
docs/users_manual_processed.md

.readthedocs.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ build:
1010
pre_build:
1111
# Generate Doxygen XML that Breathe will consume
1212
- doxygen Doxyfile
13+
# Preprocess users manual: convert HTML anchors to MyST targets
14+
- python docs/preprocess_manual.py documentation/pestpp_users_manual.md docs/users_manual_processed.md
1315
# Copy users manual media assets so Sphinx places them at _build/html/media/
1416
- mkdir -p docs/_manual_assets/media
1517
- cp -r documentation/media/. docs/_manual_assets/media/

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This is the top-level CMake file for PEST++
22
cmake_minimum_required(VERSION 3.15)
3-
3+
set(CMAKE_VERBOSE_MAKEFILE,ON)
44
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
55
add_compile_definitions(_DEBUG)
66
endif()

benchmarks/glm_test_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,13 +801,13 @@ def tenpar_fosm_external_stdev_test():
801801

802802
if __name__ == "__main__":
803803
#freyberg_stress_test()
804-
tenpar_xsec_stress_test()
804+
#tenpar_xsec_stress_test()
805805
#tenpar_base_test()
806806
#tenpar_fosm_external_stdev_test()
807807
#tenpar_superpar_restart_test()
808808
#freyberg_basic_restart_test()
809809
# jac_diff_invest()
810-
#new_fmt_load_test()
810+
new_fmt_load_test()
811811
#tenpar_hotstart_test()
812812
#tenpar_normalform_test()
813813
#freyberg_stress_test()

benchmarks/ies_test_part4.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2025,6 +2025,7 @@ def tenpar_adjust_weights_test():
20252025
obs.loc[:,"standard_deviation"] = 0.1
20262026
obs.loc[pst.obs_names[12:],"standard_deviation"] = 1e-11
20272027
obs.loc[pst.obs_names[12:],"obsval"] = 1e-9
2028+
#these values are hard coded later...
20282029
with open(os.path.join(template_d,"phi.csv"),'w') as f:
20292030
f.write("og1,0.333333\n")
20302031
f.write("og3,0.333333\n")
@@ -2044,6 +2045,29 @@ def tenpar_adjust_weights_test():
20442045
pst.pestpp_options['ies_verbose_level'] = 4
20452046
pst.pestpp_options["ies_bad_phi_sigma"] = -1.5
20462047

2048+
pst.control_data.noptmax = 0
2049+
pst.pestpp_options["ies_drop_conflicts"] = False
2050+
pst.pestpp_options["ies_phi_factor_file"] = "phi.csv"
2051+
pst_name = "pest_adj.pst"
2052+
pst.write(os.path.join(template_d,pst_name),version=2)
2053+
if os.path.exists(test_d):
2054+
shutil.rmtree(test_d)
2055+
shutil.copytree(template_d, test_d)
2056+
pyemu.os_utils.run("{0} {1}".format(exe_path, pst_name), cwd=test_d)
2057+
pst.set_res(os.path.join(test_d,pst_name.replace(".pst",".base.rei")))
2058+
print(pst.phi_components)
2059+
obs = pst.observation_data
2060+
aobs = pd.read_csv(os.path.join(test_d,pst_name.replace(".pst",".0.adjusted.obs_data.csv")),index_col=0)
2061+
obs.loc[aobs.index,"weight"] = aobs.weight.values
2062+
print(pst.phi_components)
2063+
print(pst.phi_components_normalized)
2064+
pcn = pst.phi_components_normalized
2065+
assert np.isclose(pcn["og3blahblah"],0.33333,1e-3)
2066+
assert np.isclose(pcn["og4yadayada"],0.33333,1e-3)
2067+
2068+
2069+
pst.pestpp_options.pop("ies_phi_factor_file")
2070+
20472071

20482072
pst.control_data.noptmax = 2
20492073
pst.pestpp_options["ies_drop_conflicts"] = False
@@ -5007,10 +5031,48 @@ def large_invest():
50075031
pyemu.os_utils.run("{0} pest.pst /e".format(exe_path),cwd=t_d)
50085032

50095033

5034+
def tenpar_xsec_combined_autoadaloc_mm_stress_test():
5035+
"""testing combined matrix + autoadaloc"""
5036+
model_d = "ies_10par_xsec"
5037+
test_d = os.path.join(model_d, "master_comb_aal_test1")
5038+
template_d = os.path.join(model_d, "test_template")
5039+
5040+
if not os.path.exists(template_d):
5041+
raise Exception("template_d {0} not found".format(template_d))
5042+
pst_name = os.path.join(template_d, "pest.pst")
5043+
pst = pyemu.Pst(pst_name)
5044+
5045+
if os.path.exists(test_d):
5046+
shutil.rmtree(test_d)
5047+
shutil.copytree(template_d, test_d)
5048+
pst.pestpp_options = {}
5049+
pst.pestpp_options["ies_num_reals"] = 30
5050+
5051+
mat = pyemu.Matrix.from_names(pst.nnz_obs_names, pst.adj_par_names).to_dataframe()
5052+
mat.loc[:, :] = 1
5053+
mat.loc[:, pst.adj_par_names[::2]] = 0
5054+
pyemu.Matrix.from_dataframe(mat).to_ascii(os.path.join(template_d, "loc.mat"))
50105055

5056+
pst.pestpp_options["ies_localizer"] = "loc.mat"
5057+
pst.pestpp_options["ies_autoadaloc"] = True
5058+
pst.pestpp_options["ies_verbose_level"] = 3
5059+
pst.pestpp_options["ies_debug_fail_remainder"] = True
5060+
pst.pestpp_options["ies_debug_fail_subset"] = True
5061+
pst.pestpp_options["ies_debug_bad_phi"] = True
5062+
pst.pestpp_options["ies_multimodal_alpha"] = 0.99
5063+
5064+
pst.control_data.noptmax = 3
50115065

5066+
pst.write(os.path.join(template_d, "pest_aal_restart.pst"))
5067+
pyemu.os_utils.start_workers(template_d, exe_path, "pest_aal_restart.pst", num_workers=10,
5068+
master_dir=test_d, verbose=True, worker_root=model_d,
5069+
port=port)
5070+
50125071

50135072
if __name__ == "__main__":
5073+
tenpar_xsec_combined_autoadaloc_mm_stress_test()
5074+
5075+
#tenpar_adjust_weights_test()
50145076
#large_invest()
50155077
#tenpar_fixed_transform_test()
50165078
#tenpar_reg_factor_test()
@@ -5031,7 +5093,7 @@ def large_invest():
50315093
#tenpar_reg_factor_test()
50325094
#tenpar_high_phi_test()
50335095
#tenpar_iqr_bad_phi_sigma_test()
5034-
multimodal_test()
5096+
#multimodal_test()
50355097
#plot_mm1_sweep_results()
50365098
#plot_mm1_results()
50375099
#plot_mm1_results_seq()

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
html_extra_path = ["_manual_assets"]
4141

4242
# -- MyST (Markdown support) -------------------------------------------------
43-
myst_enable_extensions = ["colon_fence"]
43+
myst_enable_extensions = ["colon_fence", "html_image"]
4444
source_suffix = {
4545
".rst": "restructuredtext",
4646
".md": "markdown",

docs/preprocess_manual.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Preprocess pestpp_users_manual.md for Sphinx/MyST compatibility.
4+
5+
Converts HTML anchor tags in headings to MyST named targets so that
6+
internal links like [Section](#s5-1) resolve correctly in Sphinx.
7+
8+
Before: ## <a id='s5-1' />1.1 PEST++ and PEST
9+
After: (s5-1)=
10+
## 1.1 PEST++ and PEST
11+
"""
12+
import re
13+
import sys
14+
from pathlib import Path
15+
16+
17+
def preprocess(input_path, output_path):
18+
content = Path(input_path).read_text(encoding="utf-8")
19+
20+
# Convert: # <a id='s5' />1. Introduction
21+
# To: (s5)=
22+
# # 1. Introduction
23+
def replace_heading_anchor(m):
24+
hashes = m.group(1)
25+
anchor_id = m.group(2)
26+
heading_text = m.group(3).strip()
27+
return f"({anchor_id})=\n{hashes} {heading_text}"
28+
29+
content = re.sub(
30+
r'^(#{1,6})[ \t]*<a\s+id=[\'"]([^\'"]+)[\'"][ \t]*/?>[ \t]*(.*)$',
31+
replace_heading_anchor,
32+
content,
33+
flags=re.MULTILINE,
34+
)
35+
36+
Path(output_path).write_text(content, encoding="utf-8")
37+
38+
39+
if __name__ == "__main__":
40+
preprocess(sys.argv[1], sys.argv[2])

docs/users_guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Users Guide
22

3-
```{include} ../documentation/pestpp_users_manual.md
3+
```{include} users_manual_processed.md
44
```

scripts/build_pestpp_mac_arm.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ cd "$script_path"/..
1111
rm -rfd build
1212
mkdir build
1313
cd build
14-
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++ -DFORCE_STATIC=OFF ..
14+
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" -DCMAKE_CXX_COMPILER=g++ -DFORCE_STATIC=OFF ..
1515
make -j
1616
cpack -G TGZ
1717
cp *.tar.gz ../

src/libs/common/utilities.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,45 @@ double get_duration_sec(std::chrono::system_clock::time_point start_time)
9696
return (double)std::chrono::duration_cast<std::chrono::milliseconds>(dt).count() / 1000.0;
9797
}
9898

99+
// Split `input` on whitespace into tokens. Single and double quotes may be
100+
// nested inside each other; all quote characters (at every nesting level) are
101+
// stripped from the output. Empty and whitespace-only tokens are omitted.
102+
// Throws std::runtime_error if a quoted section is never closed.
103+
std::vector<std::string> tokenize_w_quotes(const std::string& input) {
104+
std::vector<std::string> tokens;
105+
std::string current;
106+
std::vector<char> quote_stack; // innermost open-quote char at back
107+
108+
for (char c : input) {
109+
if (!quote_stack.empty() && c == quote_stack.back()) {
110+
// Closing quote matches the innermost open quote — pop it.
111+
// The quote character itself is not copied into the token.
112+
quote_stack.pop_back();
113+
} else if (c == '"' || c == '\'') {
114+
// Opening quote (or an inner quote of the other type) — push it.
115+
// The quote character itself is not copied into the token.
116+
quote_stack.push_back(c);
117+
} else if (quote_stack.empty() && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) {
118+
// Whitespace outside all quotes — flush non-empty token.
119+
if (!current.empty()) {
120+
tokens.push_back(current);
121+
current.clear();
122+
}
123+
} else {
124+
current += c;
125+
}
126+
}
127+
128+
if (!quote_stack.empty()) {
129+
throw std::runtime_error("unterminated quoted string: '" + input + "'");
130+
}
131+
if (!current.empty()) {
132+
tokens.push_back(current);
133+
}
134+
135+
return tokens;
136+
}
137+
99138
template < class ContainerT >
100139
void tokenize(const std::string& str, ContainerT& tokens, const std::string& delimiters, const bool trimEmpty)
101140
{

0 commit comments

Comments
 (0)