From da745cc8edbc50cc557f2726c5ecba64ad4292d3 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 26 Jan 2026 14:18:53 -0700 Subject: [PATCH 01/46] continuing SWC development --- .../concH2oSoilSalinity_group_assignment.yaml | 2 +- .../concH2oSoilSalinity_group_path.yaml | 71 +++++++++++++++++++ .../pipe_list_concH2oSoilSalinity.txt | 3 + .../pipe_list_enviroscan_development.txt | 5 +- 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml create mode 100644 pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_assignment.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_assignment.yaml index 68cd56acb..c00f96896 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_assignment.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_assignment.yaml @@ -23,7 +23,7 @@ input: - pfs: name: DIR_IN repo: concH2oSoilSalinity_group_loader - glob: /aspirated-single/* + glob: /conc-h2o-soil-salinity/* - pfs: name: FILE_YEAR repo: enviroscan_cron_daily_and_date_control diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml new file mode 100644 index 000000000..69e957684 --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml @@ -0,0 +1,71 @@ +--- +pipeline: + name: concH2oSoilSalinity_group_path +transform: + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-group-path:v1.0.1 + cmd: + - /bin/bash + stdin: + - '#!/bin/bash' + - python3 -m group_path.group_path_main + env: + GROUP: conc-h2o-soil-salinity_ + LOG_LEVEL: INFO + OUT_PATH: /pfs/out + # ERR_PATH can be changed, it is user specified + ERR_PATH: /pfs/out/errored_datums + GROUP_ASSIGNMENT_YEAR_INDEX: '4' + GROUP_ASSIGNMENT_MONTH_INDEX: '5' + GROUP_ASSIGNMENT_DAY_INDEX: '6' + GROUP_ASSIGNMENT_MEMBER_INDEX: '7' + GROUP_ASSIGNMENT_DATA_TYPE_INDEX: '8' + LOCATION_FOCUS_SOURCE_TYPE_INDEX: '3' + LOCATION_FOCUS_YEAR_INDEX: '4' + LOCATION_FOCUS_MONTH_INDEX: '5' + LOCATION_FOCUS_DAY_INDEX: '6' + LOCATION_FOCUS_LOCATION_INDEX: '7' + LOCATION_FOCUS_DATA_TYPE_INDEX: '8' + GROUP_FOCUS_YEAR_INDEX: '3' + GROUP_FOCUS_MONTH_INDEX: '4' + GROUP_FOCUS_DAY_INDEX: '5' + GROUP_FOCUS_GROUP_INDEX: '6' +input: +# The input to the group_path module must be a join between the group_assignment module and the union of any/all +# data repos that are currently in a location focus structure or a (different/L1+ dependency) group focus structure + join: + - pfs: + # name must be GROUP_ASSIGNMENT_PATH + name: GROUP_ASSIGNMENT_PATH + repo: concH2oSoilSalinity_group_assignment + glob: /conc-h2o-soil-salinity/(2025/10/18) + joinOn: $1 + outer_join: True + - pfs: + name: LOCATION_FOCUS_PATH + repo: enviroscan_fill_date_gaps_and_regularize + glob: /*/(2025/10/18) + joinOn: $1 + outer_join: True + - pfs: + name: GROUP_FOCUS_PATH + repo: tempSoil_level1_group_consolidate_srf + glob: /(2025/10/18) + joinOn: $1 + outer_join: True +parallelism_spec: + constant: 5 +autoscaling: true +resource_requests: + memory: 600M + cpu: 0.4 +resource_limits: + memory: 2G + cpu: 1.2 +sidecar_resource_requests: + memory: 3G + cpu: 1 +datum_set_spec: + number: 1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class diff --git a/pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt b/pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt new file mode 100644 index 000000000..a286dcda8 --- /dev/null +++ b/pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt @@ -0,0 +1,3 @@ +concH2oSoilSalinity_group_loader.yaml +concH2oSalinity_group_assignment.yaml +concH2oSalinity_group_path.yaml \ No newline at end of file diff --git a/pipe/enviroscan/pipe_list_enviroscan_development.txt b/pipe/enviroscan/pipe_list_enviroscan_development.txt index 19346723d..e124f4052 100644 --- a/pipe/enviroscan/pipe_list_enviroscan_development.txt +++ b/pipe/enviroscan/pipe_list_enviroscan_development.txt @@ -7,4 +7,7 @@ enviroscan_calibration_assignment.yaml enviroscan_calibration_group_and_convert.yaml enviroscan_location_asset.yaml enviroscan_location_asset_assignment.yaml -enviroscan_location_group_and_restructure.yaml \ No newline at end of file +enviroscan_location_group_and_restructure.yaml +enviroscan_location_loader.yaml +enviroscan_location_active_dates_assignment.yaml +enviroscan_fill_date_gaps_and_regularize.yaml From 04c77fa2ea64b60cd29789c2de736162c42f72ce Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 2 Feb 2026 15:36:13 -0700 Subject: [PATCH 02/46] WIP threshold loader. Needs to handle different sets of thresholds going in but ultimately still only output one threshold file for subsequent module. --- .../concH2oSoilSalinity_threshold.yaml | 65 ++++++++++++++++ ...concH2oSoilSalinity_threshold_example.yaml | 75 +++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_example.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml new file mode 100644 index 000000000..d88748983 --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml @@ -0,0 +1,65 @@ +--- +pipeline: + name: concH2oSoilSalinity_threshold +transform: + # image_pull_secrets: + # - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:v1.0.0 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + # Create output directories + mkdir -p /pfs/out/ion-content + mkdir -p /pfs/out/water-content-factory + mkdir -p /pfs/out/water-content-soil-spec + + if [ ${CTXT_IC+x} ]; then + export CTXT=$CTXT_IC + export OUT_PATH=/pfs/out/ion-content + python3 -m threshold_loader.threshold_loader_main + fi + + if [ ${CTXT_FACTWC+x} ]; then + export CTXT=$CTXT_FACTWC + export OUT_PATH=/pfs/out/water-content-factory + python3 -m threshold_loader.threshold_loader_main + fi + + if [ ${CTXT_SSWC+x} ]; then + export CTXT=$CTXT_SSWC + export OUT_PATH=/pfs/out/water-content-soil-spec + python3 -m threshold_loader.threshold_loader_main + fi + + EOF + env: + LOG_LEVEL: INFO + # Separate multiple terms with a pipe (|). Enter "none" to retrieve all terms + TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 + # Separate multiple contexts with a pipe (|). E.g. (par-met|upward-facing). Enter "none" for no context filtering. + CTXT_IC: soil|ion-content + CTXT_FACTWC: soil|water-content|factory + CTXT_SSWC: soil|water-content|soil-specific + secrets: + - name: pdr-secret + mount_path: /var/db_secret +input: + pfs: + repo: enviroscan_cron_daily_and_date_control_tick + glob: /* + empty_files: true +autoscaling: true +resource_requests: + memory: 32M + cpu: 0.05 +resource_limits: + memory: 200M + cpu: 0.5 +sidecar_resource_requests: + memory: 120M + cpu: 0.1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class \ No newline at end of file diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_example.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_example.yaml new file mode 100644 index 000000000..d568148af --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_example.yaml @@ -0,0 +1,75 @@ +--- +pipeline: + name: concH2oSoilSalinity_threshold +transform: + # image_pull_secrets: + # - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:v1.0.1 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + # Create output directories using environment variables + mkdir -p $OUT_PATH_IC + mkdir -p $OUT_PATH_FACT + mkdir -p $OUT_PATH_SSWC + + if [ ${CTXT_IC+x} ]; then + export CTXT=$CTXT_IC + export OUT_PATH=$OUT_PATH_IC + python3 -m threshold_loader.threshold_loader_main + fi + + if [ ${CTXT_FACTWC+x} ]; then + export CTXT=$CTXT_FACTWC + export OUT_PATH=$OUT_PATH_FACT + python3 -m threshold_loader.threshold_loader_main + fi + + if [ ${CTXT_SSWC+x} ]; then + export CTXT=$CTXT_SSWC + export OUT_PATH=$OUT_PATH_SSWC + python3 -m threshold_loader.threshold_loader_main + fi + + # Merge all threshold files using environment variables + jq -s '{thresholds: ([.[].thresholds // []] | flatten | unique_by(.threshold_name + .term_name + .location_name))}' \ + $OUT_PATH_IC/thresholds.json \ + $OUT_PATH_FACT/thresholds.json \ + $OUT_PATH_SSWC/thresholds.json \ + > /pfs/out/thresholds.json + + EOF + env: + OUT_PATH_IC: /pfs/out/ion-content + OUT_PATH_FACT: /pfs/out/water-content-factory + OUT_PATH_SSWC: /pfs/out/water-content-soil-spec + LOG_LEVEL: INFO + # Separate multiple terms with a pipe (|). Enter "none" to retrieve all terms + TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 + # Separate multiple contexts with a pipe (|). E.g. (par-met|upward-facing). Enter "none" for no context filtering. + CTXT_IC: soil|ion-content + CTXT_FACTWC: soil|water-content|factory + CTXT_SSWC: soil|water-content|sensor-specific + secrets: + - name: pdr-secret + mount_path: /var/db_secret +input: + pfs: + repo: enviroscan_cron_daily_and_date_control_tick + glob: /* + empty_files: true +autoscaling: true +resource_requests: + memory: 32M + cpu: 0.05 +resource_limits: + memory: 200M + cpu: 0.5 +sidecar_resource_requests: + memory: 120M + cpu: 0.1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class \ No newline at end of file From 4c434de8da714c07e602fe4ba5b49d5a6c858d4e Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 3 Feb 2026 10:58:29 -0700 Subject: [PATCH 03/46] example using multiple different sets of contexts. --- .../concH2oSoilSalinity_threshold.yaml | 18 ++++++++------ ... concH2oSoilSalinity_threshold_test2.yaml} | 24 +++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) rename pipe/concH2oSoilSalinity/{concH2oSoilSalinity_threshold_example.yaml => concH2oSoilSalinity_threshold_test2.yaml} (75%) diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml index d88748983..4414b97e3 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml @@ -10,31 +10,35 @@ transform: - "-c" - |- /bin/bash <<'EOF' - # Create output directories - mkdir -p /pfs/out/ion-content - mkdir -p /pfs/out/water-content-factory - mkdir -p /pfs/out/water-content-soil-spec + + # Create output directories using environment variables + mkdir -p $OUT_PATH_IC + mkdir -p $OUT_PATH_FACT + mkdir -p $OUT_PATH_SSWC if [ ${CTXT_IC+x} ]; then export CTXT=$CTXT_IC - export OUT_PATH=/pfs/out/ion-content + export OUT_PATH=$OUT_PATH_IC python3 -m threshold_loader.threshold_loader_main fi if [ ${CTXT_FACTWC+x} ]; then export CTXT=$CTXT_FACTWC - export OUT_PATH=/pfs/out/water-content-factory + export OUT_PATH=$OUT_PATH_FACT python3 -m threshold_loader.threshold_loader_main fi if [ ${CTXT_SSWC+x} ]; then export CTXT=$CTXT_SSWC - export OUT_PATH=/pfs/out/water-content-soil-spec + export OUT_PATH=$OUT_PATH_SSWC python3 -m threshold_loader.threshold_loader_main fi EOF env: + OUT_PATH_IC: /pfs/out/ion-content + OUT_PATH_FACT: /pfs/out/water-content-factory + OUT_PATH_SSWC: /pfs/out/water-content-soil-spec LOG_LEVEL: INFO # Separate multiple terms with a pipe (|). Enter "none" to retrieve all terms TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_example.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test2.yaml similarity index 75% rename from pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_example.yaml rename to pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test2.yaml index d568148af..873a73982 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_example.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test2.yaml @@ -1,15 +1,21 @@ --- pipeline: - name: concH2oSoilSalinity_threshold + name: concH2oSoilSalinity_threshold_test2 transform: # image_pull_secrets: # - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:v1.0.1 + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:v1.0.0 cmd: - sh - "-c" - |- /bin/bash <<'EOF' + # Create output directories + + # Install jq for testing (download static binary) + curl -L https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-amd64 -o /tmp/jq + chmod +x /tmp/jq + # Create output directories using environment variables mkdir -p $OUT_PATH_IC mkdir -p $OUT_PATH_FACT @@ -33,11 +39,11 @@ transform: python3 -m threshold_loader.threshold_loader_main fi - # Merge all threshold files using environment variables - jq -s '{thresholds: ([.[].thresholds // []] | flatten | unique_by(.threshold_name + .term_name + .location_name))}' \ - $OUT_PATH_IC/thresholds.json \ - $OUT_PATH_FACT/thresholds.json \ - $OUT_PATH_SSWC/thresholds.json \ + # Merge using the downloaded jq + /tmp/jq -s '{thresholds: ([.[].thresholds // []] | flatten | unique_by(.threshold_name + .term_name + .location_name))}' \ + /pfs/out/ion-content/thresholds.json \ + /pfs/out/water-content-factory/thresholds.json \ + /pfs/out/water-content-soil-spec/thresholds.json \ > /pfs/out/thresholds.json EOF @@ -46,12 +52,10 @@ transform: OUT_PATH_FACT: /pfs/out/water-content-factory OUT_PATH_SSWC: /pfs/out/water-content-soil-spec LOG_LEVEL: INFO - # Separate multiple terms with a pipe (|). Enter "none" to retrieve all terms TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 - # Separate multiple contexts with a pipe (|). E.g. (par-met|upward-facing). Enter "none" for no context filtering. CTXT_IC: soil|ion-content CTXT_FACTWC: soil|water-content|factory - CTXT_SSWC: soil|water-content|sensor-specific + CTXT_SSWC: soil|water-content|soil-specific secrets: - name: pdr-secret mount_path: /var/db_secret From 1bd62c264b4eb25f1270c18a54f8c24f66e879c8 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 3 Feb 2026 15:14:07 -0700 Subject: [PATCH 04/46] test update to threshold module to accept multiple contexts. --- modules/threshold_loader/threshold_loader.py | 40 ++++++++++++------- .../threshold_loader/threshold_loader_main.py | 20 +++++++++- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/modules/threshold_loader/threshold_loader.py b/modules/threshold_loader/threshold_loader.py index 6cc801cbe..8f11afffa 100644 --- a/modules/threshold_loader/threshold_loader.py +++ b/modules/threshold_loader/threshold_loader.py @@ -1,31 +1,41 @@ #!/usr/bin/env python3 from pathlib import Path import json -from typing import Callable, Iterator +from typing import Callable, Iterator, List from data_access.types.threshold import Threshold -def load_thresholds(get_thresholds: Callable[[str], Iterator[Threshold]], out_path: Path, term: str, context: str): +def load_thresholds(get_thresholds: Callable[[str], Iterator[Threshold]], out_path: Path, term: str, contexts: List[str]): """ - Write a threshold file into the output path. + Write a threshold file into the output path with combined results from multiple context sets. :param get_thresholds: Function yielding thresholds. :param out_path: The path for writing results. :param term: The term name. - :param context: The context code. + :param contexts: List of context strings (each with pipe-separated values). """ + # Create output directory if it doesn't exist + out_path.mkdir(parents=True, exist_ok=True) + with open(Path(out_path,'thresholds.json'), 'w') as file: - thresholds = [] - if context == 'none': - context_l = [] - else: - context_l = context.split("|") - for threshold in get_thresholds(term=term): - if threshold[3]: - if set(context_l).issubset(set(threshold[3])): - thresholds.append(threshold._asdict()) - threshold_data = {} - threshold_data.update({'thresholds': thresholds}) + all_thresholds = [] + seen = set() # Track unique thresholds to avoid duplicates + + # Process each context set + for context in contexts: + context_l = context.split("|") if context != 'none' else [] + + # Get all thresholds for the term and filter by this context + for threshold in get_thresholds(term=term): + if threshold[3]: # if threshold has contexts + if set(context_l).issubset(set(threshold[3])): + # Create unique key to avoid duplicates + unique_key = (threshold[0], threshold[1], threshold[2]) # threshold_name, term_name, location_name + if unique_key not in seen: + seen.add(unique_key) + all_thresholds.append(threshold._asdict()) + + threshold_data = {'thresholds': all_thresholds} json_data = json.dumps(threshold_data, indent=4, sort_keys=True, default=str) file.write(json_data) diff --git a/modules/threshold_loader/threshold_loader_main.py b/modules/threshold_loader/threshold_loader_main.py index 30266444d..402629fa6 100644 --- a/modules/threshold_loader/threshold_loader_main.py +++ b/modules/threshold_loader/threshold_loader_main.py @@ -18,15 +18,31 @@ def main() -> None: env = environs.Env() out_path: Path = env.path('OUT_PATH') term: str = env.str('TERM') - context: str = env.str('CTXT') + + # Read multiple context sets - support CTXT_1, CTXT_2, CTXT_3, etc. + contexts = [] + for i in range(1, 100): # Support up to 99 context sets + ctxt_var = f'CTXT_{i}' + context = env.str(ctxt_var, default=None) + if context: + contexts.append(context) + else: + break # Stop at first missing number + + # Fallback to single CTXT for backward compatibility + if not contexts: + context = env.str('CTXT', default='none') + contexts = [context] + log_level: str = env.log_level('LOG_LEVEL', 'INFO') log_config.configure(log_level) log = get_logger() log.debug(f'out_path: {out_path}') + log.debug(f'contexts: {contexts}') db_config = read_from_mount(Path('/var/db_secret')) with closing(DbConnector(db_config)) as connector: get_thresholds_partial = partial(get_thresholds, connector=connector) - load_thresholds(get_thresholds_partial, out_path, term=term, context=context) + load_thresholds(get_thresholds_partial, out_path, term=term, contexts=contexts) if __name__ == "__main__": From b138894a4808a5208d330f45948d45dd15770192 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 3 Feb 2026 15:45:30 -0700 Subject: [PATCH 05/46] update context filter --- modules/threshold_loader/threshold_loader.py | 23 ++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/modules/threshold_loader/threshold_loader.py b/modules/threshold_loader/threshold_loader.py index 8f11afffa..67ccf6afc 100644 --- a/modules/threshold_loader/threshold_loader.py +++ b/modules/threshold_loader/threshold_loader.py @@ -22,19 +22,24 @@ def load_thresholds(get_thresholds: Callable[[str], Iterator[Threshold]], out_pa all_thresholds = [] seen = set() # Track unique thresholds to avoid duplicates + # Get all thresholds once (generator, so materialize it to a list) + all_db_thresholds = list(get_thresholds(term=term)) + # Process each context set for context in contexts: context_l = context.split("|") if context != 'none' else [] - # Get all thresholds for the term and filter by this context - for threshold in get_thresholds(term=term): - if threshold[3]: # if threshold has contexts - if set(context_l).issubset(set(threshold[3])): - # Create unique key to avoid duplicates - unique_key = (threshold[0], threshold[1], threshold[2]) # threshold_name, term_name, location_name - if unique_key not in seen: - seen.add(unique_key) - all_thresholds.append(threshold._asdict()) + # Filter thresholds by this context + for threshold in all_db_thresholds: + threshold_contexts = threshold[3] if threshold[3] else [] + + # If no context filter, or if context filter matches + if not context_l or set(context_l).issubset(set(threshold_contexts)): + # Create unique key to avoid duplicates + unique_key = (threshold[0], threshold[1], threshold[2]) # threshold_name, term_name, location_name + if unique_key not in seen: + seen.add(unique_key) + all_thresholds.append(threshold._asdict()) threshold_data = {'thresholds': all_thresholds} json_data = json.dumps(threshold_data, indent=4, sort_keys=True, default=str) From d58e46b51fbcfb5c90378f63d1316ee89d4e91f6 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 3 Feb 2026 16:00:54 -0700 Subject: [PATCH 06/46] req update for docker image --- modules/threshold_loader/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/threshold_loader/requirements.txt b/modules/threshold_loader/requirements.txt index 166046be7..341e614b1 100644 --- a/modules/threshold_loader/requirements.txt +++ b/modules/threshold_loader/requirements.txt @@ -1,2 +1,3 @@ -environs==6.0.0 +environs==9.5.0 +marshmallow>=3.18.0 structlog==21.5.0 \ No newline at end of file From 49bd86a0376382557b479ec56beac2f6911dd1a0 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 3 Feb 2026 16:02:48 -0700 Subject: [PATCH 07/46] updating requirements to stay with older version (for now) Could update to > for marshmallow and increase environs. --- modules/threshold_loader/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/threshold_loader/requirements.txt b/modules/threshold_loader/requirements.txt index 341e614b1..5b5dc0ccc 100644 --- a/modules/threshold_loader/requirements.txt +++ b/modules/threshold_loader/requirements.txt @@ -1,3 +1,3 @@ -environs==9.5.0 -marshmallow>=3.18.0 +environs==6.0.0 +marshmallow<3.18.0 structlog==21.5.0 \ No newline at end of file From f9a3091a5cc2dd05ae6ba84f2f9c10b67748f758 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 3 Feb 2026 17:41:13 -0700 Subject: [PATCH 08/46] too much filtering in deduplication checks. also was not doing an exact context match. --- modules/threshold_loader/threshold_loader.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/threshold_loader/threshold_loader.py b/modules/threshold_loader/threshold_loader.py index 67ccf6afc..0909f109a 100644 --- a/modules/threshold_loader/threshold_loader.py +++ b/modules/threshold_loader/threshold_loader.py @@ -33,10 +33,20 @@ def load_thresholds(get_thresholds: Callable[[str], Iterator[Threshold]], out_pa for threshold in all_db_thresholds: threshold_contexts = threshold[3] if threshold[3] else [] - # If no context filter, or if context filter matches - if not context_l or set(context_l).issubset(set(threshold_contexts)): - # Create unique key to avoid duplicates - unique_key = (threshold[0], threshold[1], threshold[2]) # threshold_name, term_name, location_name + # Exact context match: threshold must have exactly these contexts (no more, no less) + # If no context filter (empty list), only match thresholds with no contexts + if set(context_l) == set(threshold_contexts): + # Create unique key including dates to avoid losing seasonal thresholds + # threshold[4]=start_date, threshold[5]=end_date, threshold[7]=start_day_of_year, threshold[8]=end_day_of_year + unique_key = ( + threshold[0], # threshold_name + threshold[1], # term_name + threshold[2], # location_name + threshold[4], # start_date + threshold[5], # end_date + threshold[7], # start_day_of_year + threshold[8] # end_day_of_year + ) if unique_key not in seen: seen.add(unique_key) all_thresholds.append(threshold._asdict()) From 775f6ec60600c1f0884da05ba2f533a4a8471a09 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 4 Feb 2026 16:13:16 -0700 Subject: [PATCH 09/46] removing indexing in deduplication checks --- modules/threshold_loader/threshold_loader.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/threshold_loader/threshold_loader.py b/modules/threshold_loader/threshold_loader.py index 0909f109a..9402c2128 100644 --- a/modules/threshold_loader/threshold_loader.py +++ b/modules/threshold_loader/threshold_loader.py @@ -31,21 +31,20 @@ def load_thresholds(get_thresholds: Callable[[str], Iterator[Threshold]], out_pa # Filter thresholds by this context for threshold in all_db_thresholds: - threshold_contexts = threshold[3] if threshold[3] else [] + threshold_contexts = threshold.context if threshold.context else [] # Exact context match: threshold must have exactly these contexts (no more, no less) # If no context filter (empty list), only match thresholds with no contexts if set(context_l) == set(threshold_contexts): # Create unique key including dates to avoid losing seasonal thresholds - # threshold[4]=start_date, threshold[5]=end_date, threshold[7]=start_day_of_year, threshold[8]=end_day_of_year unique_key = ( - threshold[0], # threshold_name - threshold[1], # term_name - threshold[2], # location_name - threshold[4], # start_date - threshold[5], # end_date - threshold[7], # start_day_of_year - threshold[8] # end_day_of_year + threshold.threshold_name, + threshold.term_name, + threshold.location_name, + threshold.start_date, + threshold.end_date, + threshold.start_day_of_year, + threshold.end_day_of_year ) if unique_key not in seen: seen.add(unique_key) From e728cde602bea8e334634b991fcbc7fcaa85e887 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 9 Feb 2026 12:34:29 -0700 Subject: [PATCH 10/46] req update and testing to see if uuid works for the unique key --- modules/threshold_loader/requirements.txt | 4 ++-- modules/threshold_loader/threshold_loader.py | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/threshold_loader/requirements.txt b/modules/threshold_loader/requirements.txt index 5b5dc0ccc..52c9e0845 100644 --- a/modules/threshold_loader/requirements.txt +++ b/modules/threshold_loader/requirements.txt @@ -1,3 +1,3 @@ -environs==6.0.0 -marshmallow<3.18.0 +environs==11.0.0 +marshmallow==3.21.3 structlog==21.5.0 \ No newline at end of file diff --git a/modules/threshold_loader/threshold_loader.py b/modules/threshold_loader/threshold_loader.py index 9402c2128..762817b4d 100644 --- a/modules/threshold_loader/threshold_loader.py +++ b/modules/threshold_loader/threshold_loader.py @@ -38,13 +38,7 @@ def load_thresholds(get_thresholds: Callable[[str], Iterator[Threshold]], out_pa if set(context_l) == set(threshold_contexts): # Create unique key including dates to avoid losing seasonal thresholds unique_key = ( - threshold.threshold_name, - threshold.term_name, - threshold.location_name, - threshold.start_date, - threshold.end_date, - threshold.start_day_of_year, - threshold.end_day_of_year + threshold.threshold_uuid ) if unique_key not in seen: seen.add(unique_key) From efbad02506a1b59c4fe18500b639f349bffc7072 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 9 Feb 2026 15:26:46 -0700 Subject: [PATCH 11/46] changing back key --- modules/threshold_loader/threshold_loader.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/threshold_loader/threshold_loader.py b/modules/threshold_loader/threshold_loader.py index 762817b4d..9402c2128 100644 --- a/modules/threshold_loader/threshold_loader.py +++ b/modules/threshold_loader/threshold_loader.py @@ -38,7 +38,13 @@ def load_thresholds(get_thresholds: Callable[[str], Iterator[Threshold]], out_pa if set(context_l) == set(threshold_contexts): # Create unique key including dates to avoid losing seasonal thresholds unique_key = ( - threshold.threshold_uuid + threshold.threshold_name, + threshold.term_name, + threshold.location_name, + threshold.start_date, + threshold.end_date, + threshold.start_day_of_year, + threshold.end_day_of_year ) if unique_key not in seen: seen.add(unique_key) From c949a137313d6800532412d53d39d718c2f33ef9 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 9 Feb 2026 16:10:20 -0700 Subject: [PATCH 12/46] testing thresh_select_ts_pad --- .../concH2oSoilSalinity_group_path.yaml | 6 +- ...cH2oSoilSalinity_thresh_select_ts_pad.yaml | 90 +++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml index 69e957684..b83592a0d 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_group_path.yaml @@ -37,19 +37,19 @@ input: # name must be GROUP_ASSIGNMENT_PATH name: GROUP_ASSIGNMENT_PATH repo: concH2oSoilSalinity_group_assignment - glob: /conc-h2o-soil-salinity/(2025/10/18) + glob: /conc-h2o-soil-salinity/(2025/10/(16|17|18)) joinOn: $1 outer_join: True - pfs: name: LOCATION_FOCUS_PATH repo: enviroscan_fill_date_gaps_and_regularize - glob: /*/(2025/10/18) + glob: /*/(2025/10/(16|17|18)) joinOn: $1 outer_join: True - pfs: name: GROUP_FOCUS_PATH repo: tempSoil_level1_group_consolidate_srf - glob: /(2025/10/18) + glob: /(2025/10/(16|17|18)) joinOn: $1 outer_join: True parallelism_spec: diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml new file mode 100644 index 000000000..0362a7fea --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml @@ -0,0 +1,90 @@ +--- +pipeline: + name: concH2oSoilSalinity_thresh_select_ts_pad +transform: + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-thsh-slct-ts-pad:v2.2.0 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ + set -euo pipefail + IFS=$'\n\t' + # Refresh interim directories with each datum (otherwise they persist and cause probs) + rm -rf /tmp/threshold_select + rm -rf /tmp/threshold_selectCopy + mkdir -p /tmp/threshold_select + # Run first module - threshold_select + Rscript ./flow.thsh.slct.R \ + DirIn=$REPO_LOCATIONS \ + DirOut=/tmp/threshold_select \ + DirErr=/pfs/out/errored_datums \ + FileThsh=$FILE_THRESHOLDS \ + "TermCtxt1=VSICDepth02|soil|ion-content" \ + "TermCtxt2=VSICDepth03|soil|ion-content" \ + "TermCtxt3=VSICDepth04|soil|ion-content" \ + "TermCtxt4=VSICDepth05|soil|ion-content" \ + "TermCtxt5=VSICDepth06|soil|ion-content" \ + "TermCtxt6=VSICDepth07|soil|ion-content" \ + "TermCtxt7=VSICDepth08|soil|ion-content" \ + "TermCtxt8=VSICDepth01|soil|ion-content" \ + "TermCtxt9=VSWCfactoryDepth02|soil|water-content|factory" \ + "TermCtxt10=VSWCfactoryDepth03|soil|water-content|factory" \ + "TermCtxt11=VSWCfactoryDepth04|soil|water-content|factory" \ + "TermCtxt12=VSWCfactoryDepth05|soil|water-content|factory" \ + "TermCtxt13=VSWCfactoryDepth06|soil|water-content|factory" \ + "TermCtxt14=VSWCfactoryDepth07|soil|water-content|factory" \ + "TermCtxt15=VSWCfactoryDepth08|soil|water-content|factory" \ + "TermCtxt16=VSWCfactoryDepth01|soil|water-content|factory" \ + "TermCtxt17=VSWCsoilSpecificDepth02|soil|water-content|soil-specific" \ + "TermCtxt18=VSWCsoilSpecificDepth03|soil|water-content|soil-specific" \ + "TermCtxt19=VSWCsoilSpecificDepth04|soil|water-content|soil-specific" \ + "TermCtxt20=VSWCsoilSpecificDepth05|soil|water-content|soil-specific" \ + "TermCtxt21=VSWCsoilSpecificDepth06|soil|water-content|soil-specific" \ + "TermCtxt22=VSWCsoilSpecificDepth07|soil|water-content|soil-specific" \ + "TermCtxt23=VSWCsoilSpecificDepth08|soil|water-content|soil-specific" \ + "TermCtxt24=VSWCsoilSpecificDepth01|soil|water-content|soil-specific" \ + "DirSubCopy=data|location" + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/threshold_select /tmp/threshold_selectCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/threshold_select + # Run second module - timeseries_padder + python3 -m timeseries_padder.timeseries_padder.variable_pad_main --yearindex 3 --monthindex 4 --dayindex 5 --locindex 8 --subdirindex 9 + EOF + env: + DATA_PATH: /tmp/threshold_selectCopy + OUT_PATH: /pfs/out + LOG_LEVEL: INFO + PAD_DIR: data + COPY_DIR: none # Can be multiple, separated by commas without spaces. Directories other than the pad directory to copy to the output + RELATIVE_PATH_INDEX: '3' + PARALLELIZATION_INTERNAL: '5' # For threshold select module +output_branch: master +input: + cross: + - pfs: + name: REPO_LOCATIONS + repo: concH2oSoilSalinity_group_path + glob: /*/*/* + - pfs: + name: FILE_THRESHOLDS + repo: concH2oSoilSalinity_threshold_test3 #test 2 + glob: /thresholds.json +parallelism_spec: + constant: 5 +autoscaling: true +resource_requests: + memory: 1.5G + cpu: 5.5 +resource_limits: + memory: 3G + cpu: 6.5 +sidecar_resource_requests: + memory: 4G + cpu: 1 +datum_set_spec: + number: 1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class From bf850a8a4c9d912f68e042e7309b579d479a55bc Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 10 Feb 2026 13:57:32 -0700 Subject: [PATCH 13/46] continued swc pipe development --- ...oilSalinity_analyze_pad_and_qaqc_plau.yaml | 99 +++++++++++++++++++ ...cH2oSoilSalinity_thresh_select_ts_pad.yaml | 2 +- .../concH2oSoilSalinity_threshold_test3.yaml | 40 ++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml new file mode 100644 index 000000000..82417982d --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml @@ -0,0 +1,99 @@ +--- +pipeline: + name: concH2oSoilSalinity_analyze_pad_and_qaqc_plau +transform: + image_pull_secrets: + - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-ts-pad-anls-qaqc-plau:v2.0.0 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + + # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ + set -euo pipefail + IFS=$'\n\t' + + # Refresh interim directories with each datum (otherwise they persist and cause probs) + rm -rf /tmp/pfs/padded_analyzer + rm -rf /tmp/pfs/padded_analyzerCopy + mkdir -p /tmp/pfs/padded_analyzer + + # Run first module - padded_timeseries_analyzer + python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main + + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/padded_analyzer + + # Run second module - qaqc plausibility + + Rscript ./flow.qaqc.plau.R \ + DirIn=/tmp/pfs/padded_analyzerCopy \ + DirOut=/pfs/out \ + DirErr=/pfs/out/errored_datums \ + "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "DirSubCopy=data|location|threshold" + + ###custom script will go here for temperature test + + EOF + env: + # Environment variables for padded timeseries analyzer + OUT_PATH: /tmp/pfs/padded_analyzer + LOG_LEVEL: INFO + RELATIVE_PATH_INDEX: '3' + ERR_PATH: /pfs/out/errored_datums + # Environment variables for qaqc plausibility + PARALLELIZATION_INTERNAL: '5' +input: + cross: + - pfs: + name: DATA_PATH + repo: concH2oSoilSalinity_thresh_select_ts_pad + glob: /*/*/* + # - pfs: + # name: SCHEMA_FLAGS + # repo: concH2oSoilSalinity_avro_schemas + # glob: /concH2oSoilSalinity/flags_plausibility_concH2oSoilSalinity.avsc +parallelism_spec: + constant: 5 +autoscaling: true +resource_requests: + memory: 2G + cpu: 5.5 +resource_limits: + memory: 4G + cpu: 7 +sidecar_resource_requests: + memory: 3G + cpu: 0.3 +datum_set_spec: + number: 1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml index 0362a7fea..fa2ad77c4 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml @@ -66,7 +66,7 @@ input: - pfs: name: REPO_LOCATIONS repo: concH2oSoilSalinity_group_path - glob: /*/*/* + glob: /*/*/*/*/enviroscan - pfs: name: FILE_THRESHOLDS repo: concH2oSoilSalinity_threshold_test3 #test 2 diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml new file mode 100644 index 000000000..5324acc6a --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml @@ -0,0 +1,40 @@ +--- +pipeline: + name: concH2oSoilSalinity_threshold_test3 +transform: + # image_pull_secrets: + # - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:sha-efbad02 + cmd: + - /bin/bash + stdin: + - '#!/bin/bash' + - python3 -m threshold_loader.threshold_loader_main + env: + OUT_PATH: /pfs/out + LOG_LEVEL: INFO + TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 + CTXT_1: soil|ion-content + CTXT_2: soil|water-content|factory + CTXT_3: soil|water-content|soil-specific + secrets: + - name: pdr-secret + mount_path: /var/db_secret +input: + pfs: + repo: enviroscan_cron_daily_and_date_control_tick + glob: /* + empty_files: true +autoscaling: true +resource_requests: + memory: 32M + cpu: 0.05 +resource_limits: + memory: 200M + cpu: 0.5 +sidecar_resource_requests: + memory: 120M + cpu: 0.1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class \ No newline at end of file From 055c7c58fd739d9f9e5ab84b39ce2a1603bf536c Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 17 Feb 2026 13:52:56 -0700 Subject: [PATCH 14/46] testing out new flow module to run temperature test on enviroscans --- .../def.apply.temp.flags.R | 84 +++ .../def.calc.temp.flags.R | 136 +++++ .../def.find.temp.sensor.R | 90 +++ .../def.load.temp.sensors.R | 114 ++++ .../flow.envscn.temp.flags/def.sort.qf.cols.R | 84 +++ .../flow.envscn.temp.flags.R | 213 +++++++ .../wrap.envscn.temp.flags.R | 281 +++++++++ .../wrap.envscn.temp.flags_messy.R | 537 ++++++++++++++++++ ...cH2oSoilSalinity_thresh_select_ts_pad.yaml | 6 +- 9 files changed, 1542 insertions(+), 3 deletions(-) create mode 100644 flow/flow.envscn.temp.flags/def.apply.temp.flags.R create mode 100644 flow/flow.envscn.temp.flags/def.calc.temp.flags.R create mode 100644 flow/flow.envscn.temp.flags/def.find.temp.sensor.R create mode 100644 flow/flow.envscn.temp.flags/def.load.temp.sensors.R create mode 100644 flow/flow.envscn.temp.flags/def.sort.qf.cols.R create mode 100644 flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R create mode 100644 flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R create mode 100644 flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R diff --git a/flow/flow.envscn.temp.flags/def.apply.temp.flags.R b/flow/flow.envscn.temp.flags/def.apply.temp.flags.R new file mode 100644 index 000000000..5836189b6 --- /dev/null +++ b/flow/flow.envscn.temp.flags/def.apply.temp.flags.R @@ -0,0 +1,84 @@ +############################################################################################## +#' @title Apply temperature flags to high-frequency data + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} + +#' @description +#' Join minute-interval temperature flags to high-frequency (e.g., 10-second) soil moisture data +#' using time-based overlap matching. + +#' @param dataSm Data frame containing soil moisture data with readout_time column +#' @param tempData Data frame with temperature flags (startDateTime, endDateTime, temp_flag) +#' @param qfColName Character. Name of the QF column to update in dataSm +#' @param log A logger object. Defaults to NULL. + +#' @return Updated dataSm data frame with qfColName column populated with temperature flags + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @seealso Currently none + +#' @export + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-17) +#' original creation +############################################################################################## +def.apply.temp.flags <- function(dataSm, + tempData, + qfColName, + log = NULL) { + + # Initialize log if not provided + if (base::is.null(log)) { + log <- NEONprocIS.base::def.log.init() + } + + # Convert to data.table for efficient joins + dtSm <- data.table::as.data.table(dataSm) + dtTemp <- data.table::as.data.table(tempData)[, .(startDateTime, endDateTime, temp_flag)] + + # Ensure proper datetime format + dtSm[, `:=`( + readout_start = base::as.POSIXct(readout_time, tz = "UTC"), + readout_end = base::as.POSIXct(readout_time, tz = "UTC") + )] + + dtTemp[, `:=`( + startDateTime = base::as.POSIXct(startDateTime, tz = "UTC"), + endDateTime = base::as.POSIXct(endDateTime, tz = "UTC"), + temp_flag = base::as.integer(temp_flag) + )] + + # Set key for foverlaps (required on the interval table) + data.table::setkey(dtTemp, startDateTime, endDateTime) + + # Perform overlap join - find which minute interval each point falls within + joined <- data.table::foverlaps( + x = dtSm[, .(readout_start, readout_end, .rows = .I)], + y = dtTemp[, .(startDateTime, endDateTime, temp_flag)], + by.x = c("readout_start", "readout_end"), + by.y = c("startDateTime", "endDateTime"), + type = "within", # point must fall within interval + nomatch = NA_integer_ + ) + + # Get current QF values + newQf <- dataSm[[qfColName]] + + # Update QF values where we found a matching interval + hasMatch <- !base::is.na(joined$temp_flag) + newQf[joined$.rows[hasMatch]] <- joined$temp_flag[hasMatch] + + # Assign back to original data frame + dataSm[[qfColName]] <- newQf + + numMatched <- base::sum(hasMatch) + numTotal <- base::nrow(dataSm) + log$debug(base::paste0('Applied temperature flags: ', numMatched, ' / ', numTotal, + ' rows matched (', base::round(100 * numMatched / numTotal, 1), '%)')) + + return(dataSm) +} diff --git a/flow/flow.envscn.temp.flags/def.calc.temp.flags.R b/flow/flow.envscn.temp.flags/def.calc.temp.flags.R new file mode 100644 index 000000000..b0a890421 --- /dev/null +++ b/flow/flow.envscn.temp.flags/def.calc.temp.flags.R @@ -0,0 +1,136 @@ +############################################################################################## +#' @title Calculate temperature test flags for soil moisture data + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} + +#' @description +#' Calculate temperature-based quality flags by comparing soil temperature to uncertainty. +#' If primary sensor is flagged, uses average of neighboring sensors. + +#' @param sensorInfo List containing 'closest' sensor and 'neighbors' list (from def.find.temp.sensor) +#' @param log A logger object. Defaults to NULL. + +#' @return Data frame with columns: +#' \describe{ +#' \item{startDateTime}{Start time of measurement interval} +#' \item{endDateTime}{End time of measurement interval} +#' \item{temp_flag}{Flag value: 0=pass, 1=fail, -1=test not run} +#' } + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @seealso Currently none + +#' @export + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-17) +#' original creation +############################################################################################## +def.calc.temp.flags <- function(sensorInfo, + log = NULL) { + + # Initialize log if not provided + if (base::is.null(log)) { + log <- NEONprocIS.base::def.log.init() + } + + # Constants + QF_PASS <- 0L + QF_FAIL <- 1L + QF_NA <- -1L + TEMP_DIFF_THRESHOLD <- 1 # degrees + + # Read primary sensor data + closestSensor <- sensorInfo$closest + tempDataClose <- NEONprocIS.base::def.read.parq(closestSensor$data_path) + + # Initialize output data frame + tempData <- tempDataClose[, c('startDateTime', 'endDateTime')] + tempData$temp_flag <- NA_integer_ + + # Check if primary sensor has good data (finalQF == 0) + idxGood <- !base::is.na(tempDataClose$finalQF) & tempDataClose$finalQF == QF_PASS + + if (base::all(idxGood)) { + # All primary sensor data is good - use it directly + log$debug(base::paste0('Using primary sensor ', closestSensor$sensor_id, ' (all data good)')) + tempData$temp_flag <- base::as.integer( + tempDataClose$soilTempMean < tempDataClose$soilTempExpUncert + ) + + } else { + # Some primary data is flagged - need to use neighbors for those intervals + log$debug(base::paste0('Primary sensor has flagged data, attempting neighbor average')) + + # Use primary sensor where data is good + tempData$temp_flag[idxGood] <- base::as.integer( + tempDataClose$soilTempMean[idxGood] < tempDataClose$soilTempExpUncert[idxGood] + ) + + # Try to use neighbor average for flagged intervals + nextHigher <- sensorInfo$neighbors$higher + nextLower <- sensorInfo$neighbors$lower + + if (!base::is.null(nextHigher) && !base::is.null(nextLower)) { + # Read neighbor data + tempDataHigher <- NEONprocIS.base::def.read.parq(nextHigher$data_path) + tempDataLower <- NEONprocIS.base::def.read.parq(nextLower$data_path) + + # Filter for good data only (finalQF < 1) + tempDataHigher <- tempDataHigher[tempDataHigher$finalQF < 1, ] + tempDataLower <- tempDataLower[tempDataLower$finalQF < 1, ] + + # Calculate test statistic for each neighbor + tempDataLower$zeroCheckLow <- tempDataLower$soilTempMean - tempDataLower$soilTempExpUncert + tempDataHigher$zeroCheckHigh <- tempDataHigher$soilTempMean - tempDataHigher$soilTempExpUncert + + # Join neighbor data + tempDataJoin <- base::merge( + tempDataLower[, c("startDateTime", "endDateTime", "zeroCheckLow")], + tempDataHigher[, c("startDateTime", "endDateTime", "zeroCheckHigh")], + by = c("startDateTime", "endDateTime"), + all = TRUE + ) + + # Calculate average of neighbor checks + tempDataJoin$avgZeroCheck <- base::rowMeans( + base::cbind(tempDataJoin$zeroCheckLow, tempDataJoin$zeroCheckHigh), + na.rm = TRUE + ) + tempDataJoin$avgZeroCheck[base::is.nan(tempDataJoin$avgZeroCheck)] <- NA_real_ + + # Test if average is less than threshold + tempDataJoin$zeroCheck <- base::ifelse( + base::is.na(tempDataJoin$avgZeroCheck), + NA_integer_, + base::as.integer(tempDataJoin$avgZeroCheck < TEMP_DIFF_THRESHOLD) + ) + + # Merge with primary data and fill in gaps + tempData <- base::merge( + tempData, + tempDataJoin[, c("startDateTime", "endDateTime", "zeroCheck")], + by = c("startDateTime", "endDateTime"), + all.x = TRUE + ) + + # Use neighbor check where primary flag is NA + idxNeedNeighbor <- base::is.na(tempData$temp_flag) + tempData$temp_flag[idxNeedNeighbor] <- tempData$zeroCheck[idxNeedNeighbor] + tempData$zeroCheck <- NULL + + log$debug(base::paste0('Filled ', base::sum(idxNeedNeighbor & !base::is.na(tempData$temp_flag)), + ' intervals using neighbor average')) + } else { + log$warn('Insufficient neighbor sensors to calculate backup flags') + } + } + + # Set remaining NA values to -1 (test could not be run) + tempData$temp_flag[base::is.na(tempData$temp_flag)] <- QF_NA + + return(tempData) +} diff --git a/flow/flow.envscn.temp.flags/def.find.temp.sensor.R b/flow/flow.envscn.temp.flags/def.find.temp.sensor.R new file mode 100644 index 000000000..1ed198d2c --- /dev/null +++ b/flow/flow.envscn.temp.flags/def.find.temp.sensor.R @@ -0,0 +1,90 @@ +############################################################################################## +#' @title Find closest temperature sensor to target depth + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} + +#' @description +#' Identify the temperature sensor with depth closest to the target depth. +#' In case of tie, prefers shallower (less negative/more positive) depth. + +#' @param targetDepth Numeric value. Target depth in meters (negative = below surface) +#' @param sensorDepthDf Data frame with columns sensor_id, depth_m, data_path, location_path +#' @param log A logger object. Defaults to NULL. + +#' @return A list with two elements: +#' \describe{ +#' \item{closest}{Data frame row for the closest sensor} +#' \item{neighbors}{List with elements 'higher' and 'lower' containing neighbor sensor info} +#' } + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @seealso Currently none + +#' @export + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-17) +#' original creation +############################################################################################## +def.find.temp.sensor <- function(targetDepth, + sensorDepthDf, + log = NULL) { + + # Initialize log if not provided + if (base::is.null(log)) { + log <- NEONprocIS.base::def.log.init() + } + + # Filter out sensors with missing depths + validSensors <- sensorDepthDf[!base::is.na(sensorDepthDf$depth_m), ] + + if (base::nrow(validSensors) == 0) { + log$error('No valid temperature sensors with depth information') + stop() + } + + # Calculate absolute difference from target depth + validSensors$abs_diff <- base::abs(validSensors$depth_m - targetDepth) + + # Sort by: 1) smallest difference, 2) shallowest depth, 3) sensor_id (for stability) + validSensors <- validSensors[base::order(validSensors$abs_diff, + -validSensors$depth_m, + validSensors$sensor_id), ] + + # Select closest sensor + closestSensor <- validSensors[1, c("sensor_id", "depth_m", "data_path", "location_path")] + closestDepth <- closestSensor$depth_m + + # Find neighbor sensors (one shallower, one deeper) + # Shallower = greater depth_m (less negative) + higherSensors <- validSensors[validSensors$depth_m > closestDepth, ] + if (base::nrow(higherSensors) > 0) { + higherSensors <- higherSensors[base::order(higherSensors$depth_m, higherSensors$sensor_id), ] + nextHigher <- higherSensors[1, c("sensor_id", "depth_m", "data_path", "location_path")] + } else { + nextHigher <- NULL + } + + # Deeper = smaller depth_m (more negative) + lowerSensors <- validSensors[validSensors$depth_m < closestDepth, ] + if (base::nrow(lowerSensors) > 0) { + lowerSensors <- lowerSensors[base::order(-lowerSensors$depth_m, lowerSensors$sensor_id), ] + nextLower <- lowerSensors[1, c("sensor_id", "depth_m", "data_path", "location_path")] + } else { + nextLower <- NULL + } + + log$debug(base::paste0('Closest sensor to depth ', targetDepth, 'm: ', + closestSensor$sensor_id, ' (', closestDepth, 'm)')) + + return(base::list( + closest = closestSensor, + neighbors = base::list( + higher = nextHigher, + lower = nextLower + ) + )) +} diff --git a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R new file mode 100644 index 000000000..363b606e5 --- /dev/null +++ b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R @@ -0,0 +1,114 @@ +############################################################################################## +#' @title Load temperature sensor metadata and data paths + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} + +#' @description +#' Extract temperature sensor IDs, depths, and file paths from a directory structure. +#' Parses location files to extract z_offset (depth) and matches with corresponding data files. + +#' @param DirTemp Character value. Path to directory containing temperature sensor data with +#' subdirectories structured as temp-soil_SENSORID/data/ and temp-soil_SENSORID/location/ + +#' @param log A logger object as produced by NEONprocIS.base::def.log.init to produce structured log +#' output. Defaults to NULL. + +#' @return A data frame with columns: +#' \describe{ +#' \item{sensor_id}{Sensor identifier (e.g., temp-soil_GRSM005501)} +#' \item{depth_m}{Sensor depth in meters (negative = below surface)} +#' \item{location_path}{Path to location JSON file} +#' \item{data_path}{Path to data parquet file} +#' } + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @seealso Currently none + +#' @export + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-17) +# original creation +############################################################################################## +def.load.temp.sensors <- function(DirTemp, + log = NULL) { + + # Initialize log if not provided + if (base::is.null(log)) { + log <- NEONprocIS.base::def.log.init() + } + + # Find all temperature sensor files + filesTemp <- base::list.files(DirTemp, full.names = TRUE, recursive = TRUE) + + # Filter for 1-minute data files and location JSON files + filesTempData <- filesTemp[base::grepl("/temp-soil_[^/]+/data/[^/]+001\\.parquet$", filesTemp)] + filesTempLocation <- filesTemp[base::grepl("/temp-soil_[^/]+/location/[^/]*_locations\\.json$", filesTemp)] + + if (base::length(filesTempData) == 0) { + log$warn(base::paste0('No temperature data files found in ', DirTemp)) + return(data.frame(sensor_id = character(), depth_m = numeric(), + location_path = character(), data_path = character())) + } + + if (base::length(filesTempLocation) == 0) { + log$error(base::paste0('No temperature location files found in ', DirTemp)) + stop() + } + + # Helper function to extract sensor ID from path + extract_sensor_id <- function(paths) { + matches <- base::regmatches(paths, base::regexpr("temp-soil_[^/]+", paths)) + return(matches) + } + + # Helper function to extract z_offset from location JSON + extract_z_offset <- function(path) { + base::tryCatch({ + locData <- jsonlite::fromJSON(path, simplifyVector = FALSE) + zOffset <- locData$features[[1]]$properties$locations$features[[1]]$properties$z_offset + return(base::as.numeric(zOffset)) + }, error = function(e) { + return(NA_real_) + }) + } + + # Build location metadata table + sensorIds <- extract_sensor_id(filesTempLocation) + depths <- base::vapply(filesTempLocation, extract_z_offset, numeric(1)) + + dfLocations <- data.frame( + sensor_id = sensorIds, + depth_m = depths, + location_path = filesTempLocation, + stringsAsFactors = FALSE + ) + dfLocations <- dfLocations[!base::duplicated(dfLocations$sensor_id), ] + + # Build data file table + dataIds <- extract_sensor_id(filesTempData) + dfData <- data.frame( + sensor_id = dataIds, + data_path = filesTempData, + stringsAsFactors = FALSE + ) + dfData <- dfData[!base::duplicated(dfData$sensor_id), ] + + # Join location and data information + sensorDepthDf <- base::merge(dfLocations, dfData, by = "sensor_id", all = FALSE) + + # Validate that all depths were successfully extracted + if (base::any(base::is.na(sensorDepthDf$depth_m))) { + failedSensors <- sensorDepthDf$sensor_id[base::is.na(sensorDepthDf$depth_m)] + log$error(base::paste0('Failed to extract depth from location files for: ', + base::paste(failedSensors, collapse = ', '))) + stop() + } + + log$info(base::paste0('Loaded ', base::nrow(sensorDepthDf), ' temperature sensors')) + + return(sensorDepthDf) +} diff --git a/flow/flow.envscn.temp.flags/def.sort.qf.cols.R b/flow/flow.envscn.temp.flags/def.sort.qf.cols.R new file mode 100644 index 000000000..4ed62ef09 --- /dev/null +++ b/flow/flow.envscn.temp.flags/def.sort.qf.cols.R @@ -0,0 +1,84 @@ +############################################################################################## +#' @title Sort quality flag columns in standard order + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} + +#' @description +#' Sort QF columns by depth, then test type, then variable type. +#' Order: readout_time, then for each depth (01-08): +#' VSIC/VSWCfactory/VSWCsoilSpecific x Null/Gap/Range/Step/Persistence/Spike, then tempTest + +#' @param cols Character vector of column names to sort + +#' @return Character vector of column names in sorted order + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @seealso Currently none + +#' @ export + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-17) +#' original creation +############################################################################################## +def.sort.qf.cols <- function(cols) { + + # Keep readout_time first + readoutCol <- cols[cols == "readout_time"] + + # Get all QF columns + qfCols <- cols[base::grepl("QF$", cols)] + + # Extract components for sorting + sortDf <- data.frame( + col = qfCols, + stringsAsFactors = FALSE + ) + + # Classify variable type + sortDf$varType <- base::ifelse( + base::grepl("^VSIC", sortDf$col), "1_VSIC", + base::ifelse( + base::grepl("^VSWCfactory", sortDf$col), "2_VSWCfactory", + base::ifelse( + base::grepl("^VSWCsoilSpecific", sortDf$col), "3_VSWCsoilSpecific", + "4_temp" + ) + ) + ) + + # Extract depth number + sortDf$depth <- base::as.numeric( + base::gsub(".*Depth(\\d+).*|.*depth(\\d+).*", "\\1\\2", sortDf$col) + ) + + # Classify QF test type + sortDf$qfType <- base::ifelse( + base::grepl("NullQF$", sortDf$col), "1_Null", + base::ifelse( + base::grepl("GapQF$", sortDf$col), "2_Gap", + base::ifelse( + base::grepl("RangeQF$", sortDf$col), "3_Range", + base::ifelse( + base::grepl("StepQF$", sortDf$col), "4_Step", + base::ifelse( + base::grepl("PersistenceQF$", sortDf$col), "5_Persistence", + base::ifelse( + base::grepl("SpikeQF$", sortDf$col), "6_Spike", + "7_tempTest" + ) + ) + ) + ) + ) + ) + + # Sort by depth, then test type, then variable type + sortDf <- sortDf[base::order(sortDf$depth, sortDf$qfType, sortDf$varType), ] + + # Combine readout_time first, then sorted QF columns + return(base::c(readoutCol, sortDf$col)) +} diff --git a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R new file mode 100644 index 000000000..898784429 --- /dev/null +++ b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R @@ -0,0 +1,213 @@ +############################################################################################## +#' @title Assess soil temperature closest to sensor depths to determine if data should be flagged. + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} \cr +#' +#' @description Workflow. Compute the heater and status flags by assessing the bit rate. Only +#' flagging alarm codes of interest. Add columns to qfPlau table prior to push to QM calculation module. +#' +#' +#' General code workflow: +#' Parse input parameters +#' Determine datums to process (set of files/folders to process as a single unit) +#' For each datum: +#' Create output directories and copy (by symbolic link) unmodified components +#' Read in the L0 data files into arrow datasets +#' Compute flags based on sensor status streams +#' aggregate data to 5 and 30 minutes +#' Write stats and flags output to file +#' +#' This script is run at the command line with the following arguments. Each argument must be a string +#' in the format "Para=value", where "Para" is the intended parameter name and "value" is the value of +#' the parameter. Note: If the "value" string begins with a $ (e.g. $DIR_IN), the value of the +#' parameter will be assigned from the system environment variable matching the value string. +#' +#' The arguments are: +#' +#' 1. "DirIn=value", where value is the path to the input data directory. +#' The input repo should be structured by source ID as follows: +#' #/pfs/BASE_REPO/#/yyyy/mm/dd/#/source-id, where # indicates any number of parent and child directories +#' of any name, so long as they are not 'pfs' or recognizable as the 'yyyy/mm/dd' structure which indicates +#' the 4-digit year, 2-digit month, and' 2-digit day. The source-id is the unique identifier of the sensor. \cr +#' +#' Nested within the path for each source ID is (at a minimum) the folder: +#' /data +#' /flags +#' The data/flags folders holds any number of daily data/flags files padded around the yyyy/mm/dd in the input path. +#' #' +#' For example: +#' Input path = precipWeighingv2_analyze_pad_and_qaqc_plau/2025/03/31/precip-weighing-v2_HQTW900000/pluvio/CFGLOC114405 +#' +#' There may be other folders at the same level as the data directory. They are ignored and not passed +#' to the output unless indicated in SubDirCopy. +#' +#' 2. "DirOut=value", where the value is the output path that will replace the #/pfs/BASE_REPO portion +#' of \code{DirIn}. +#' +#' 3. "DirErr=value", where the value is the output path to place the path structure of errored datums that will +#' replace the #/pfs/BASE_REPO portion of \code{DirIn}. +#' +#' 4. "SchmQF=value" (optional), where value is the full path to schema for the QF flags after inputing custom flags +#' +#' 5. "DirTemp=valuë", where the value is the input path for soil temperature and location data to perform test. +#' +#' Ensure that any schema input here matches the column order of the auto-generated schema, +#' simply making any desired changes to column names. +#' +#' Ensure that any schema input here matches the column order of the auto-generated schema, +#' simply making any desired changes to column names. +#' +#' 6. "DirSubCopy=value" (optional), where value is the names of additional subfolders, separated by +#' pipes, at the same level as the data folder that are to be copied with a +#' symbolic link to the output path. May NOT include 'data'. +#' +#' Note: This script implements logging described in \code{\link[NEONprocIS.base]{def.log.init}}, +#' which uses system environment variables if available. +#' +#' @return A repository with the computed precipitation and flags in DirOut, where DirOut replaces BASE_REPO but +#' otherwise retains the child directory structure of the input path. The terminal directories of each +#' sensor location folder are "data" and "flags". +#' +#' @references +#' License: (example) GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords Currently none + +#' @examples +#' Not Run - Stepping through the code in Rstudio +#' Sys.setenv(DIR_IN="DirIn=/scratch/pfs/concH2oSoilSalinity_analyze_pad_and_qaqc_plau/2025/10/17/conc-h2o-soil-salinity_GRSM001501/") +#' log <- NEONprocIS.base::def.log.init(Lvl = "debug") +#' arg = c( "DirIn=$DIR_IN", +#' "DirTemp=/scratch/pfs/concH2oSoilSalinity_group_path/2025/10/17/conc-h2o-soil-salinity_GRSM005501/", +#' "DirOut=/scratch/pfs/tb_out", +#' "DirErr=/scratch/pfs/tb_out/errored_datums") +#' Then copy and paste rest of workflow into the command window + +#' @seealso Currently none. + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-16) +# original creation +############################################################################################## +library(foreach) +library(doParallel) +#TODO uncomment! +# # Source the wrapper function and other dependency functions. Assume it is in the working directory +# source("./wrap.envscn.temp.flags.R") +# source("./def.apply.temp.flags.R") +# source("./def.find.temp.sensor.R") +# source("./def.load.temp.sensor.R") +# source("./def.sort.qf.cols.R") +# source("./def.calc.temp.flags.R") + +# # Pull in command line arguments (parameters) +# arg <- base::commandArgs(trailingOnly = TRUE) + +# Start logging +log <- NEONprocIS.base::def.log.init() + +# Use environment variable to specify how many cores to run on +numCoreUse <- base::as.numeric(Sys.getenv('PARALLELIZATION_INTERNAL')) +numCoreAvail <- parallel::detectCores() +if (base::is.na(numCoreUse)){ + numCoreUse <- 1 +} +if(numCoreUse > numCoreAvail){ + numCoreUse <- numCoreAvail +} +log$debug(paste0(numCoreUse, ' of ',numCoreAvail, ' available cores will be used for internal parallelization.')) + +# Parse the input arguments into parameters +Para <- + NEONprocIS.base::def.arg.pars( + arg = arg, + NameParaReqd = c( + "DirIn", + "DirOut", + "DirErr", + "DirTemp" + ), + NameParaOptn = c( + "DirSubCopy", + "SchmQf" + ), + log = log + ) + + +# Echo arguments +log$debug(base::paste0('Input directory: ', Para$DirIn)) +log$debug(base::paste0('Output directory: ', Para$DirOut)) +log$debug(base::paste0('Error directory: ', Para$DirErr)) +log$debug(base::paste0('Temperature directory: ', Para$DirTemp)) + +# Retrieve output schema for flags +FileSchmQf <- Para$SchmQf +log$debug(base::paste0('Output schema for bulk precipitation custom flags: ',base::paste0(FileSchmData,collapse=','))) + +# Read in the schema +if(base::is.null(FileSchmQf) || FileSchmQf == 'NA'){ + FileSchmQf <- NULL +} else { + FileSchmQf <- base::paste0(base::readLines(FileSchmQf),collapse='') +} + +# Retrieve optional subdirectories to copy over +DirSubCopy <- base::unique(Para$DirSubCopy) +log$debug(base::paste0( + 'Additional subdirectories to copy: ', + base::paste0(DirSubCopy, collapse = ',') +)) + +# What are the expected subdirectories of each input path +nameDirSub <- c('data','flags') +log$debug(base::paste0( + 'Minimum expected subdirectories of each datum path: ', + base::paste0(nameDirSub, collapse = ',') +)) + +# Find all the input paths (datums). We will process each one. +DirIn <- + NEONprocIS.base::def.dir.in(DirBgn = Para$DirIn, + nameDirSub = nameDirSub, + log = log) + + +# Process each datum path +doParallel::registerDoParallel(numCoreUse) +foreach::foreach(idxDirIn = DirIn) %dopar% { + log$info(base::paste0('Processing path to datum: ', idxDirIn)) + + # Run the wrapper function for each datum, with error routing + tryCatch( + withCallingHandlers( + wrap.envscn.temp.flags(DirIn=idxDirIn, + DirOutBase=Para$DirOut, + DirTemp=Para$DirTemp, + SchmQf=FileSchmQf, + DirSubCopy=DirSubCopy, + log=log + ), + error = function(err) { + call.stack <- base::sys.calls() # is like a traceback within "withCallingHandlers" + + # Re-route the failed datum + NEONprocIS.base::def.err.datm( + err=err, + call.stack=call.stack, + DirDatm=idxDirIn, + DirErrBase=Para$DirErr, + RmvDatmOut=TRUE, + DirOutBase=Para$DirOut, + log=log + ) + } + ), + # This simply to avoid returning the error + error=function(err) {} + ) + +return() + +} # End loop around datum paths diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R new file mode 100644 index 000000000..16164fb76 --- /dev/null +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -0,0 +1,281 @@ +############################################################################################## +#' @title Assess soil temperature closest to sensor depths to determine if data should be flagged. + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} \cr + +#' @description Workflow. Determine if test is to be run based on thresholds, find closests temperature sensor, +#' read in data and check if it is below freezing. If the closest sensor is flagged, do an average of the one above and one below +#' and check to see if the average of the temperatures are less than 1 degree. If one or both are unavailable, flag NA +#' +#' @param DirIn Character value. The input path to the data from a single source ID, structured as follows: +#' #/pfs/BASE_REPO/#/yyyy/mm/dd/#/location-id, where # indicates any number of parent and child directories +#' of any name, so long as they are not 'pfs' or recognizable as the 'yyyy/mm/dd' structure which indicates +#' the 4-digit year, 2-digit month, and' 2-digit day. The location-id is the unique identifier of the location. \cr +#' +#' Nested within this path are (at a minimum) the folders: +#' /data +#' /flags + +#' The flags folder holds two files containing basic plausibility and calibration quality flags for +#' the central processing day only. These files should respectively be named in the convention: +#' SOURCETYPE_LOCATIONID_YYYY-MM-DD_flagsPlausibility.parquet +#' All other files in this directory will be ignored. +#' +#' The threshold folder contains a single file named thresholds.json that holds threshold parameters +#' applicable to the smoothing algorithm. +#' +#' @param DirTemp Character value. The input path to the temperature data that is used to perform the the temperature test. +#' Location files are used to determine depth of the sensors and pair with the appropriate enviroscan depth. +#' Then the data is read in to perform tests. +#' +#' @param DirOutBase Character value. The output path that will replace the #/pfs/BASE_REPO portion of DirIn. +#' +#' @param SchmQf (Optional). A json-formatted character string containing the schema for the standard calibration and +#' plausibility QFs as well as the custom QFs tempTestDepth##QF + +#' @param DirSubCopy (optional) Character vector. The names of additional subfolders at +#' the same level as the data/flags/threshold folders in the input path that are to be copied with a +#' symbolic link to the output path (i.e. carried through as-is). Note that the 'stats' and 'flags' directories +#' are automatically populated in the output and cannot be included here. + +#' @param log A logger object as produced by NEONprocIS.base::def.log.init to produce structured log +#' output. Defaults to NULL, in which the logger will be created and used within the function. See NEONprocIS.base::def.log.init +#' for more details. +#' +#' @return A repository in DirOutBase containing the quality flags of the three different enviroscan data streams:" +#' VSIC, VSWCFactory and VSWCSoilSpecific. Standard flags are read in, then a test for frozen soil is performed. +#' +#' @references +#' License: (example) GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords Currently none + +#' @examples +#' # NOT RUN +#' +#' "DirIn=/scratch/pfs/concH2oSoilSalinity_analyze_pad_and_qaqc_plau/2025/10/17/conc-h2o-soil-salinity_GRSM001501/", +#' "DirTemp=/scratch/pfs/concH2oSoilSalinity_group_path/2025/10/17/conc-h2o-soil-salinity_GRSM005501/", +#' "DirOut=/scratch/pfs/tb_out", +#' "DirErr=/scratch/pfs/tb_out/errored_datums" +#' wrap.envscn.temp.flags(DirIn,DirOutBase,DirTemp,DirSubCopy) + +#' @seealso Currently none + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-16) +# Initial creation +############################################################################################## +wrap.envscn.temp.flags <- function(DirIn, + DirOutBase, + DirTemp, + SchmQf = NULL, + DirSubCopy = NULL, + log = NULL +){ + + # Start logging if not already initialized + if(base::is.null(log)){ + log <- NEONprocIS.base::def.log.init() + } + + # Gather info about the input directory and create the output directory. + InfoDirIn <- NEONprocIS.base::def.dir.splt.pach.time(DirIn,log=log) + dirInData <- fs::path(DirIn,'data') + dirInQf <- fs::path(DirIn,'flags') + + dirOut <- fs::path(DirOutBase,InfoDirIn$dirRepo) + dirOutQf <- fs::path(dirOut,'flags') + + #dirOutData <- fs::path(dirOut,'data') + NEONprocIS.base::def.dir.crea(DirBgn = dirOut, + DirSub = c('flags'), + log = log) + + # Copy with a symbolic link the desired subfolders + DirSubCopy <- base::unique(base::setdiff(DirSubCopy, c('flags'))) + if(base::length(DirSubCopy) > 0){ + NEONprocIS.base::def.dir.copy.symb(DirSrc = fs::path(DirIn, DirSubCopy), + DirDest = dirOut, + LnkSubObj = FALSE, + log = log) + } + + # ===== Load and validate thresholds ===== + dirInThrsh <- fs::path(DirIn, 'threshold') + fileThsh <- base::list.files(dirInThrsh) + + if (base::length(fileThsh) == 0) { + log$error(base::paste0('No threshold files found in ', dirInThrsh)) + stop() + } + + if (base::length(fileThsh) > 1) { + log$debug(base::paste0('Multiple threshold files found: ', base::paste(fileThsh, collapse = ', '))) + fileThsh <- fileThsh[1] + log$info(base::paste0('Using first threshold file: ', fileThsh)) + } + + thsh <- NEONprocIS.qaqc::def.read.thsh.qaqc.df( + NameFile = fs::path(dirInThrsh, fileThsh) + ) + + # Define expected terms and threshold names + termTest <- c("VSICDepth01", "VSICDepth02", "VSICDepth03", "VSICDepth04", + "VSICDepth05", "VSICDepth06", "VSICDepth07", "VSICDepth08") + tempTestName <- "tempTest" + + ############# + #dummy TODO delete after thresholds finalized + tempTestName <- "Range Threshold Soft Min" + thshSubset <- thsh[thsh$threshold_name == tempTestName & thsh$term_name %in% termTest, ] + thshSubset$threshold_name <- "tempTest" + + #replace with just thshSubset <- thsh[thsh$threshold_name == tempTestName & thsh$term_name %in% termTest, ] + + ############### + + # Filter thresholds for temperature test + + if (base::nrow(thshSubset) == 0) { + log$error(base::paste0('No "', tempTestName, '" thresholds found for terms: ', + base::paste(termTest, collapse = ', '))) + stop() + } + + # Create threshold lookup: term_name -> number_value + thresholdLookup <- stats::setNames(thshSubset$number_value, thshSubset$term_name) + + # Validate all expected terms have threshold values + missingTerms <- termTest[!termTest %in% base::names(thresholdLookup)] + if (base::length(missingTerms) > 0) { + log$error(base::paste0('Missing "', tempTestName, '" thresholds for: ', + base::paste(missingTerms, collapse = ', '))) + stop() + } + + # ===== Load enviroscan data ===== + fileData <- base::list.files(dirInData, pattern = '.parquet', full.names = FALSE) + + # Read the datasets + data <- NEONprocIS.base::def.read.parq.ds(fileIn=fs::path(dirInData,fileData), + VarTime='readout_time', + RmvDupl=TRUE, + Df=TRUE, + log=log) + + # Filter to soil moisture columns and essential metadata + soilCols <- base::names(data)[base::grepl("SoilMoisture", base::names(data))] + + keepStatic <- c("source_id", "site_id", "readout_time") + keepCols <- c(keepStatic, base::intersect(soilCols, base::names(data))) + dataSm <- data[, keepCols, drop = FALSE] + + # Extract depth keys (e.g., "01", "02", etc.) + depthKeys <- base::sub("^depth(\\d{2})SoilMoisture$", "\\1", soilCols) + + # Initialize temperature test QF columns (default: -1 = test not run) + qfCols <- base::paste0("tempTestDepth", depthKeys, "QF") + dataSm[qfCols] <- -1L + + # ===== Map thresholds to data column names ===== + # Convert threshold names from "VSICDepth02" to "depth02SoilMoisture" + base::names(thresholdLookup) <- base::sub( + "^VSICDepth(\\d{2})$", + "depth\\1SoilMoisture", + base::names(thresholdLookup) + ) + + # ===== Load temperature sensor metadata ===== + sensorDepthDf <- def.load.temp.sensors(DirTemp = DirTemp, log = log) + + # ===== Apply temperature test to each depth ===== + for (col in soilCols) { + + depthNum <- base::sub("^depth(\\d{2}).*$", "\\1", col) + qfName <- base::paste0("tempTestDepth", depthNum, "QF") + + # Check if test should be run (threshold value: 0 = skip, 1 = run) + if (thresholdLookup[[col]] == 0) { + log$info(base::paste0('Skipping temperature test for ', col, ' (threshold = 0)')) + dataSm[[qfName]] <- 0L # Automatic pass + + } else if (thresholdLookup[[col]] == 1) { + log$debug(base::paste0('Running temperature test for ', col)) + + # Find target depth from maximum sensor depth in the data + targetDepth <- base::max(dataSm[[col]], na.rm = TRUE) + + # Find closest temperature sensor + sensorInfo <- def.find.temp.sensor( + targetDepth = targetDepth, + sensorDepthDf = sensorDepthDf, + log = log + ) + + # Calculate temperature flags + tempData <- def.calc.temp.flags( + sensorInfo = sensorInfo, + log = log + ) + + # Apply flags to high-frequency data + dataSm <- def.apply.temp.flags( + dataSm = dataSm, + tempData = tempData, + qfColName = qfName, + log = log + ) + } + } + + # ===== Merge with existing plausibility flags ===== + qfFlags <- dataSm[, c("readout_time", qfCols)] + + fileQfPlau <- base::list.files(dirInQf, pattern = 'Plausibility.parquet', full.names = FALSE) + + if (base::length(fileQfPlau) == 0) { + log$error(base::paste0('No plausibility flag files found in ', dirInQf)) + stop() + } + + qfPlau <- NEONprocIS.base::def.read.parq.ds( + fileIn = fs::path(dirInQf, fileQfPlau), + VarTime = 'readout_time', + RmvDupl = TRUE, + Df = TRUE, + log = log + ) + + # Combine plausibility flags with temperature test flags + qfAll <- base::merge(qfPlau, qfFlags, by = "readout_time", all = TRUE) + + # ===== Sort columns in standard order ===== + sortedCols <- def.sort.qf.cols(base::names(qfAll)) + qfAll <- qfAll[, sortedCols] + + # ===== Write output ===== + nameFileQfOut <- fs::path(dirOutQf, fileQfPlau) + + rptWrite <- base::try( + NEONprocIS.base::def.wrte.parq( + data = qfAll, + NameFile = nameFileQfOut, + log = log + ), + silent = TRUE + ) + + if ('try-error' %in% base::class(rptWrite)) { + log$error(base::paste0( + 'Cannot write output to ', nameFileQfOut, '. ', + base::attr(rptWrite, "condition") + )) + stop() + } else { + log$info(base::paste0('Wrote updated flags to ', nameFileQfOut)) + } + + return() +} + diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R new file mode 100644 index 000000000..7135305ff --- /dev/null +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R @@ -0,0 +1,537 @@ +############################################################################################## +#' @title Assess soil temperature closest to sensor depths to determine if data should be flagged. + +#' @author +#' Teresa Burlingame \email{tburlingame@battelleecology.org} \cr + +#' @description Workflow. Determine if test is to be run based on thresholds, find closests temperature sensor, +#' read in data and check if it is below freezing. If the closest sensor is flagged, do an average of the one above and one below +#' and check to see if the average of the temperatures are less than 1 degree. If one or both are unavailable, flag NA +#' +#' @param DirIn Character value. The input path to the data from a single source ID, structured as follows: +#' #/pfs/BASE_REPO/#/yyyy/mm/dd/#/location-id, where # indicates any number of parent and child directories +#' of any name, so long as they are not 'pfs' or recognizable as the 'yyyy/mm/dd' structure which indicates +#' the 4-digit year, 2-digit month, and' 2-digit day. The location-id is the unique identifier of the location. \cr +#' +#' Nested within this path are (at a minimum) the folders: +#' /data +#' /flags + +#' The flags folder holds two files containing basic plausibility and calibration quality flags for +#' the central processing day only. These files should respectively be named in the convention: +#' SOURCETYPE_LOCATIONID_YYYY-MM-DD_flagsPlausibility.parquet +#' All other files in this directory will be ignored. +#' +#' The threshold folder contains a single file named thresholds.json that holds threshold parameters +#' applicable to the smoothing algorithm. +#' +#' @param DirTemp Character value. The input path to the temperature data that is used to perform the the temperature test. +#' Location files are used to determine depth of the sensors and pair with the appropriate enviroscan depth. +#' Then the data is read in to perform tests. +#' +#' @param DirOutBase Character value. The output path that will replace the #/pfs/BASE_REPO portion of DirIn. +#' +#' @param SchmQf (Optional). A json-formatted character string containing the schema for the standard calibration and +#' plausibility QFs as well as the custom QFs tempTestDepth##QF + +#' @param DirSubCopy (optional) Character vector. The names of additional subfolders at +#' the same level as the data/flags/threshold folders in the input path that are to be copied with a +#' symbolic link to the output path (i.e. carried through as-is). Note that the 'stats' and 'flags' directories +#' are automatically populated in the output and cannot be included here. + +#' @param log A logger object as produced by NEONprocIS.base::def.log.init to produce structured log +#' output. Defaults to NULL, in which the logger will be created and used within the function. See NEONprocIS.base::def.log.init +#' for more details. +#' +#' @return A repository in DirOutBase containing the quality flags of the three different enviroscan data streams:" +#' VSIC, VSWCFactory and VSWCSoilSpecific. Standard flags are read in, then a test for frozen soil is performed. +#' +#' @references +#' License: (example) GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords Currently none + +#' @examples +#' # NOT RUN +#' +#' "DirIn=/scratch/pfs/concH2oSoilSalinity_analyze_pad_and_qaqc_plau/2025/10/17/conc-h2o-soil-salinity_GRSM001501/", +#' "DirTemp=/scratch/pfs/concH2oSoilSalinity_group_path/2025/10/17/conc-h2o-soil-salinity_GRSM005501/", +#' "DirOut=/scratch/pfs/tb_out", +#' "DirErr=/scratch/pfs/tb_out/errored_datums" +#' wrap.envscn.temp.flags(DirIn,DirOutBase,DirTemp,DirSubCopy) + +#' @seealso Currently none + +# changelog and author contributions / copyrights +# Teresa Burlingame (2025-02-16) +# Initial creation +############################################################################################## +wrap.envscn.temp.flags<- function(DirIn, + DirOutBase, + DirTemp, + SchmQf=NULL, + DirSubCopy=NULL, + log=NULL +){ + + library(tibble) + library(dplyr) + library(purrr) + library(tidyr) + library(data.table) + + if(base::is.null(log)){ + log <- NEONprocIS.base::def.log.init() + } + + # Gather info about the input directory and create the output directory. + InfoDirIn <- NEONprocIS.base::def.dir.splt.pach.time(DirIn,log=log) + dirInData <- fs::path(DirIn,'data') + dirInQf <- fs::path(DirIn,'flags') + + dirOut <- fs::path(DirOutBase,InfoDirIn$dirRepo) + dirOutQf <- fs::path(dirOut,'flags') + + #dirOutData <- fs::path(dirOut,'data') + NEONprocIS.base::def.dir.crea(DirBgn = dirOut, + DirSub = c('flags'), + log = log) + + # Copy with a symbolic link the desired subfolders + DirSubCopy <- base::unique(base::setdiff(DirSubCopy,c('flags'))) + if(base::length(DirSubCopy) > 0){ + + NEONprocIS.base::def.dir.copy.symb(DirSrc=fs::path(DirIn,DirSubCopy), + DirDest=dirOut, + LnkSubObj=FALSE, + log=log) + } + + #####Threshold#### + dirInThrsh <- fs::path(DirIn,'threshold') + fileThsh <- list.files(dirInThrsh) + + # Read thresholds (optimized error handling) + if (base::length(fileThsh) > 1) { + log$debug(base::paste0('threshold files are ', paste(fileThsh, collapse = ', '))) + fileThsh <- fileThsh[1] + log$info(base::paste0('Using first threshold file: ', files$threshold)) + } + + thsh <- NEONprocIS.qaqc::def.read.thsh.qaqc.df( + NameFile = fs::path(dirInThrsh, fileThsh ) + ) + + # Verify terms exist (vectorized) + termTest <- c("VSICDepth01", "VSICDepth02", "VSICDepth03", "VSICDepth04", "VSICDepth05", "VSICDepth06", "VSICDepth07", "VSICDepth08") + if (!all(termTest %in% thsh$term_name)) { + log$error(base::paste0('Missing thresholds some of the following not in threshold file: ', termTest)) + stop() + } + + tempTest <- "tempTest" + + ############# + #dummy TODO delete after thresholds finalized + tempTest <- "Range Threshold Soft Min" + thsh_subset <- thsh[thsh$threshold_name == tempTest, ] + thsh_subset$threshold_name <- "tempTest" + ############### + + thsh_subset <- thsh_subset[thsh_subset$term_name %in% termTest,] + + threshold_lookup <- setNames(thsh_subset$number_value, thsh_subset$term_name) + + # Validate all thresholds exist + if (any(is.na(threshold_lookup))) { + log$error("Missing threshold values") + stop() + } + + + ############Thresholds End ############ + + #### data files ### + + fileData <- base::list.files(dirInData,pattern='.parquet',full.names=FALSE) + + # Read the datasets + data <- NEONprocIS.base::def.read.parq.ds(fileIn=fs::path(dirInData,fileData), + VarTime='readout_time', + RmvDupl=TRUE, + Df=TRUE, + log=log) + + # only keep depth variables here for testing. + # Build the SoilMoisture column names to keep + soil_cols <- names(data[grepl(names(data), pattern="SoilMoisture")]) + + # 2) Pick your always-keep columns (adjust as needed) + keep_static <- c("source_id", "site_id", "readout_time") + + # 3) Keep only those columns (and silently drop any that don’t exist) + keep_cols <- c(keep_static, intersect(soil_cols, names(data))) + data_sm <- data[, keep_cols, drop = FALSE] + + # Extract the "##" depth keys + keys <- sub("^depth(\\d{2})SoilMoisture$", "\\1", soil_cols) + + # Build the new QF column names + qf_cols <- paste0("depth", keys, "tempTestQF") + + # Add them, initialized to -1 + data_sm[qf_cols] <- -1 + + ######temperature data and locations########## + + + ###match threshold to column names + + # rename names like "VSICDepth02" -> "depth02SoilMoisture" + names(threshold_lookup) <- sub( + "^VSICDepth(\\d{2})$", + "depth\\1SoilMoisture", + names(threshold_lookup) + ) + + + ####Read in temperature locations. + + filesTemp <- list.files(DirTemp, full.names = TRUE, recursive = TRUE) + + filesTempData <- filesTemp[grepl("/temp-soil_[^/]+/data/[^/]+001\\.parquet$", filesTemp)] + filesTempLocation <- filesTemp[grepl("/temp-soil_[^/]+/location/[^/]*_locations\\.json$", filesTemp)] + + + # Helper to extract the temp-soil ID from a path, e.g., temp-soil_GRSM005501 + extract_ts_id <- function(paths) { + str_match(paths, "/(temp-soil_[^/]+)/")[, 2] + } + + extract_z_offset <- function(path) { + tryCatch({ + x <- jsonlite::fromJSON(path, simplifyVector = FALSE) + z <- x$features[[1]]$properties$locations$features[[1]]$properties$z_offset + as.numeric(z) + }, error = function(e) NA_real_) + } + + # Build a table from location files + df_locations <- tibble( + sensor_id = extract_ts_id(filesTempLocation), + depth_m = vapply(filesTempLocation, extract_z_offset, numeric(1)), + location_path = filesTempLocation + ) %>% + distinct(sensor_id, .keep_all = TRUE) + + # Build a table from data files + df_data <- tibble( + sensor_id = extract_ts_id(filesTempData), + data_path = filesTempData + ) %>% + distinct(sensor_id, .keep_all = TRUE) + + # Safe join by key + sensor_depth_df <- df_locations %>% + inner_join(df_data, by = "sensor_id") %>% + select(sensor_id, depth_m, location_path, data_path) + + # if theres errors stop + if (any(is.na(sensor_depth_df$depth_m))) { + error("Some location files failed to yield z_offset:\n", + paste(z_offsets_df$location_file[!is.na(z_offsets_df$error)], collapse = "\n")) + stop() + } + +########### + +########## + +#generate test logic +for(col in soil_cols){ + + depth_num <- sub("^depth(\\d{2}).*$", "\\1", col) + qf_name <- paste0("depth", depth_num, "tempTestQF") + + #first check if we are running the test at all + if(threshold_lookup[[col]]==0){ + log$info(paste0('skipping ', col, 'threshold indicates test not run')) + #if not running test, data is automatic pass. + data_sm[[qf_name]] <- 0 + + }else if (threshold_lookup[[col]]==1){ + + #find closest matching temp data. + # target depth (meters, negative = below surface) + target_depth <- max(data_sm[[col]], na.rm = T) + + # Find closest (tie-breaker: prefer shallower, i.e., greater/more positive) + closest_sensor <- sensor_depth_df %>% + filter(!is.na(depth_m)) %>% + mutate(abs_diff = abs(depth_m - target_depth)) %>% + arrange(abs_diff, depth_m, sensor_id) %>% # stable tie-breaker + slice(1) %>% + select(sensor_id, depth_m, data_path, location_path) + + closest_depth <- closest_sensor$depth_m + + #read in data + + ####todo Null handling? + temp_data_close <- NEONprocIS.base::def.read.parq(closest_sensor$data_path) + + #make new flag checking data frame + temp_data <- temp_data_close[,c('startDateTime', 'endDateTime')] + + #if no temp data is flagged for closest sensor, continue with test + if(all(temp_data_close$finalQF==0)){ + temp_data$temp_flag <- NA_integer_ + temp_data$temp_flag <- as.integer(temp_data_close$soilTempMean < temp_data_close$soilTempExpUncert) + #convert any remaining NA to -1 + temp_data$temp_flag[is.na(temp_data$temp_flag)] <- -1 + }else{#if we need one up one down, grab them. + + ### combine results of the closest sensor and the joined neighbors + #temp_data flags + #initialize column + temp_data$temp_flag <- NA_integer_ + #add flag value for non-flagged close data + + idx_ok <- !is.na(temp_data_close$finalQF) & temp_data_close$finalQF == 0L + temp_data$temp_flag[idx_ok] <- as.integer( + temp_data_close$soilTempMean[idx_ok] < temp_data_close$soilTempExpUncert[idx_ok] + ) + + # Ensure POSIXct and convert to data.table + setDT(temp_data) + temp_data[, `:=`( + startDateTime = as.POSIXct(startDateTime, tz = "UTC"), + endDateTime = as.POSIXct(endDateTime, tz = "UTC"), + temp_flag = as.integer(temp_flag) + )] + + # Neighbors relative to the closest depth + # - "next higher (shallower)" means depth_m > closest_depth (less negative) + next_higher <- sensor_depth_df %>% + filter(!is.na(depth_m), depth_m > closest_depth) %>% + arrange(depth_m, sensor_id) %>% + slice(1) %>% + select(sensor_id, depth_m, data_path, location_path) + + # - "next lower (deeper)" means depth_m < closest_depth (more negative) + # choose the one closest to the closest depth + next_lower <- sensor_depth_df %>% + filter(!is.na(depth_m), depth_m < closest_depth) %>% + arrange(desc(depth_m), sensor_id) %>% + slice(1) %>% + select(sensor_id, depth_m, data_path, location_path) + + # 3) Read the parquet files if those neighbors exist + if (nrow(next_lower) == 1) { + temp_data_lower <- NEONprocIS.base::def.read.parq(next_lower$data_path) + } else { + temp_data_lower <- NULL + } + if (nrow(next_higher) == 1) { + temp_data_higher <- NEONprocIS.base::def.read.parq(next_higher$data_path) + } else { + temp_data_higher <- NULL + } + + #if they both exist, pull in and do check + if (!is.null(temp_data_lower) & !is.null(temp_data_higher)){ + has_neighbor_join <- TRUE + #filter for only finalQF <- 1 + temp_data_lower <- temp_data_lower[temp_data_lower$finalQF<1, ] + temp_data_higher <- temp_data_higher[temp_data_higher$finalQF<1, ] + #calculate test + temp_data_lower$zero_check_low <- temp_data_lower$soilTempMean - temp_data_lower$soilTempExpUncert + temp_data_higher$zero_check_high <- temp_data_higher$soilTempMean - temp_data_higher$soilTempExpUncert + #join data + temp_data_join <- dplyr::full_join(temp_data_lower[,c("startDateTime", "endDateTime","zero_check_low")], + temp_data_higher[,c("startDateTime", "endDateTime","zero_check_high")], + by = c("startDateTime", "endDateTime")) + + #test to see if average of 2 temps is < 1 degree + setDT(temp_data_join) + + temp_data_join[, avg_zero_check := rowMeans(cbind(zero_check_low, zero_check_high), na.rm = TRUE)] + temp_data_join[is.nan(avg_zero_check), avg_zero_check := NA_real_] + temp_data_join[, zero_check := ifelse(is.na(avg_zero_check), NA_integer_, as.integer(avg_zero_check < 1))] + + ## If neighbor join exists, merge and coalesce (prefer closest; else neighbor) + if (is.data.frame(temp_data_join) && nrow(temp_data_join) > 0) { + setDT(temp_data_join) + temp_data_join[, `:=`( + startDateTime = as.POSIXct(startDateTime, tz = "UTC"), + endDateTime = as.POSIXct(endDateTime, tz = "UTC"), + zero_check = as.integer(zero_check) # ensure 0/1/NA + )] + + # Exact equality join on [startDateTime, endDateTime]. + combined <- merge( + temp_data, + temp_data_join[, .(startDateTime, endDateTime, zero_check)], + by = c("startDateTime", "endDateTime"), + all.x = TRUE, + sort = FALSE + ) + + # Fallback: if temp_flag is NA, use neighbor zero_check + # fcoalesce is a data.table helper (vector-wise coalesce) + combined[, temp_flag := fcoalesce(temp_flag, zero_check)] + combined[, zero_check := NULL] + + temp_data <- combined + } + + } #if statement looking for neighbors + + #any remaining NA values for temp_check == -1 + temp_data$temp_flag[is.na(temp_data$temp_flag)] <- -1 + + } #end of assigning temp test. + + + #next apply flag to the appropriate soil moisture flag. + + #ensure we have proper date formats + + # Non-equi join: assign each 10-sec row the flag of the minute interval it falls into + dt_sm <- as.data.table(data_sm) + dt_temp <- as.data.table(temp_data)[, .(startDateTime, endDateTime, temp_flag)] + + dt_sm[, `:=`( + readout_start = as.POSIXct(readout_time, tz = "UTC"), + readout_end = as.POSIXct(readout_time, tz = "UTC") + )] + + dt_temp[, `:=`( + startDateTime = as.POSIXct(startDateTime, tz = "UTC"), + endDateTime = as.POSIXct(endDateTime, tz = "UTC"), + temp_flag = as.integer(temp_flag) # TRUE->1, FALSE->0 + )] + + # foverlaps requires a key on y (the interval table) + setkey(dt_temp, startDateTime, endDateTime) + + # Use the point-interval columns you created in x + joined <- foverlaps( + x = dt_sm[, .(readout_start, readout_end, .rows = .I)], + y = dt_temp[, .(startDateTime, endDateTime, temp_flag)], + by.x = c("readout_start", "readout_end"), + by.y = c("startDateTime", "endDateTime"), + type = "within", # inclusive [start, end] + nomatch = NA_integer_ + ) + + new_qf <- data_sm[[qf_name]] + + # rows that have a matching minute interval + has_match <- !is.na(joined$temp_flag) + + # Overwrite with 1/0 where we matched + new_qf[ joined$.rows[has_match] ] <- joined$temp_flag[has_match] + + # 4) Assign back + data_sm[[qf_name]] <- new_qf + + }#end logic for running test + + } #end logic looping through all the columns. + + #filter flags to just columns of interest + qf_flags <- data_sm[,c("readout_time", qf_cols)] + #Read in flag data + fileQfPlau <- base::list.files(dirInQf,pattern='Plausibility.parquet',full.names=FALSE) + + qfPlau <- NEONprocIS.base::def.read.parq.ds(fileIn=fs::path(dirInQf,fileQfPlau), + VarTime='readout_time', + RmvDupl=TRUE, + Df=TRUE, + log=log) + + qfAll <- dplyr::full_join(qfPlau, qf_flags, by = "readout_time") + #write out logic. + + ###########pick it up here! write it out, clean it up and continue testing. + +###reorder the variables +# Your column names vector +cols <- names(qfAll) + +# Define sorting function +sort_cols <- function(cols) { + # Keep readout_time first + readout <- cols[cols == "readout_time"] + + # Get all QF columns + qf_cols <- cols[grepl("QF$", cols)] + + # Extract components for sorting + sort_df <- data.frame( + col = qf_cols, + stringsAsFactors = FALSE + ) + + # Extract variable type, depth, and QF type + sort_df$var_type <- ifelse(grepl("^VSIC", sort_df$col), "1_VSIC", + ifelse(grepl("^VSWCfactory", sort_df$col), "2_VSWCfactory", + ifelse(grepl("^VSWCsoilSpecific", sort_df$col), "3_VSWCsoilSpecific", + "4_temp"))) + + # Extract depth number + sort_df$depth <- as.numeric(gsub(".*Depth(\\d+).*|.*depth(\\d+).*", "\\1\\2", sort_df$col)) + + # Extract QF type with ordering + sort_df$qf_type <- ifelse(grepl("NullQF$", sort_df$col), "1_Null", + ifelse(grepl("GapQF$", sort_df$col), "2_Gap", + ifelse(grepl("RangeQF$", sort_df$col), "3_Range", + ifelse(grepl("StepQF$", sort_df$col), "4_Step", + ifelse(grepl("PersistenceQF$", sort_df$col), "5_Persistence", + ifelse(grepl("SpikeQF$", sort_df$col), "6_Spike", + "7_TempTest")))))) + + # Sort by var_type, then depth, then qf_type + sort_df <- sort_df[order(sort_df$depth, sort_df$qf_type, sort_df$var_type), ] + + # Combine readout_time first, then sorted QF columns + c(readout, sort_df$col) +} + +# Apply sorting +sorted_cols <- sort_cols(cols) + +# Reorder your dataframe +qfAll<- qfAll[, sorted_cols] + +nameFileQfOutFlag <- fileQfPlau + +nameFileQfOutFlag <- fs::path(dirOutQf,nameFileQfOutFlag) + + rptWrte <- + base::try(NEONprocIS.base::def.wrte.parq( + data = qfAll, + NameFile = nameFileQfOutFlag, + log=log + ), + silent = TRUE) + + if ('try-error' %in% base::class(rptWrte)) { + log$error(base::paste0( + 'Cannot write output to ', + nameFileQfOutFlag, + '. ', + attr(rptWrte, "condition") + )) + stop() + } else { + log$info(base::paste0( + 'Wrote updated flags data to file ', + nameFileQfOutFlag + )) + } + + return() +} #end function + diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml index fa2ad77c4..5216bc071 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml @@ -45,7 +45,7 @@ transform: "TermCtxt22=VSWCsoilSpecificDepth07|soil|water-content|soil-specific" \ "TermCtxt23=VSWCsoilSpecificDepth08|soil|water-content|soil-specific" \ "TermCtxt24=VSWCsoilSpecificDepth01|soil|water-content|soil-specific" \ - "DirSubCopy=data|location" + "DirSubCopy=data|location|uncertainty_coef|uncertainty_data" # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) cp -rL /tmp/threshold_select /tmp/threshold_selectCopy || : # Allow to fail without exit code (happens if step above produced no output) rm -r -f /tmp/threshold_select @@ -57,7 +57,7 @@ transform: OUT_PATH: /pfs/out LOG_LEVEL: INFO PAD_DIR: data - COPY_DIR: none # Can be multiple, separated by commas without spaces. Directories other than the pad directory to copy to the output + COPY_DIR: location,uncertainty_coef,uncertainty_data # Can be multiple, separated by commas without spaces. Directories other than the pad directory to copy to the output RELATIVE_PATH_INDEX: '3' PARALLELIZATION_INTERNAL: '5' # For threshold select module output_branch: master @@ -69,7 +69,7 @@ input: glob: /*/*/*/*/enviroscan - pfs: name: FILE_THRESHOLDS - repo: concH2oSoilSalinity_threshold_test3 #test 2 + repo: concH2oSoilSalinity_threshold_test3 glob: /thresholds.json parallelism_spec: constant: 5 From ed167873f8b07c68368608b7013cdd16e063a90c Mon Sep 17 00:00:00 2001 From: Teresa Burlingame Date: Tue, 17 Feb 2026 13:52:45 -0800 Subject: [PATCH 15/46] updates to functions to harden pipeline --- .../def.load.temp.sensors.R | 28 ++++++++- .../wrap.envscn.temp.flags.R | 59 +++++++++++-------- 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R index 363b606e5..ac6efd8ea 100644 --- a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R +++ b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R @@ -55,8 +55,10 @@ def.load.temp.sensors <- function(DirTemp, } if (base::length(filesTempLocation) == 0) { - log$error(base::paste0('No temperature location files found in ', DirTemp)) - stop() + log$warn(base::paste0('No temperature location files found in ', DirTemp, + '. Temperature test will not run.')) + return(data.frame(sensor_id = character(), depth_m = numeric(), + location_path = character(), data_path = character())) } # Helper function to extract sensor ID from path @@ -86,7 +88,27 @@ def.load.temp.sensors <- function(DirTemp, location_path = filesTempLocation, stringsAsFactors = FALSE ) - dfLocations <- dfLocations[!base::duplicated(dfLocations$sensor_id), ] + + # Check for duplicate sensor_ids and average depths if found + dupCheck <- base::table(dfLocations$sensor_id) + dupSensors <- base::names(dupCheck)[dupCheck > 1] + + if (base::length(dupSensors) > 0) { + log$warn(base::paste0('Multiple location files found for sensor(s): ', + base::paste(dupSensors, collapse = ', '), + '. Averaging depths.')) + + # Average depths for duplicate sensors + avgDepths <- base::aggregate( + depth_m ~ sensor_id, + data = dfLocations, + FUN = function(x) base::mean(x, na.rm = TRUE) + ) + firstPaths <- dfLocations[!base::duplicated(dfLocations$sensor_id), c('sensor_id', 'location_path')] + dfLocations <- base::merge(avgDepths, firstPaths, by = 'sensor_id') + } else { + dfLocations <- dfLocations[!base::duplicated(dfLocations$sensor_id), ] + } # Build data file table dataIds <- extract_sensor_id(filesTempData) diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R index 16164fb76..1051632d6 100644 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -189,8 +189,13 @@ wrap.envscn.temp.flags <- function(DirIn, # ===== Load temperature sensor metadata ===== sensorDepthDf <- def.load.temp.sensors(DirTemp = DirTemp, log = log) - # ===== Apply temperature test to each depth ===== - for (col in soilCols) { + # Check if we have any temperature sensors available + if (nrow(sensorDepthDf) == 0) { + log$warn("No temperature sensors available. All temperature test flags will remain -1 (test not run).") + # Skip temperature testing - flags already initialized to -1 + } else { + # ===== Apply temperature test to each depth ===== + for (col in soilCols) { depthNum <- base::sub("^depth(\\d{2}).*$", "\\1", col) qfName <- base::paste0("tempTestDepth", depthNum, "QF") @@ -206,28 +211,36 @@ wrap.envscn.temp.flags <- function(DirIn, # Find target depth from maximum sensor depth in the data targetDepth <- base::max(dataSm[[col]], na.rm = TRUE) - # Find closest temperature sensor - sensorInfo <- def.find.temp.sensor( - targetDepth = targetDepth, - sensorDepthDf = sensorDepthDf, - log = log - ) - - # Calculate temperature flags - tempData <- def.calc.temp.flags( - sensorInfo = sensorInfo, - log = log - ) - - # Apply flags to high-frequency data - dataSm <- def.apply.temp.flags( - dataSm = dataSm, - tempData = tempData, - qfColName = qfName, - log = log - ) + # Check if target depth is valid + if (!base::is.finite(targetDepth) || base::is.na(targetDepth)) { + log$warn(base::paste0('No valid depth data found for ', col, + '. Temperature test flag will remain -1 (test not run).')) + # Leave flag at -1 (already initialized) + } else { + # Find closest temperature sensor + sensorInfo <- def.find.temp.sensor( + targetDepth = targetDepth, + sensorDepthDf = sensorDepthDf, + log = log + ) + + # Calculate temperature flags + tempData <- def.calc.temp.flags( + sensorInfo = sensorInfo, + log = log + ) + + # Apply flags to high-frequency data + dataSm <- def.apply.temp.flags( + dataSm = dataSm, + tempData = tempData, + qfColName = qfName, + log = log + ) + } } - } + } # End temperature test loop + } # End check for available sensors # ===== Merge with existing plausibility flags ===== qfFlags <- dataSm[, c("readout_time", qfCols)] From 4690a657eaf9bed98b90db925a73c6b84b6d2ccf Mon Sep 17 00:00:00 2001 From: Teresa Burlingame Date: Tue, 17 Feb 2026 14:03:47 -0800 Subject: [PATCH 16/46] continued hardening --- .../wrap.envscn.temp.flags.R | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R index 1051632d6..bde4fc0ec 100644 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -224,19 +224,26 @@ wrap.envscn.temp.flags <- function(DirIn, log = log ) - # Calculate temperature flags - tempData <- def.calc.temp.flags( - sensorInfo = sensorInfo, - log = log - ) - - # Apply flags to high-frequency data - dataSm <- def.apply.temp.flags( - dataSm = dataSm, - tempData = tempData, - qfColName = qfName, - log = log - ) + # Check if a matching sensor was found + if (base::is.null(sensorInfo)) { + log$warn(base::paste0('No temperature sensor found for ', col, + ' at depth ', targetDepth, 'm. Temperature test flag will remain -1 (test not run).')) + # Leave flag at -1 (already initialized) + } else { + # Calculate temperature flags + tempData <- def.calc.temp.flags( + sensorInfo = sensorInfo, + log = log + ) + + # Apply flags to high-frequency data + dataSm <- def.apply.temp.flags( + dataSm = dataSm, + tempData = tempData, + qfColName = qfName, + log = log + ) + } } } } # End temperature test loop From 0102e077451de0b6b328fb935b6d441cba1ff085 Mon Sep 17 00:00:00 2001 From: Teresa Burlingame Date: Tue, 17 Feb 2026 15:00:43 -0800 Subject: [PATCH 17/46] handling for limited PRT data and not grabbing sensors that are too far away. --- .../def.calc.temp.flags.R | 23 +++++++++++- .../wrap.envscn.temp.flags.R | 37 +++++++++++++------ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/flow/flow.envscn.temp.flags/def.calc.temp.flags.R b/flow/flow.envscn.temp.flags/def.calc.temp.flags.R index b0a890421..9e99823c6 100644 --- a/flow/flow.envscn.temp.flags/def.calc.temp.flags.R +++ b/flow/flow.envscn.temp.flags/def.calc.temp.flags.R @@ -9,6 +9,7 @@ #' If primary sensor is flagged, uses average of neighboring sensors. #' @param sensorInfo List containing 'closest' sensor and 'neighbors' list (from def.find.temp.sensor) +#' @param targetDepth Numeric. The target depth (in meters) for distance validation #' @param log A logger object. Defaults to NULL. #' @return Data frame with columns: @@ -30,6 +31,7 @@ #' original creation ############################################################################################## def.calc.temp.flags <- function(sensorInfo, + targetDepth = NULL, log = NULL) { # Initialize log if not provided @@ -74,7 +76,26 @@ def.calc.temp.flags <- function(sensorInfo, nextHigher <- sensorInfo$neighbors$higher nextLower <- sensorInfo$neighbors$lower - if (!base::is.null(nextHigher) && !base::is.null(nextLower)) { + # Check if neighbors exist and are within acceptable distance (0.25m) + useNeighbors <- FALSE + if (!base::is.null(nextHigher) && !base::is.null(nextLower) && !base::is.null(targetDepth)) { + distHigher <- base::abs(targetDepth - nextHigher$depth_m) + distLower <- base::abs(targetDepth - nextLower$depth_m) + + if (distHigher > 0.25 || distLower > 0.25) { + log$warn(base::paste0('Neighbor sensors exceed 0.25m distance threshold. ', + 'Higher: ', base::round(distHigher, 3), 'm, ', + 'Lower: ', base::round(distLower, 3), 'm. ', + 'Skipping neighbor averaging.')) + } else { + useNeighbors <- TRUE + } + } else if (!base::is.null(nextHigher) && !base::is.null(nextLower)) { + # targetDepth not provided, proceed without distance check (legacy behavior) + useNeighbors <- TRUE + } + + if (useNeighbors) { # Read neighbor data tempDataHigher <- NEONprocIS.base::def.read.parq(nextHigher$data_path) tempDataLower <- NEONprocIS.base::def.read.parq(nextLower$data_path) diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R index bde4fc0ec..f38b3bb87 100644 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -230,19 +230,32 @@ wrap.envscn.temp.flags <- function(DirIn, ' at depth ', targetDepth, 'm. Temperature test flag will remain -1 (test not run).')) # Leave flag at -1 (already initialized) } else { - # Calculate temperature flags - tempData <- def.calc.temp.flags( - sensorInfo = sensorInfo, - log = log - ) + # Check if sensor is within acceptable distance (0.25m) + sensorDepth <- sensorInfo$depth_m + depthDiff <- base::abs(targetDepth - sensorDepth) - # Apply flags to high-frequency data - dataSm <- def.apply.temp.flags( - dataSm = dataSm, - tempData = tempData, - qfColName = qfName, - log = log - ) + if (depthDiff > 0.25) { + log$warn(base::paste0('Temperature sensor for ', col, ' is ', + base::round(depthDiff, 3), 'm away from target depth ', + targetDepth, 'm (max allowed: 0.25m). ', + 'Temperature test flag will remain -1 (test not run).')) + # Leave flag at -1 (already initialized) + } else { + # Calculate temperature flags + tempData <- def.calc.temp.flags( + sensorInfo = sensorInfo, + targetDepth = targetDepth, + log = log + ) + + # Apply flags to high-frequency data + dataSm <- def.apply.temp.flags( + dataSm = dataSm, + tempData = tempData, + qfColName = qfName, + log = log + ) + } } } } From f71f7587c1c50dc5592151df971eaa80a45d2fbe Mon Sep 17 00:00:00 2001 From: Teresa Burlingame Date: Tue, 17 Feb 2026 15:19:41 -0800 Subject: [PATCH 18/46] dynamic distance paramater calculations --- .../def.calc.temp.flags.R | 12 +++++++----- .../wrap.envscn.temp.flags.R | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/flow/flow.envscn.temp.flags/def.calc.temp.flags.R b/flow/flow.envscn.temp.flags/def.calc.temp.flags.R index 9e99823c6..2fb20397f 100644 --- a/flow/flow.envscn.temp.flags/def.calc.temp.flags.R +++ b/flow/flow.envscn.temp.flags/def.calc.temp.flags.R @@ -10,6 +10,7 @@ #' @param sensorInfo List containing 'closest' sensor and 'neighbors' list (from def.find.temp.sensor) #' @param targetDepth Numeric. The target depth (in meters) for distance validation +#' @param distThreshold Numeric. Maximum allowed distance (in meters) between sensor and target depth #' @param log A logger object. Defaults to NULL. #' @return Data frame with columns: @@ -32,6 +33,7 @@ ############################################################################################## def.calc.temp.flags <- function(sensorInfo, targetDepth = NULL, + distThreshold = NULL, log = NULL) { # Initialize log if not provided @@ -76,14 +78,14 @@ def.calc.temp.flags <- function(sensorInfo, nextHigher <- sensorInfo$neighbors$higher nextLower <- sensorInfo$neighbors$lower - # Check if neighbors exist and are within acceptable distance (0.25m) + # Check if neighbors exist and are within acceptable distance useNeighbors <- FALSE - if (!base::is.null(nextHigher) && !base::is.null(nextLower) && !base::is.null(targetDepth)) { + if (!base::is.null(nextHigher) && !base::is.null(nextLower) && !base::is.null(targetDepth) && !base::is.null(distThreshold)) { distHigher <- base::abs(targetDepth - nextHigher$depth_m) distLower <- base::abs(targetDepth - nextLower$depth_m) - if (distHigher > 0.25 || distLower > 0.25) { - log$warn(base::paste0('Neighbor sensors exceed 0.25m distance threshold. ', + if (distHigher > distThreshold || distLower > distThreshold) { + log$warn(base::paste0('Neighbor sensors exceed ', distThreshold, 'm distance threshold. ', 'Higher: ', base::round(distHigher, 3), 'm, ', 'Lower: ', base::round(distLower, 3), 'm. ', 'Skipping neighbor averaging.')) @@ -91,7 +93,7 @@ def.calc.temp.flags <- function(sensorInfo, useNeighbors <- TRUE } } else if (!base::is.null(nextHigher) && !base::is.null(nextLower)) { - # targetDepth not provided, proceed without distance check (legacy behavior) + # targetDepth or distThreshold not provided, proceed without distance check (legacy behavior) useNeighbors <- TRUE } diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R index f38b3bb87..28412b091 100644 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -230,14 +230,24 @@ wrap.envscn.temp.flags <- function(DirIn, ' at depth ', targetDepth, 'm. Temperature test flag will remain -1 (test not run).')) # Leave flag at -1 (already initialized) } else { - # Check if sensor is within acceptable distance (0.25m) + # Determine distance threshold based on depth level + depthLevel <- base::as.numeric(depthNum) + if (depthLevel <= 2) { + distThreshold <- 0.15 # Levels 1-2 + } else if (depthLevel <= 5) { + distThreshold <- 0.35 # Levels 3-5 + } else { + distThreshold <- 0.75 # Levels 6-8 + } + + # Check if sensor is within acceptable distance sensorDepth <- sensorInfo$depth_m depthDiff <- base::abs(targetDepth - sensorDepth) - if (depthDiff > 0.25) { + if (depthDiff > distThreshold) { log$warn(base::paste0('Temperature sensor for ', col, ' is ', base::round(depthDiff, 3), 'm away from target depth ', - targetDepth, 'm (max allowed: 0.25m). ', + targetDepth, 'm (max allowed: ', distThreshold, 'm for level ', depthLevel, '). ', 'Temperature test flag will remain -1 (test not run).')) # Leave flag at -1 (already initialized) } else { @@ -245,6 +255,7 @@ wrap.envscn.temp.flags <- function(DirIn, tempData <- def.calc.temp.flags( sensorInfo = sensorInfo, targetDepth = targetDepth, + distThreshold = distThreshold, log = log ) From 15620f07a5e93dd6728bc3129b55a7cc135b7d4f Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 18 Feb 2026 16:22:35 -0700 Subject: [PATCH 19/46] updates to fix errors discovered in unit testing. --- flow/flow.envscn.temp.flags/def.load.temp.sensors.R | 10 +++++++--- flow/flow.envscn.temp.flags/def.sort.qf.cols.R | 4 ++-- flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R | 11 ++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R index ac6efd8ea..228210b79 100644 --- a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R +++ b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R @@ -72,12 +72,17 @@ def.load.temp.sensors <- function(DirTemp, base::tryCatch({ locData <- jsonlite::fromJSON(path, simplifyVector = FALSE) zOffset <- locData$features[[1]]$properties$locations$features[[1]]$properties$z_offset + # Check if zOffset is NULL or has length 0 + if (base::is.null(zOffset) || base::length(zOffset) == 0) { + return(NA_real_) + } return(base::as.numeric(zOffset)) }, error = function(e) { return(NA_real_) }) } + # Build location metadata table sensorIds <- extract_sensor_id(filesTempLocation) depths <- base::vapply(filesTempLocation, extract_z_offset, numeric(1)) @@ -99,7 +104,7 @@ def.load.temp.sensors <- function(DirTemp, '. Averaging depths.')) # Average depths for duplicate sensors - avgDepths <- base::aggregate( + avgDepths <- stats::aggregate( depth_m ~ sensor_id, data = dfLocations, FUN = function(x) base::mean(x, na.rm = TRUE) @@ -125,9 +130,8 @@ def.load.temp.sensors <- function(DirTemp, # Validate that all depths were successfully extracted if (base::any(base::is.na(sensorDepthDf$depth_m))) { failedSensors <- sensorDepthDf$sensor_id[base::is.na(sensorDepthDf$depth_m)] - log$error(base::paste0('Failed to extract depth from location files for: ', + log$warn(base::paste0('Failed to extract depth from location files for: ', base::paste(failedSensors, collapse = ', '))) - stop() } log$info(base::paste0('Loaded ', base::nrow(sensorDepthDf), ' temperature sensors')) diff --git a/flow/flow.envscn.temp.flags/def.sort.qf.cols.R b/flow/flow.envscn.temp.flags/def.sort.qf.cols.R index 4ed62ef09..569ad0a5c 100644 --- a/flow/flow.envscn.temp.flags/def.sort.qf.cols.R +++ b/flow/flow.envscn.temp.flags/def.sort.qf.cols.R @@ -51,9 +51,9 @@ def.sort.qf.cols <- function(cols) { ) # Extract depth number - sortDf$depth <- base::as.numeric( + sortDf$depth <- base::suppressWarnings(base::as.numeric( base::gsub(".*Depth(\\d+).*|.*depth(\\d+).*", "\\1\\2", sortDf$col) - ) + )) # Classify QF test type sortDf$qfType <- base::ifelse( diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R index 28412b091..24a209e57 100644 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -241,12 +241,13 @@ wrap.envscn.temp.flags <- function(DirIn, } # Check if sensor is within acceptable distance - sensorDepth <- sensorInfo$depth_m - depthDiff <- base::abs(targetDepth - sensorDepth) + sensorDepthClose <- sensorInfo$closest$depth_m - if (depthDiff > distThreshold) { - log$warn(base::paste0('Temperature sensor for ', col, ' is ', - base::round(depthDiff, 3), 'm away from target depth ', + depthDiffClose <- base::abs(targetDepth - sensorDepthClose) + + if (depthDiffClose > distThreshold) { + log$warn(base::paste0('Temperature sensor for ', col , ' is ', + base::round(depthDiffClose, 3), 'm away from target depth ', targetDepth, 'm (max allowed: ', distThreshold, 'm for level ', depthLevel, '). ', 'Temperature test flag will remain -1 (test not run).')) # Leave flag at -1 (already initialized) From 1c7973f5866f3e3bc0c2beb722d138288ff3cacc Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 18 Feb 2026 16:35:32 -0700 Subject: [PATCH 20/46] prepping to load to pachyderm --- .../build_push_analyze_pad_qaqc_envscn.yml | 51 ++ .../dockerfile_in_combined_module.txt | 1 + .../flow.envscn.temp.flags.R | 15 +- flow/flow.envscn.temp.flags/renv.lock | 88 +++ .../wrap.envscn.temp.flags_messy.R | 537 ------------------ .../analyze_pad_qaqc_envscn/Dockerfile | 66 +++ 6 files changed, 213 insertions(+), 545 deletions(-) create mode 100644 .github/workflows/build_push_analyze_pad_qaqc_envscn.yml create mode 100644 flow/flow.envscn.temp.flags/dockerfile_in_combined_module.txt create mode 100644 flow/flow.envscn.temp.flags/renv.lock delete mode 100644 flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R create mode 100644 modules_combined/analyze_pad_qaqc_envscn/Dockerfile diff --git a/.github/workflows/build_push_analyze_pad_qaqc_envscn.yml b/.github/workflows/build_push_analyze_pad_qaqc_envscn.yml new file mode 100644 index 000000000..991ce4ee1 --- /dev/null +++ b/.github/workflows/build_push_analyze_pad_qaqc_envscn.yml @@ -0,0 +1,51 @@ +name: "Build-push_analyze_pad_qaqc_envscn" + +on: + push: + branches: + - 'master' + paths: + - 'modules_combined/qaqc_plau_and_radiation_custom/**' + - 'modules/padded_timeseries_analyzer/**' + - 'modules/common/**' + - 'flow/flow.qaqc.plau/**' + - 'flow/flow.envscn.temp.flags/**' + workflow_dispatch: {} # Allows trigger of workflow from web interface + +env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN }} + # Use github and google registries + GHCR_REGISTRY: ghcr.io + GCP_ARTIFACT_HOST: ${{ vars.SHARED_WIF_LOCATON }}-docker.pkg.dev + GCP_REGISTRY: ${{ vars.SHARED_WIF_LOCATON }}-docker.pkg.dev/${{ vars.SHARED_WIF_PROJECT }}/${{ vars.SHARED_WIF_REPO }} + GCP_PROVIDER: ${{ vars.SHARED_WIF_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ vars.SHARED_WIF_SERVICE_ACCOUNT }} + GHCR_NS: battelleecology + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # Get just the repo name from the event, i.e., NEON-IS-data-processing + REPO_NAME: ${{ github.event.repository.name }} + # IS module name + MODULE_PATH: ./modules_combined/analyze_pad_qaqc_envscn + IMAGE_NAME: neon-is-qaqc-plau-rad + +jobs: + build-push: + runs-on: ubuntu-latest + permissions: + contents: 'write' + id-token: 'write' + steps: + - name: "Checkout" + uses: "actions/checkout@v4.1.4" + with: + fetch-depth: '0' + + - name: Get short SHA + run: echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + + - name: Build and push + uses: ./.github/actions/build-push + with: + image-tag: "${short_sha}" + diff --git a/flow/flow.envscn.temp.flags/dockerfile_in_combined_module.txt b/flow/flow.envscn.temp.flags/dockerfile_in_combined_module.txt new file mode 100644 index 000000000..57353332f --- /dev/null +++ b/flow/flow.envscn.temp.flags/dockerfile_in_combined_module.txt @@ -0,0 +1 @@ +analyze_pad_qaqc_envscn diff --git a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R index 898784429..808ba1c5d 100644 --- a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R @@ -92,17 +92,16 @@ ############################################################################################## library(foreach) library(doParallel) -#TODO uncomment! # # Source the wrapper function and other dependency functions. Assume it is in the working directory -# source("./wrap.envscn.temp.flags.R") -# source("./def.apply.temp.flags.R") -# source("./def.find.temp.sensor.R") -# source("./def.load.temp.sensor.R") -# source("./def.sort.qf.cols.R") -# source("./def.calc.temp.flags.R") +source("./wrap.envscn.temp.flags.R") +source("./def.apply.temp.flags.R") +source("./def.find.temp.sensor.R") +source("./def.load.temp.sensor.R") +source("./def.sort.qf.cols.R") +source("./def.calc.temp.flags.R") # # Pull in command line arguments (parameters) -# arg <- base::commandArgs(trailingOnly = TRUE) +arg <- base::commandArgs(trailingOnly = TRUE) # Start logging log <- NEONprocIS.base::def.log.init() diff --git a/flow/flow.envscn.temp.flags/renv.lock b/flow/flow.envscn.temp.flags/renv.lock new file mode 100644 index 000000000..24ea1ef1f --- /dev/null +++ b/flow/flow.envscn.temp.flags/renv.lock @@ -0,0 +1,88 @@ +{ + "R": { + "Version": "4.1.3", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Packages": { + "codetools": { + "Package": "codetools", + "Version": "0.2-18", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "019388fc48e48b3da0d3a76ff94608a8", + "Requirements": [] + }, + "data.table": { + "Package": "data.table", + "Version": "1.18.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "498817481016697c065f66fe14f3bcce", + "Requirements": [] + }, + "doParallel": { + "Package": "doParallel", + "Version": "1.0.17", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "451e5edf411987991ab6a5410c45011f", + "Requirements": [ + "foreach", + "iterators" + ] + }, + "foreach": { + "Package": "foreach", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "618609b42c9406731ead03adf5379850", + "Requirements": [ + "codetools", + "iterators" + ] + }, + "fs": { + "Package": "fs", + "Version": "1.6.6", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "7eb1e342eee7e0a7449c49cdaa526d39", + "Requirements": [] + }, + "iterators": { + "Package": "iterators", + "Version": "1.0.14", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "8954069286b4b2b0d023d1b288dce978", + "Requirements": [] + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "2.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "b0776f526d36d8bd4a3344a88fe165c4", + "Requirements": [] + }, + "renv": { + "Package": "renv", + "Version": "0.16.0", + "Source": "GitHub", + "RemoteType": "github", + "RemoteHost": "api.github.com", + "RemoteRepo": "renv", + "RemoteUsername": "rstudio", + "RemoteRef": "0.16.0", + "RemoteSha": "0e3aab27a928eb261819a3fc45a3ee2b4ba902a5", + "Hash": "9e5e2246d73254a29a4182f4e8257c09", + "Requirements": [] + } + } +} diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R deleted file mode 100644 index 7135305ff..000000000 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags_messy.R +++ /dev/null @@ -1,537 +0,0 @@ -############################################################################################## -#' @title Assess soil temperature closest to sensor depths to determine if data should be flagged. - -#' @author -#' Teresa Burlingame \email{tburlingame@battelleecology.org} \cr - -#' @description Workflow. Determine if test is to be run based on thresholds, find closests temperature sensor, -#' read in data and check if it is below freezing. If the closest sensor is flagged, do an average of the one above and one below -#' and check to see if the average of the temperatures are less than 1 degree. If one or both are unavailable, flag NA -#' -#' @param DirIn Character value. The input path to the data from a single source ID, structured as follows: -#' #/pfs/BASE_REPO/#/yyyy/mm/dd/#/location-id, where # indicates any number of parent and child directories -#' of any name, so long as they are not 'pfs' or recognizable as the 'yyyy/mm/dd' structure which indicates -#' the 4-digit year, 2-digit month, and' 2-digit day. The location-id is the unique identifier of the location. \cr -#' -#' Nested within this path are (at a minimum) the folders: -#' /data -#' /flags - -#' The flags folder holds two files containing basic plausibility and calibration quality flags for -#' the central processing day only. These files should respectively be named in the convention: -#' SOURCETYPE_LOCATIONID_YYYY-MM-DD_flagsPlausibility.parquet -#' All other files in this directory will be ignored. -#' -#' The threshold folder contains a single file named thresholds.json that holds threshold parameters -#' applicable to the smoothing algorithm. -#' -#' @param DirTemp Character value. The input path to the temperature data that is used to perform the the temperature test. -#' Location files are used to determine depth of the sensors and pair with the appropriate enviroscan depth. -#' Then the data is read in to perform tests. -#' -#' @param DirOutBase Character value. The output path that will replace the #/pfs/BASE_REPO portion of DirIn. -#' -#' @param SchmQf (Optional). A json-formatted character string containing the schema for the standard calibration and -#' plausibility QFs as well as the custom QFs tempTestDepth##QF - -#' @param DirSubCopy (optional) Character vector. The names of additional subfolders at -#' the same level as the data/flags/threshold folders in the input path that are to be copied with a -#' symbolic link to the output path (i.e. carried through as-is). Note that the 'stats' and 'flags' directories -#' are automatically populated in the output and cannot be included here. - -#' @param log A logger object as produced by NEONprocIS.base::def.log.init to produce structured log -#' output. Defaults to NULL, in which the logger will be created and used within the function. See NEONprocIS.base::def.log.init -#' for more details. -#' -#' @return A repository in DirOutBase containing the quality flags of the three different enviroscan data streams:" -#' VSIC, VSWCFactory and VSWCSoilSpecific. Standard flags are read in, then a test for frozen soil is performed. -#' -#' @references -#' License: (example) GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 - -#' @keywords Currently none - -#' @examples -#' # NOT RUN -#' -#' "DirIn=/scratch/pfs/concH2oSoilSalinity_analyze_pad_and_qaqc_plau/2025/10/17/conc-h2o-soil-salinity_GRSM001501/", -#' "DirTemp=/scratch/pfs/concH2oSoilSalinity_group_path/2025/10/17/conc-h2o-soil-salinity_GRSM005501/", -#' "DirOut=/scratch/pfs/tb_out", -#' "DirErr=/scratch/pfs/tb_out/errored_datums" -#' wrap.envscn.temp.flags(DirIn,DirOutBase,DirTemp,DirSubCopy) - -#' @seealso Currently none - -# changelog and author contributions / copyrights -# Teresa Burlingame (2025-02-16) -# Initial creation -############################################################################################## -wrap.envscn.temp.flags<- function(DirIn, - DirOutBase, - DirTemp, - SchmQf=NULL, - DirSubCopy=NULL, - log=NULL -){ - - library(tibble) - library(dplyr) - library(purrr) - library(tidyr) - library(data.table) - - if(base::is.null(log)){ - log <- NEONprocIS.base::def.log.init() - } - - # Gather info about the input directory and create the output directory. - InfoDirIn <- NEONprocIS.base::def.dir.splt.pach.time(DirIn,log=log) - dirInData <- fs::path(DirIn,'data') - dirInQf <- fs::path(DirIn,'flags') - - dirOut <- fs::path(DirOutBase,InfoDirIn$dirRepo) - dirOutQf <- fs::path(dirOut,'flags') - - #dirOutData <- fs::path(dirOut,'data') - NEONprocIS.base::def.dir.crea(DirBgn = dirOut, - DirSub = c('flags'), - log = log) - - # Copy with a symbolic link the desired subfolders - DirSubCopy <- base::unique(base::setdiff(DirSubCopy,c('flags'))) - if(base::length(DirSubCopy) > 0){ - - NEONprocIS.base::def.dir.copy.symb(DirSrc=fs::path(DirIn,DirSubCopy), - DirDest=dirOut, - LnkSubObj=FALSE, - log=log) - } - - #####Threshold#### - dirInThrsh <- fs::path(DirIn,'threshold') - fileThsh <- list.files(dirInThrsh) - - # Read thresholds (optimized error handling) - if (base::length(fileThsh) > 1) { - log$debug(base::paste0('threshold files are ', paste(fileThsh, collapse = ', '))) - fileThsh <- fileThsh[1] - log$info(base::paste0('Using first threshold file: ', files$threshold)) - } - - thsh <- NEONprocIS.qaqc::def.read.thsh.qaqc.df( - NameFile = fs::path(dirInThrsh, fileThsh ) - ) - - # Verify terms exist (vectorized) - termTest <- c("VSICDepth01", "VSICDepth02", "VSICDepth03", "VSICDepth04", "VSICDepth05", "VSICDepth06", "VSICDepth07", "VSICDepth08") - if (!all(termTest %in% thsh$term_name)) { - log$error(base::paste0('Missing thresholds some of the following not in threshold file: ', termTest)) - stop() - } - - tempTest <- "tempTest" - - ############# - #dummy TODO delete after thresholds finalized - tempTest <- "Range Threshold Soft Min" - thsh_subset <- thsh[thsh$threshold_name == tempTest, ] - thsh_subset$threshold_name <- "tempTest" - ############### - - thsh_subset <- thsh_subset[thsh_subset$term_name %in% termTest,] - - threshold_lookup <- setNames(thsh_subset$number_value, thsh_subset$term_name) - - # Validate all thresholds exist - if (any(is.na(threshold_lookup))) { - log$error("Missing threshold values") - stop() - } - - - ############Thresholds End ############ - - #### data files ### - - fileData <- base::list.files(dirInData,pattern='.parquet',full.names=FALSE) - - # Read the datasets - data <- NEONprocIS.base::def.read.parq.ds(fileIn=fs::path(dirInData,fileData), - VarTime='readout_time', - RmvDupl=TRUE, - Df=TRUE, - log=log) - - # only keep depth variables here for testing. - # Build the SoilMoisture column names to keep - soil_cols <- names(data[grepl(names(data), pattern="SoilMoisture")]) - - # 2) Pick your always-keep columns (adjust as needed) - keep_static <- c("source_id", "site_id", "readout_time") - - # 3) Keep only those columns (and silently drop any that don’t exist) - keep_cols <- c(keep_static, intersect(soil_cols, names(data))) - data_sm <- data[, keep_cols, drop = FALSE] - - # Extract the "##" depth keys - keys <- sub("^depth(\\d{2})SoilMoisture$", "\\1", soil_cols) - - # Build the new QF column names - qf_cols <- paste0("depth", keys, "tempTestQF") - - # Add them, initialized to -1 - data_sm[qf_cols] <- -1 - - ######temperature data and locations########## - - - ###match threshold to column names - - # rename names like "VSICDepth02" -> "depth02SoilMoisture" - names(threshold_lookup) <- sub( - "^VSICDepth(\\d{2})$", - "depth\\1SoilMoisture", - names(threshold_lookup) - ) - - - ####Read in temperature locations. - - filesTemp <- list.files(DirTemp, full.names = TRUE, recursive = TRUE) - - filesTempData <- filesTemp[grepl("/temp-soil_[^/]+/data/[^/]+001\\.parquet$", filesTemp)] - filesTempLocation <- filesTemp[grepl("/temp-soil_[^/]+/location/[^/]*_locations\\.json$", filesTemp)] - - - # Helper to extract the temp-soil ID from a path, e.g., temp-soil_GRSM005501 - extract_ts_id <- function(paths) { - str_match(paths, "/(temp-soil_[^/]+)/")[, 2] - } - - extract_z_offset <- function(path) { - tryCatch({ - x <- jsonlite::fromJSON(path, simplifyVector = FALSE) - z <- x$features[[1]]$properties$locations$features[[1]]$properties$z_offset - as.numeric(z) - }, error = function(e) NA_real_) - } - - # Build a table from location files - df_locations <- tibble( - sensor_id = extract_ts_id(filesTempLocation), - depth_m = vapply(filesTempLocation, extract_z_offset, numeric(1)), - location_path = filesTempLocation - ) %>% - distinct(sensor_id, .keep_all = TRUE) - - # Build a table from data files - df_data <- tibble( - sensor_id = extract_ts_id(filesTempData), - data_path = filesTempData - ) %>% - distinct(sensor_id, .keep_all = TRUE) - - # Safe join by key - sensor_depth_df <- df_locations %>% - inner_join(df_data, by = "sensor_id") %>% - select(sensor_id, depth_m, location_path, data_path) - - # if theres errors stop - if (any(is.na(sensor_depth_df$depth_m))) { - error("Some location files failed to yield z_offset:\n", - paste(z_offsets_df$location_file[!is.na(z_offsets_df$error)], collapse = "\n")) - stop() - } - -########### - -########## - -#generate test logic -for(col in soil_cols){ - - depth_num <- sub("^depth(\\d{2}).*$", "\\1", col) - qf_name <- paste0("depth", depth_num, "tempTestQF") - - #first check if we are running the test at all - if(threshold_lookup[[col]]==0){ - log$info(paste0('skipping ', col, 'threshold indicates test not run')) - #if not running test, data is automatic pass. - data_sm[[qf_name]] <- 0 - - }else if (threshold_lookup[[col]]==1){ - - #find closest matching temp data. - # target depth (meters, negative = below surface) - target_depth <- max(data_sm[[col]], na.rm = T) - - # Find closest (tie-breaker: prefer shallower, i.e., greater/more positive) - closest_sensor <- sensor_depth_df %>% - filter(!is.na(depth_m)) %>% - mutate(abs_diff = abs(depth_m - target_depth)) %>% - arrange(abs_diff, depth_m, sensor_id) %>% # stable tie-breaker - slice(1) %>% - select(sensor_id, depth_m, data_path, location_path) - - closest_depth <- closest_sensor$depth_m - - #read in data - - ####todo Null handling? - temp_data_close <- NEONprocIS.base::def.read.parq(closest_sensor$data_path) - - #make new flag checking data frame - temp_data <- temp_data_close[,c('startDateTime', 'endDateTime')] - - #if no temp data is flagged for closest sensor, continue with test - if(all(temp_data_close$finalQF==0)){ - temp_data$temp_flag <- NA_integer_ - temp_data$temp_flag <- as.integer(temp_data_close$soilTempMean < temp_data_close$soilTempExpUncert) - #convert any remaining NA to -1 - temp_data$temp_flag[is.na(temp_data$temp_flag)] <- -1 - }else{#if we need one up one down, grab them. - - ### combine results of the closest sensor and the joined neighbors - #temp_data flags - #initialize column - temp_data$temp_flag <- NA_integer_ - #add flag value for non-flagged close data - - idx_ok <- !is.na(temp_data_close$finalQF) & temp_data_close$finalQF == 0L - temp_data$temp_flag[idx_ok] <- as.integer( - temp_data_close$soilTempMean[idx_ok] < temp_data_close$soilTempExpUncert[idx_ok] - ) - - # Ensure POSIXct and convert to data.table - setDT(temp_data) - temp_data[, `:=`( - startDateTime = as.POSIXct(startDateTime, tz = "UTC"), - endDateTime = as.POSIXct(endDateTime, tz = "UTC"), - temp_flag = as.integer(temp_flag) - )] - - # Neighbors relative to the closest depth - # - "next higher (shallower)" means depth_m > closest_depth (less negative) - next_higher <- sensor_depth_df %>% - filter(!is.na(depth_m), depth_m > closest_depth) %>% - arrange(depth_m, sensor_id) %>% - slice(1) %>% - select(sensor_id, depth_m, data_path, location_path) - - # - "next lower (deeper)" means depth_m < closest_depth (more negative) - # choose the one closest to the closest depth - next_lower <- sensor_depth_df %>% - filter(!is.na(depth_m), depth_m < closest_depth) %>% - arrange(desc(depth_m), sensor_id) %>% - slice(1) %>% - select(sensor_id, depth_m, data_path, location_path) - - # 3) Read the parquet files if those neighbors exist - if (nrow(next_lower) == 1) { - temp_data_lower <- NEONprocIS.base::def.read.parq(next_lower$data_path) - } else { - temp_data_lower <- NULL - } - if (nrow(next_higher) == 1) { - temp_data_higher <- NEONprocIS.base::def.read.parq(next_higher$data_path) - } else { - temp_data_higher <- NULL - } - - #if they both exist, pull in and do check - if (!is.null(temp_data_lower) & !is.null(temp_data_higher)){ - has_neighbor_join <- TRUE - #filter for only finalQF <- 1 - temp_data_lower <- temp_data_lower[temp_data_lower$finalQF<1, ] - temp_data_higher <- temp_data_higher[temp_data_higher$finalQF<1, ] - #calculate test - temp_data_lower$zero_check_low <- temp_data_lower$soilTempMean - temp_data_lower$soilTempExpUncert - temp_data_higher$zero_check_high <- temp_data_higher$soilTempMean - temp_data_higher$soilTempExpUncert - #join data - temp_data_join <- dplyr::full_join(temp_data_lower[,c("startDateTime", "endDateTime","zero_check_low")], - temp_data_higher[,c("startDateTime", "endDateTime","zero_check_high")], - by = c("startDateTime", "endDateTime")) - - #test to see if average of 2 temps is < 1 degree - setDT(temp_data_join) - - temp_data_join[, avg_zero_check := rowMeans(cbind(zero_check_low, zero_check_high), na.rm = TRUE)] - temp_data_join[is.nan(avg_zero_check), avg_zero_check := NA_real_] - temp_data_join[, zero_check := ifelse(is.na(avg_zero_check), NA_integer_, as.integer(avg_zero_check < 1))] - - ## If neighbor join exists, merge and coalesce (prefer closest; else neighbor) - if (is.data.frame(temp_data_join) && nrow(temp_data_join) > 0) { - setDT(temp_data_join) - temp_data_join[, `:=`( - startDateTime = as.POSIXct(startDateTime, tz = "UTC"), - endDateTime = as.POSIXct(endDateTime, tz = "UTC"), - zero_check = as.integer(zero_check) # ensure 0/1/NA - )] - - # Exact equality join on [startDateTime, endDateTime]. - combined <- merge( - temp_data, - temp_data_join[, .(startDateTime, endDateTime, zero_check)], - by = c("startDateTime", "endDateTime"), - all.x = TRUE, - sort = FALSE - ) - - # Fallback: if temp_flag is NA, use neighbor zero_check - # fcoalesce is a data.table helper (vector-wise coalesce) - combined[, temp_flag := fcoalesce(temp_flag, zero_check)] - combined[, zero_check := NULL] - - temp_data <- combined - } - - } #if statement looking for neighbors - - #any remaining NA values for temp_check == -1 - temp_data$temp_flag[is.na(temp_data$temp_flag)] <- -1 - - } #end of assigning temp test. - - - #next apply flag to the appropriate soil moisture flag. - - #ensure we have proper date formats - - # Non-equi join: assign each 10-sec row the flag of the minute interval it falls into - dt_sm <- as.data.table(data_sm) - dt_temp <- as.data.table(temp_data)[, .(startDateTime, endDateTime, temp_flag)] - - dt_sm[, `:=`( - readout_start = as.POSIXct(readout_time, tz = "UTC"), - readout_end = as.POSIXct(readout_time, tz = "UTC") - )] - - dt_temp[, `:=`( - startDateTime = as.POSIXct(startDateTime, tz = "UTC"), - endDateTime = as.POSIXct(endDateTime, tz = "UTC"), - temp_flag = as.integer(temp_flag) # TRUE->1, FALSE->0 - )] - - # foverlaps requires a key on y (the interval table) - setkey(dt_temp, startDateTime, endDateTime) - - # Use the point-interval columns you created in x - joined <- foverlaps( - x = dt_sm[, .(readout_start, readout_end, .rows = .I)], - y = dt_temp[, .(startDateTime, endDateTime, temp_flag)], - by.x = c("readout_start", "readout_end"), - by.y = c("startDateTime", "endDateTime"), - type = "within", # inclusive [start, end] - nomatch = NA_integer_ - ) - - new_qf <- data_sm[[qf_name]] - - # rows that have a matching minute interval - has_match <- !is.na(joined$temp_flag) - - # Overwrite with 1/0 where we matched - new_qf[ joined$.rows[has_match] ] <- joined$temp_flag[has_match] - - # 4) Assign back - data_sm[[qf_name]] <- new_qf - - }#end logic for running test - - } #end logic looping through all the columns. - - #filter flags to just columns of interest - qf_flags <- data_sm[,c("readout_time", qf_cols)] - #Read in flag data - fileQfPlau <- base::list.files(dirInQf,pattern='Plausibility.parquet',full.names=FALSE) - - qfPlau <- NEONprocIS.base::def.read.parq.ds(fileIn=fs::path(dirInQf,fileQfPlau), - VarTime='readout_time', - RmvDupl=TRUE, - Df=TRUE, - log=log) - - qfAll <- dplyr::full_join(qfPlau, qf_flags, by = "readout_time") - #write out logic. - - ###########pick it up here! write it out, clean it up and continue testing. - -###reorder the variables -# Your column names vector -cols <- names(qfAll) - -# Define sorting function -sort_cols <- function(cols) { - # Keep readout_time first - readout <- cols[cols == "readout_time"] - - # Get all QF columns - qf_cols <- cols[grepl("QF$", cols)] - - # Extract components for sorting - sort_df <- data.frame( - col = qf_cols, - stringsAsFactors = FALSE - ) - - # Extract variable type, depth, and QF type - sort_df$var_type <- ifelse(grepl("^VSIC", sort_df$col), "1_VSIC", - ifelse(grepl("^VSWCfactory", sort_df$col), "2_VSWCfactory", - ifelse(grepl("^VSWCsoilSpecific", sort_df$col), "3_VSWCsoilSpecific", - "4_temp"))) - - # Extract depth number - sort_df$depth <- as.numeric(gsub(".*Depth(\\d+).*|.*depth(\\d+).*", "\\1\\2", sort_df$col)) - - # Extract QF type with ordering - sort_df$qf_type <- ifelse(grepl("NullQF$", sort_df$col), "1_Null", - ifelse(grepl("GapQF$", sort_df$col), "2_Gap", - ifelse(grepl("RangeQF$", sort_df$col), "3_Range", - ifelse(grepl("StepQF$", sort_df$col), "4_Step", - ifelse(grepl("PersistenceQF$", sort_df$col), "5_Persistence", - ifelse(grepl("SpikeQF$", sort_df$col), "6_Spike", - "7_TempTest")))))) - - # Sort by var_type, then depth, then qf_type - sort_df <- sort_df[order(sort_df$depth, sort_df$qf_type, sort_df$var_type), ] - - # Combine readout_time first, then sorted QF columns - c(readout, sort_df$col) -} - -# Apply sorting -sorted_cols <- sort_cols(cols) - -# Reorder your dataframe -qfAll<- qfAll[, sorted_cols] - -nameFileQfOutFlag <- fileQfPlau - -nameFileQfOutFlag <- fs::path(dirOutQf,nameFileQfOutFlag) - - rptWrte <- - base::try(NEONprocIS.base::def.wrte.parq( - data = qfAll, - NameFile = nameFileQfOutFlag, - log=log - ), - silent = TRUE) - - if ('try-error' %in% base::class(rptWrte)) { - log$error(base::paste0( - 'Cannot write output to ', - nameFileQfOutFlag, - '. ', - attr(rptWrte, "condition") - )) - stop() - } else { - log$info(base::paste0( - 'Wrote updated flags data to file ', - nameFileQfOutFlag - )) - } - - return() -} #end function - diff --git a/modules_combined/analyze_pad_qaqc_envscn/Dockerfile b/modules_combined/analyze_pad_qaqc_envscn/Dockerfile new file mode 100644 index 000000000..d3b0596da --- /dev/null +++ b/modules_combined/analyze_pad_qaqc_envscn/Dockerfile @@ -0,0 +1,66 @@ +# Dockerfile for NEON IS Data Processing - Combined padded_timeseries_analyzer and Basic QA/QC module (plausibility) and enviroscan custom flagging +# Example command (must be run from project root directory to include both modules in Docker context): +# Build with the following command +# docker build --no-cache -t neon-is-qaqc-plau-envscn -f ./modules_combined/analyze_pad_qaqc_envsc/Dockerfile . + +# Start with the neon-is-pack-qaqc-r image. +FROM us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-pack-qaqc-r:v1.1.10 + +ARG MODULE_DIR="modules" +ARG APP_DIR="padded_timeseries_analyzer" +ARG COMMON_DIR="common" +ARG CONTAINER_APP_DIR="/usr/src/app" +ENV PYTHONPATH="${PYTHONPATH}:${CONTAINER_APP_DIR}" + +WORKDIR ${CONTAINER_APP_DIR} + +COPY ${MODULE_DIR}/${APP_DIR}/requirements.txt ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt + + +RUN apt update && \ + apt-get install -y --no-install-recommends \ + python3.8 && \ + apt install -y python3-pip && \ + python3 -mpip install --no-cache-dir --upgrade pip setuptools wheel && \ + python3 -mpip install --no-cache-dir -r ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt && \ + apt-get autoremove -y && \ + apt-get autoclean -y && \ + rm -rf /var/lib/apt/lists/* + +# Copy in python code +COPY ${MODULE_DIR}/${APP_DIR} ${CONTAINER_APP_DIR}/${APP_DIR} +COPY ${MODULE_DIR}/${COMMON_DIR} ${CONTAINER_APP_DIR}/${COMMON_DIR} + +# Build in the threshold selection module +ARG MODULE_DIR="flow" +ARG APP_DIR="flow.qaqc.plau" + +# Copy the lockfile and restore known working versions of R dependency packages +COPY ./${MODULE_DIR}/${APP_DIR}/renv.lock ./renv.lock +RUN R -e 'renv::restore(lockfile="./renv.lock")' + +# Copy in application code +COPY ./${MODULE_DIR}/${APP_DIR}/wrap.qaqc.plau.R . +COPY ./${MODULE_DIR}/${APP_DIR}/flow.qaqc.plau.R . + +# Create app user +RUN groupadd appuser && \ + useradd appuser -g appuser + +# Build in the custom radiation flags +ARG APP_DIR2="flow.envscn.temp.flags" + +# Copy the lockfile and restore known working versions of R dependency packages +COPY ./${MODULE_DIR}/${APP_DIR2}/renv.lock ./renv.lock2 +RUN R -e 'renv::restore(lockfile="./renv.lock2")' + +# Copy in application code +COPY ./${MODULE_DIR}/${APP_DIR2}/wrap.envscn.temp.flags.R . +COPY ./${MODULE_DIR}/${APP_DIR2}/flow.envscn.temp.flags.R . +COPY ./${MODULE_DIR}/${APP_DIR2}/def.apply.temp.flags.R . +COPY ./${MODULE_DIR}/${APP_DIR2}/def.calc.temp.flags.R . +COPY ./${MODULE_DIR}/${APP_DIR2}/def.find.temp.sensor.R . +COPY ./${MODULE_DIR}/${APP_DIR2}/def.load.temp.sensors.R . +COPY ./${MODULE_DIR}/${APP_DIR2}/def.sort.qf.cols.R . +# Run as app user +USER appuser From d814c1433cc4da8bcdcdd34a986974a573112896 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 18 Feb 2026 16:40:07 -0700 Subject: [PATCH 21/46] clean up of threshold tests --- .../concH2oSoilSalinity_threshold.yaml | 47 +++-------- .../concH2oSoilSalinity_threshold_test2.yaml | 79 ------------------- .../concH2oSoilSalinity_threshold_test3.yaml | 40 ---------- 3 files changed, 9 insertions(+), 157 deletions(-) delete mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test2.yaml delete mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml index 4414b97e3..d70411543 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml @@ -4,48 +4,19 @@ pipeline: transform: # image_pull_secrets: # - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:v1.0.0 + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:sha-efbad02 cmd: - - sh - - "-c" - - |- - /bin/bash <<'EOF' - - # Create output directories using environment variables - mkdir -p $OUT_PATH_IC - mkdir -p $OUT_PATH_FACT - mkdir -p $OUT_PATH_SSWC - - if [ ${CTXT_IC+x} ]; then - export CTXT=$CTXT_IC - export OUT_PATH=$OUT_PATH_IC - python3 -m threshold_loader.threshold_loader_main - fi - - if [ ${CTXT_FACTWC+x} ]; then - export CTXT=$CTXT_FACTWC - export OUT_PATH=$OUT_PATH_FACT - python3 -m threshold_loader.threshold_loader_main - fi - - if [ ${CTXT_SSWC+x} ]; then - export CTXT=$CTXT_SSWC - export OUT_PATH=$OUT_PATH_SSWC - python3 -m threshold_loader.threshold_loader_main - fi - - EOF + - /bin/bash + stdin: + - '#!/bin/bash' + - python3 -m threshold_loader.threshold_loader_main env: - OUT_PATH_IC: /pfs/out/ion-content - OUT_PATH_FACT: /pfs/out/water-content-factory - OUT_PATH_SSWC: /pfs/out/water-content-soil-spec + OUT_PATH: /pfs/out LOG_LEVEL: INFO - # Separate multiple terms with a pipe (|). Enter "none" to retrieve all terms TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 - # Separate multiple contexts with a pipe (|). E.g. (par-met|upward-facing). Enter "none" for no context filtering. - CTXT_IC: soil|ion-content - CTXT_FACTWC: soil|water-content|factory - CTXT_SSWC: soil|water-content|soil-specific + CTXT_1: soil|ion-content + CTXT_2: soil|water-content|factory + CTXT_3: soil|water-content|soil-specific secrets: - name: pdr-secret mount_path: /var/db_secret diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test2.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test2.yaml deleted file mode 100644 index 873a73982..000000000 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test2.yaml +++ /dev/null @@ -1,79 +0,0 @@ ---- -pipeline: - name: concH2oSoilSalinity_threshold_test2 -transform: - # image_pull_secrets: - # - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:v1.0.0 - cmd: - - sh - - "-c" - - |- - /bin/bash <<'EOF' - # Create output directories - - # Install jq for testing (download static binary) - curl -L https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-amd64 -o /tmp/jq - chmod +x /tmp/jq - - # Create output directories using environment variables - mkdir -p $OUT_PATH_IC - mkdir -p $OUT_PATH_FACT - mkdir -p $OUT_PATH_SSWC - - if [ ${CTXT_IC+x} ]; then - export CTXT=$CTXT_IC - export OUT_PATH=$OUT_PATH_IC - python3 -m threshold_loader.threshold_loader_main - fi - - if [ ${CTXT_FACTWC+x} ]; then - export CTXT=$CTXT_FACTWC - export OUT_PATH=$OUT_PATH_FACT - python3 -m threshold_loader.threshold_loader_main - fi - - if [ ${CTXT_SSWC+x} ]; then - export CTXT=$CTXT_SSWC - export OUT_PATH=$OUT_PATH_SSWC - python3 -m threshold_loader.threshold_loader_main - fi - - # Merge using the downloaded jq - /tmp/jq -s '{thresholds: ([.[].thresholds // []] | flatten | unique_by(.threshold_name + .term_name + .location_name))}' \ - /pfs/out/ion-content/thresholds.json \ - /pfs/out/water-content-factory/thresholds.json \ - /pfs/out/water-content-soil-spec/thresholds.json \ - > /pfs/out/thresholds.json - - EOF - env: - OUT_PATH_IC: /pfs/out/ion-content - OUT_PATH_FACT: /pfs/out/water-content-factory - OUT_PATH_SSWC: /pfs/out/water-content-soil-spec - LOG_LEVEL: INFO - TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 - CTXT_IC: soil|ion-content - CTXT_FACTWC: soil|water-content|factory - CTXT_SSWC: soil|water-content|soil-specific - secrets: - - name: pdr-secret - mount_path: /var/db_secret -input: - pfs: - repo: enviroscan_cron_daily_and_date_control_tick - glob: /* - empty_files: true -autoscaling: true -resource_requests: - memory: 32M - cpu: 0.05 -resource_limits: - memory: 200M - cpu: 0.5 -sidecar_resource_requests: - memory: 120M - cpu: 0.1 -scheduling_spec: - node_selector: - cloud.google.com/compute-class: pach-pipeline-class \ No newline at end of file diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml deleted file mode 100644 index 5324acc6a..000000000 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold_test3.yaml +++ /dev/null @@ -1,40 +0,0 @@ ---- -pipeline: - name: concH2oSoilSalinity_threshold_test3 -transform: - # image_pull_secrets: - # - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-threshold-loader:sha-efbad02 - cmd: - - /bin/bash - stdin: - - '#!/bin/bash' - - python3 -m threshold_loader.threshold_loader_main - env: - OUT_PATH: /pfs/out - LOG_LEVEL: INFO - TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 - CTXT_1: soil|ion-content - CTXT_2: soil|water-content|factory - CTXT_3: soil|water-content|soil-specific - secrets: - - name: pdr-secret - mount_path: /var/db_secret -input: - pfs: - repo: enviroscan_cron_daily_and_date_control_tick - glob: /* - empty_files: true -autoscaling: true -resource_requests: - memory: 32M - cpu: 0.05 -resource_limits: - memory: 200M - cpu: 0.5 -sidecar_resource_requests: - memory: 120M - cpu: 0.1 -scheduling_spec: - node_selector: - cloud.google.com/compute-class: pach-pipeline-class \ No newline at end of file From 5f59d3a01e763645c7cced963ca01476d009cc15 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 18 Feb 2026 16:41:06 -0700 Subject: [PATCH 22/46] comments --- pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml index d70411543..577088ef1 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_threshold.yaml @@ -14,6 +14,7 @@ transform: OUT_PATH: /pfs/out LOG_LEVEL: INFO TERM: VSICDepth01|VSICDepth02|VSICDepth03|VSICDepth04|VSICDepth05|VSICDepth06|VSICDepth07|VSICDepth08|VSWCfactoryDepth01|VSWCfactoryDepth02|VSWCfactoryDepth03|VSWCfactoryDepth04|VSWCfactoryDepth05|VSWCfactoryDepth06|VSWCfactoryDepth07|VSWCfactoryDepth08|VSWCsoilSpecificDepth01|VSWCsoilSpecificDepth02|VSWCsoilSpecificDepth03|VSWCsoilSpecificDepth04|VSWCsoilSpecificDepth05|VSWCsoilSpecificDepth06|VSWCsoilSpecificDepth07|VSWCsoilSpecificDepth08 + #if you are pulling multiple term and they have different contexts, list them here numerically. CTXT_1: soil|ion-content CTXT_2: soil|water-content|factory CTXT_3: soil|water-content|soil-specific From 42b759442d02604315fa713d1ee828467060d6a9 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Fri, 20 Feb 2026 13:21:50 -0700 Subject: [PATCH 23/46] minor code changes discovered in unit testing and creation of unit tests and test data. --- flow/flow.envscn.temp.flags/TEST_SUMMARY.txt | 253 + .../def.find.temp.sensor.R | 4 +- flow/flow.envscn.temp.flags/run_unit_tests.R | 16 + .../wrap.envscn.temp.flags.R | 2 +- ...enviroscan_CFGLOC105360_2025-10-17.parquet | Bin 0 -> 19395 bytes ...05360_2025-10-17_flagsPlausibility.parquet | Bin 0 -> 75598 bytes .../CFGLOC105360/location/CFGLOC105360.json | 22 + .../location/enviroscan_46446_locations.json | 96 + .../CFGLOC105360/threshold/thresholds.json | 5604 +++++++++++++++++ ...enviroscan_CFGLOC105360_2025-10-17.parquet | Bin 0 -> 30983 bytes ...05360_2025-10-17_flagsPlausibility.parquet | Bin 0 -> 78997 bytes .../CFGLOC105360/threshold/thresholds.json | 5604 +++++++++++++++++ ...enviroscan_CFGLOC105360_2025-10-17.parquet | Bin 0 -> 19395 bytes ...05360_2025-10-17_flagsPlausibility.parquet | Bin 0 -> 75598 bytes .../CFGLOC105360/threshold/thresholds.json | 1 + ...M005503_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 31787 bytes .../group/CFGLOC105367.json | 27 + .../location/CFGLOC105367.json | 39 + .../location/prt_20057_locations.json | 409 ++ ...M005501_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 18420 bytes .../location/CFGLOC105363.json | 39 + .../location/prt_24772_locations.json | 409 ++ ...M005502_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 18423 bytes .../location/CFGLOC105365.json | 39 + .../location/prt_18574_locations.json | 409 ++ ...M005503_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 18147 bytes .../location/CFGLOC105367.json | 39 + .../location/prt_20057_locations.json | 409 ++ ...M005504_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 17525 bytes .../location/CFGLOC105369.json | 39 + .../location/prt_20037_locations.json | 409 ++ ...M005505_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 17309 bytes .../location/CFGLOC105371.json | 39 + .../location/prt_23800_locations.json | 409 ++ ...M005506_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 18503 bytes .../location/CFGLOC105373.json | 39 + .../location/prt_16828_locations.json | 409 ++ ...enviroscan_CFGLOC105360_2025-10-18.parquet | Bin 0 -> 15760 bytes ...n_CFGLOC105360_2025-10-18_flagsCal.parquet | Bin 0 -> 10861 bytes .../CFGLOC105360/location/CFGLOC105360.json | 22 + .../location/enviroscan_46446_locations.json | 96 + ...GLOC105360_2025-10-18_uncertaintyCoef.json | 1202 ++++ ...C105360_2025-10-18_uncertaintyData.parquet | Bin 0 -> 26194 bytes ...M005501_2025-10-18_ST_1_minute_001.parquet | Bin 0 -> 18425 bytes ...005501_2025-10-18_ST_30_minute_030.parquet | Bin 0 -> 13416 bytes .../location/CFGLOC105363.json | 39 + .../location/prt_24772_locations.json | 409 ++ .../location/prt_24773_locations.json | 409 ++ ...M005502_2025-10-18_ST_1_minute_001.parquet | Bin 0 -> 18387 bytes ...005502_2025-10-18_ST_30_minute_030.parquet | Bin 0 -> 13413 bytes .../location/CFGLOC105365.json | 39 + .../location/prt_18574_locations.json | 409 ++ ...M005503_2025-10-18_ST_1_minute_001.parquet | Bin 0 -> 17968 bytes ...005503_2025-10-18_ST_30_minute_030.parquet | Bin 0 -> 13375 bytes .../location/CFGLOC105367.json | 39 + .../location/prt_20057_locations.json | 409 ++ ...M005504_2025-10-18_ST_1_minute_001.parquet | Bin 0 -> 17987 bytes ...005504_2025-10-18_ST_30_minute_030.parquet | Bin 0 -> 13291 bytes .../location/CFGLOC105369.json | 39 + .../location/prt_20037_locations.json | 409 ++ ...M005505_2025-10-18_ST_1_minute_001.parquet | Bin 0 -> 17728 bytes ...005505_2025-10-18_ST_30_minute_030.parquet | Bin 0 -> 13262 bytes .../location/CFGLOC105371.json | 39 + .../location/prt_23800_locations.json | 409 ++ ...M005506_2025-10-18_ST_1_minute_001.parquet | Bin 0 -> 18427 bytes ...005506_2025-10-18_ST_30_minute_030.parquet | Bin 0 -> 11808 bytes .../location/CFGLOC105373.json | 39 + .../location/prt_16828_locations.json | 409 ++ ...M005503_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 18147 bytes ...005503_2025-10-17_ST_30_minute_030.parquet | Bin 0 -> 13366 bytes ...M005504_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 17525 bytes ...005504_2025-10-17_ST_30_minute_030.parquet | Bin 0 -> 13311 bytes ...M005503_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 18147 bytes .../location/CFGLOC105367.json | 39 + .../location/prt_20057_locations.json | 409 ++ ...M005504_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 17525 bytes .../location/CFGLOC105369.json | 39 + .../location/prt_20037_locations.json | 407 ++ ...M005505_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 17309 bytes .../location/CFGLOC105371.json | 39 + .../location/prt_23800_locations.json | 409 ++ .../testthat/test-wrap-envscn-temp-flags.R | 870 +++ 82 files changed, 21391 insertions(+), 3 deletions(-) create mode 100644 flow/flow.envscn.temp.flags/TEST_SUMMARY.txt create mode 100644 flow/flow.envscn.temp.flags/run_unit_tests.R create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/enviroscan_CFGLOC105360_2025-10-17_flagsPlausibility.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/CFGLOC105360.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/enviroscan_46446_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/enviroscan_CFGLOC105360_2025-10-17_flagsPlausibility.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/enviroscan_CFGLOC105360_2025-10-17_flagsPlausibility.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/CFGLOC105365.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/prt_18574_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/CFGLOC105373.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/prt_16828_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-18.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/enviroscan_CFGLOC105360_2025-10-18_flagsCal.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/CFGLOC105360.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/enviroscan_46446_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/uncertainty_coef/enviroscan_CFGLOC105360_2025-10-18_uncertaintyCoef.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/uncertainty_data/enviroscan_CFGLOC105360_2025-10-18_uncertaintyData.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-18_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-18_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24773_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-18_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-18_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/CFGLOC105365.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/prt_18574_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-18_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-18_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-18_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-18_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/CFGLOC105373.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/prt_16828_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json create mode 100644 flow/tests/testthat/test-wrap-envscn-temp-flags.R diff --git a/flow/flow.envscn.temp.flags/TEST_SUMMARY.txt b/flow/flow.envscn.temp.flags/TEST_SUMMARY.txt new file mode 100644 index 000000000..86e05ecfb --- /dev/null +++ b/flow/flow.envscn.temp.flags/TEST_SUMMARY.txt @@ -0,0 +1,253 @@ +################################################################################ +# Test Suite Summary for Temperature Flag Functions +# File: test-wrap-envscn-temp-flags.R +# Author: Teresa Burlingame +# Date: February 2026 +################################################################################ + +================================================================================ +UNIT TESTS (Tests 1-5) +================================================================================ + +TEST 1: def.sort.qf.cols - Column Sorting Function +-------------------------------------------------------------------------------- +Description: Tests the sorting of quality flag columns +Test Cases: + - Correctly sorts quality flag columns with depth numbers + - Handles empty input + - Handles columns without depth numbers + +Key Validations: + - Static columns (readout_time) come first + - tempTest columns are sorted numerically by depth + - All columns are preserved in output + +================================================================================ + +TEST 2: def.find.temp.sensor - Sensor Selection Logic +-------------------------------------------------------------------------------- +Description: Tests the logic for finding the closest temperature sensor +Test Cases: + - Finds closest temperature sensor to target depth + - Handles sensors with no valid depths (all NA) + - Breaks ties by preferring shallower sensors + - Identifies edge sensors (shallowest/deepest) without neighbors + - Filters out sensors with NA depths + +Key Validations: + - Returns sensor closest to target depth + - Correctly identifies higher (shallower) and lower (deeper) neighbors + - Returns NULL when no valid sensors exist + - Handles boundary conditions properly + +================================================================================ + +TEST 3: def.load.temp.sensors - Temperature Sensor Data Loading +-------------------------------------------------------------------------------- +Description: Tests loading temperature sensor metadata from location files +Test Cases: + - Loads temperature sensor metadata successfully + - Handles empty directories (no data) + - Averages depths when multiple location files exist for same sensor + - Returns empty data frame when no location files present + - Handles missing z_offset field in location files (results in NA depth) + +Key Validations: + - Returns data frame with required columns: sensor_id, depth_m, data_path, location_path + - Sensor IDs have correct prefix (temp-soil_) + - Depths are numeric and negative (below surface) + - File paths exist and are accessible + - Multiple depths for same sensor are averaged + +================================================================================ + +TEST 4: def.calc.temp.flags - Temperature Flag Calculation +-------------------------------------------------------------------------------- +Description: Tests the core temperature flag calculation logic +Test Cases: + - Basic flag calculation (freezing check: temp < uncertainty) + - Handles neighbors that are too far away (exceeds threshold distance) + - Falls back to neighbor data when primary sensor is flagged + +Key Validations: + - Flag = 1 when soilTempMean < soilTempExpUncert (potential freezing) + - Flag = 0 when temperature is above uncertainty threshold + - Flag = -1 when primary sensor is bad and neighbors too far or unavailable + - Correctly averages neighbor sensor data when needed + - Respects distance threshold for neighbor selection + +Flag Meanings: + 0 = Data is good (not frozen) + 1 = Data flagged (potentially frozen) + -1 = Cannot determine (missing or unreliable data) + +================================================================================ + +TEST 5: def.apply.temp.flags - Flag Application to High-Frequency Data +-------------------------------------------------------------------------------- +Description: Tests joining minute-interval flags to high-frequency soil moisture +Test Cases: + - Correctly joins flags to high-frequency data based on timestamps + - Handles timestamps outside any temperature interval + +Key Validations: + - High-frequency data points correctly matched to minute intervals + - Points outside temperature intervals retain -1 flag + - Temporal overlap logic works correctly (foverlaps) + +================================================================================ +INTEGRATION TESTS (Test 6) +================================================================================ + +Description: Full end-to-end testing with realistic data scenarios +All integration tests verify: + - Output directory structure created correctly + - Flag parquet files written successfully + - All 8 tempTestDepth columns present (depth01 through depth08) + - Flags have valid values only: -1, 0, or 1 + - Timestamp column (readout_time) preserved + +-------------------------------------------------------------------------------- +TEST 6.1: Baseline Test with Good Data +-------------------------------------------------------------------------------- +Scenario: Normal operating conditions +Data: + - Good soil moisture data with valid depths + - Good temperature sensor data with valid locations +Expected Result: + - Flags calculated successfully + - Mix of 0, 1, and -1 values based on actual conditions + +-------------------------------------------------------------------------------- +TEST 6.2: Missing Soil Moisture Depths +-------------------------------------------------------------------------------- +Scenario: Depth information missing from soil moisture data files +Data: + - Soil moisture data WITHOUT depth metadata + - Good temperature sensor data +Expected Result: + - All flags set to -1 (cannot match sensors to depths) + - Process completes without error + +-------------------------------------------------------------------------------- +TEST 6.3: Freezing Temperature Conditions +-------------------------------------------------------------------------------- +Scenario: All temperature values indicate freezing conditions +Data: + - Good soil moisture data + - Temperature data with all values < uncertainty threshold +Expected Result: + - Most/all flags set to 1 (flagged as frozen) + - Demonstrates frozen soil detection logic + +-------------------------------------------------------------------------------- +TEST 6.4: Missing Temperature Data Directory +-------------------------------------------------------------------------------- +Scenario: Temperature sensor data directory does not exist +Data: + - Good soil moisture data + - Non-existent temperature data path +Expected Result: + - All flags set to -1 (no temperature data available) + - Process completes without error (graceful degradation) + +-------------------------------------------------------------------------------- +TEST 6.5: Missing z_offset in Location Files +-------------------------------------------------------------------------------- +Scenario: Temperature sensor location files lack z_offset field +Data: + - Good soil moisture data + - Temperature location files missing z_offset field +Expected Result: + - Some flags may be -1 where depth cannot be determined + - Process continues with available data + - Valid values (0, 1, -1) only + +-------------------------------------------------------------------------------- +TEST 6.6: Missing Threshold Configuration File +-------------------------------------------------------------------------------- +Scenario: Required threshold configuration file is absent +Data: + - Soil moisture data WITHOUT threshold file + - Temperature data +Expected Result: + - Function throws error (threshold file is mandatory) + - Error handling test validates proper failure mode + +================================================================================ +TEST DATA ORGANIZATION +================================================================================ + +Test data is organized under: tests/testthat/pfs/envscn_temp_flags/ + +Structure: + temp/ # Temperature sensor data + tests/ + good_data/ # Normal operating data + multiple_depth/ # Multiple location files per sensor + no_locations/ # Missing location files + no_z_offset/ # Location files without z_offset + all_freezing/ # All temperatures below freezing + 2025/10/14/ # Empty directory for "no data" test + + enviroscan/ # Soil moisture data + tests/ + good_data/ # Normal operating data + no_depths/ # Missing depth metadata + no_thresholds/ # Missing threshold configuration + +Test data was copied from /scratch/pfs/ using copy_test_data.R or copy_test_data.sh + +================================================================================ +TESTING INSTRUCTIONS +================================================================================ + +To run all tests: + cd ~/github/NEON-IS-data-processing/flow/tests/testthat + Rscript run_temp_flag_tests.R + +To run tests interactively in R: + setwd("~/github/NEON-IS-data-processing/flow/tests/testthat") + library(testthat) + test_file("test-envscn-temp-flags.R") + +To run a specific test: + test_file("test-envscn-temp-flags.R", filter = "sort.qf.cols") + +Before running tests: + 1. Ensure test data is copied: source("copy_test_data.R") + 2. Verify working directory is tests/testthat/ + 3. Source required function files (or use run_temp_flag_tests.R) + +================================================================================ +COVERAGE SUMMARY +================================================================================ + +Total Test Suites: 11 +Total Test Assertions: 47+ + +Unit Test Coverage: + ✓ Column sorting and organization + ✓ Sensor depth matching logic + ✓ Sensor metadata loading and parsing + ✓ Flag calculation with fallback logic + ✓ Temporal flag application + +Integration Test Coverage: + ✓ Normal operating conditions + ✓ Missing metadata (depths, locations, z_offset) + ✓ Missing data (temperature sensors) + ✓ Edge conditions (freezing temperatures) + ✓ Error conditions (missing required files) + +Functions Tested: + - def.sort.qf.cols() + - def.find.temp.sensor() + - def.load.temp.sensors() + - def.calc.temp.flags() + - def.apply.temp.flags() + - wrap.envscn.temp.flags() + +================================================================================ +END OF TEST SUMMARY +================================================================================ diff --git a/flow/flow.envscn.temp.flags/def.find.temp.sensor.R b/flow/flow.envscn.temp.flags/def.find.temp.sensor.R index 1ed198d2c..79e6afa09 100644 --- a/flow/flow.envscn.temp.flags/def.find.temp.sensor.R +++ b/flow/flow.envscn.temp.flags/def.find.temp.sensor.R @@ -42,8 +42,8 @@ def.find.temp.sensor <- function(targetDepth, validSensors <- sensorDepthDf[!base::is.na(sensorDepthDf$depth_m), ] if (base::nrow(validSensors) == 0) { - log$error('No valid temperature sensors with depth information') - stop() + log$warn('no valid temperature depths found, all data will be flagged -1') + return(NULL) } # Calculate absolute difference from target depth diff --git a/flow/flow.envscn.temp.flags/run_unit_tests.R b/flow/flow.envscn.temp.flags/run_unit_tests.R new file mode 100644 index 000000000..54d41be5e --- /dev/null +++ b/flow/flow.envscn.temp.flags/run_unit_tests.R @@ -0,0 +1,16 @@ +#coverage report of unit tests. change directories to local repo as needed. + +library (covr) +setwd("~/GitHub/NEON-IS-data-processing/flow/tests/testthat") +baseDir = "~/GitHub/NEON-IS-data-processing/flow" +cov <- covr::file_coverage( + source_files = c(paste0(baseDir,"/flow.envscn.temp.flags/def.sort.qf.cols.R"), + paste0(baseDir,"/flow.envscn.temp.flags/def.find.temp.sensor.R"), + paste0(baseDir, "/flow.envscn.temp.flags/def.apply.temp.flags.R"), + paste0(baseDir,"/flow.envscn.temp.flags/wrap.envscn.temp.flags.R"), + paste0(baseDir,"/flow.envscn.temp.flags/def.calc.temp.flags.R"), + paste0(baseDir,"/flow.envscn.temp.flags/def.load.temp.sensors.R")), + test_files = "~/GitHub/NEON-IS-data-processing/flow/tests/testthat/test-wrap-envscn-temp-flags.R" +) + +covr::report(cov) diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R index 24a209e57..398fa4c6c 100644 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -209,7 +209,7 @@ wrap.envscn.temp.flags <- function(DirIn, log$debug(base::paste0('Running temperature test for ', col)) # Find target depth from maximum sensor depth in the data - targetDepth <- base::max(dataSm[[col]], na.rm = TRUE) + targetDepth <- suppressWarnings(base::max(dataSm[[col]], na.rm = TRUE)) # Check if target depth is valid if (!base::is.finite(targetDepth) || base::is.na(targetDepth)) { diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet new file mode 100644 index 0000000000000000000000000000000000000000..ed9637905a79396a35f8328683d1311346861c68 GIT binary patch literal 19395 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 P3q&UWF%s!9dSm|&^kw*{ literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/CFGLOC105360.json b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/CFGLOC105360.json new file mode 100644 index 000000000..1262bf2fa --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/CFGLOC105360.json @@ -0,0 +1,22 @@ +{ + "features":[ + { + "Data Rate":"0.1", + "geometry":null, + "HOR":"005", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-17T00:00:00Z", + "end_date":"2025-10-18T00:00:00Z" + } + ], + "description":"Great Smoky Mountains Soil Water Content Profile SP5", + "name":"CFGLOC105360", + "site":"GRSM" + }, + "VER":"501" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/enviroscan_46446_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/enviroscan_46446_locations.json new file mode 100644 index 000000000..aa19c24b0 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/enviroscan_46446_locations.json @@ -0,0 +1,96 @@ +{ + "features":[ + { + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.065,-0.165,-0.265,-0.365,-0.465,-0.565,-0.665,-0.865", + "geometry":null, + "HOR":"005", + "LvlMeasSoil":"8", + "properties":{ + "install_date":"2025-10-17T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 2, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 170, + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0.56, + "y_offset": 2.9, + "z_offset": -0.005 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105360", + "remove_date":"2025-10-18T00:00:00Z", + "site":"GRSM" + }, + "VER":"501" + } + ], + "source_id": 46446, + "source_type":"enviroscan", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json new file mode 100644 index 000000000..78012a2e6 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json @@ -0,0 +1,5604 @@ +{ + "thresholds":[ + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth02", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth03", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth04", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth05", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth06", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth07", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth08", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth01", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Step Test value" + } + ] +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet new file mode 100644 index 0000000000000000000000000000000000000000..f665ab69ec966e0c65bd1b057c4edb1e34cc7249 GIT binary patch literal 30983 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/enviroscan_CFGLOC105360_2025-10-17_flagsPlausibility.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/enviroscan_CFGLOC105360_2025-10-17_flagsPlausibility.parquet new file mode 100644 index 0000000000000000000000000000000000000000..643b8e595c49acd55de08cc27535729df4fc930a GIT binary patch literal 78997 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json new file mode 100644 index 000000000..78012a2e6 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json @@ -0,0 +1,5604 @@ +{ + "thresholds":[ + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth02", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth02", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth03", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth03", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth04", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth04", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth05", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth05", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth06", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth06", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth07", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth07", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth08", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth08", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSICDepth01", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 360, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 3600, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 20000, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"ion-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2000-01-01T00:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSICDepth01", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth02", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth03", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth04", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth05", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth06", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth07", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth08", + "threshold_name":"Step Test value" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"factory", +"soil", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCfactoryDepth01", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth02", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth03", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth04", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth05", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth06", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth07", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth08", + "threshold_name":"Step Test value" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 12, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking MAD" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking maximum consecutive points (n)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 60, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking maximum (%) missing points per window" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value":null, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":"A", + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking Method" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 10, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking window size - points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Despiking window step - points." + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Gap Test value - # missing points" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.0001, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Persistence (change)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 86400, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Persistence (time - seconds)" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Hard Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Hard Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 1, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Soft Max" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": -0.4, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Range Threshold Soft Min" + }, + { + "context":[ +"soil", +"soil-specific", +"water-content" + ], + "end_date":null, + "end_day_of_year":null, + "is_date_constrained":"N", + "location_name":"GRSM", + "number_value": 0.01, + "start_date":"2021-10-13T12:00:00Z", + "start_day_of_year":null, + "string_value":null, + "term_name":"VSWCsoilSpecificDepth01", + "threshold_name":"Step Test value" + } + ] +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-17.parquet new file mode 100644 index 0000000000000000000000000000000000000000..ed9637905a79396a35f8328683d1311346861c68 GIT binary patch literal 19395 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 P3q&UWF%s!9dSm|&^kw*{ literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json new file mode 100644 index 000000000..5c2015a39 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/threshold/thresholds.json @@ -0,0 +1 @@ +{ "thresholds":[]} \ No newline at end of file diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..6da0ee7f937684407011072ad93298b6098ddc13 GIT binary patch literal 31787 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 aa0B021HQWXzs`S1(xOO6%D_Jp{{H}uZ2_nN literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json new file mode 100644 index 000000000..7787fe84d --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json @@ -0,0 +1,27 @@ +{ + "features": [ + { + "domain": "D07", + "geometry": null, + "HOR": "005", + "properties": { + "active_periods": [ + { + "start_date": "2025-10-17T00:00:00Z", + "end_date": "2025-10-18T00:00:00Z" + } + ], + "data_product_ID": [ + "DP1.00041.001" + ], + "group": "temp-soil_GRSM005503", + "name": "CFGLOC105367" + }, + "site": "GRSM", + "type": "Feature", + "VER": "503", + "visibility_code": "public" + } + ], + "type": "FeatureCollection" +} \ No newline at end of file diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json new file mode 100644 index 000000000..c2c02e79d --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.165", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105367", + "VER":"503", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-17T00:00:00Z", + "end_date":"2025-10-18T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z3 Depth", + "domain":"D07", + "name":"CFGLOC105367", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth3", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json new file mode 100644 index 000000000..8a391859c --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.165", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-17T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.165 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105367", + "remove_date":"2025-10-18T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105367", + "subLocation":"depth3", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"503" + } + ], + "source_id": 20057, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..8307df09f1386dec54b1232698f40677cd751dbf GIT binary patch literal 18420 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 h*BXK#P@!#{0$+@0kGV%ZW`46+EirD}F literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json new file mode 100644 index 000000000..cb01e58ab --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.025", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105363", + "VER":"501", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-17T00:00:00Z", + "end_date":"2025-10-18T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z1 Depth", + "domain":"D07", + "name":"CFGLOC105363", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth1", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json new file mode 100644 index 000000000..acb77a1f0 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.025", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-17T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.005 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105363", + "remove_date":"2025-10-18T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105363", + "subLocation":"depth1", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"501" + } + ], + "source_id": 24772, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..0570c0dcece2f8dafa1354835ba986d4548dbd11 GIT binary patch literal 18423 zcmeHP33wD$wywr5s6c~(As7_31}7ySX$U-)<6Gti8L_|eo z98{FTZ2%D%7tFX|iCbh7N8|zHQf{b-45Fi`=)C__Z68kbt(o5AW$L-3S1L7cmzCAHI_Z zj=A)Az$c!$6nI5uB=E%##sZU@Vu5dP6M%UuCj#G%nglF)$qM`;&jGAIlmz_#`ZStW%-2U2TpmX{&!2L&_2TF5a1b!O!3d_aUtnJ7qhx*r*JLofv zA#dAd8bcqUVSU5;jtJ*`gJ^mEZx}PG8!E#vB}d)o;G@H2>UV)GOV&~0gBHy%ez83M zqNR&UY-Y0v>^~Sm}9@^E-=4dNEr3^}))_Mq~{bmPHO@AH8?0XfceL;nTxso!Hb?FwMH10VFTmLsA ze6ktM?<{N?kk1Y&kzmXX?iqtpHk<+^WZ(j#91wdf;sK!=Gu;WIQl4x*{pKLYYMegKqy^Bz!b{0Gpw zW*^ZD-v-L=w}9H4ZvdIKJAqREHfrZ0)o|K26l2ahdn-_${Q^{=(#nwAYzwqz3JP#REjq+rSvx|}I<5`gMnJCSFIKyCkIYJYqsrhIh; znoKxFgPf+v!P4Jtf6HjM_#BQ6E`ya*yppg7Rv-%|V2ix3Uc zl_T_MP(B&nb2R9FrbmN5%^M9t)H59on}TZtKZVt6gVufywkK_-piq8iLQrTIA}DxH z`Hq4@d-!`APX7+5Uhxf(ktryYtRp~c$rnIA_cNemKM2E({0NY!p|6#_3sfo(0GTEV z3RR<^VA}sq?T;uZw78v^K9piY+3*@rilLa0*HcXJf25eO9@1mtf@i7yGR1_-KLymJ zC%~$Q1aEx|1bM`xKxW<=pi=P&23Ju;@V{S8O{Oc=e@WkCfDMycp*5yPWw+yJ> zeLK*ae;bWf+yYca-%Rvp7Xr0r3JHGDTp%-e4$=QfA)&q129yU=NT_>Tpw`largT+3 znt!K&;15$kSaT^L_D8L7{z{1@T{HpvgPZh{!=h$y%J5njABB%lwyKW zC?=#6_&4ppc^@iE{TSYA?PNXq`Y&A+5RusFm=D2ma2BJn`@(;-aVO zpnIAp9%!039)hT6Iv!3K;~^f}75=Aflkuq@N_pMHS(rcE_y#`Y3m({x5AA`ayYL9g zr)^+EE%K_kw@jfH?dk=4K~p2@*?5aG?)-PyP;SV9t;D-!_5pmT|2X~xCP*>)Y=K4n z^s5(5p`7~OPBz=3cIWyzC!@L|37+V}Ax|1j@moJOCSyTPkPWMX;h1NVLaUa8{<1Zg|J>R?07 zVZ&L4H@wsYEftH2F4 zB=&r^&cYYIx4|^ZqBQ0`3=ZquOCuPI^@Y(pp-J7O=g@bSvEiIH=!1}K?xD-~LAvzg z5|!o5e|>Ruvia+Jmgm&SxtJmk`Zhx4WBOgd zuHe-EPqWb+|I`+?gHyjwXJt+~kjI8wn4}}GU@F7i71C~AR>&Sl>DbR|oV2E$UBjtU z)X~Q|N6xdRXih%*WJtJq;dC%*t43@>mFGtfXT#0X=8%1y{ECWYZg9SYJg2SwhP7JM zu@|;uFZ=_0LPE{91rb)xYH7#rQ0Dignd4m0?B9%Yj)s(a(xuuKe6sIO)tP!mqlOE!q=)s?JJ(D7c$2{6HAemcv@j zax>950At7G*RxsPtbKv5m7XD#c7U`9ptb^-hXGnCK)!}(ms8gnLiHevEifyeQ$K8A zI1`74^*K0?Ld8j1IS@+;;m!O!T1qVq(j{M}TQnaF&{*n5V%kU|4S+~yS0=m2%ygiy zzD`r_p(z#AoeEH{!sJ@~D?&%2YY6jOBIgq84;idv*7jgjqk&gx;7x#fn7XtiZ5?&> zy)UCy6TnL5;jf~W9Ux&j=HuyXt63g1Fcj+t*GJfrmF}1< z&|P@LX_48ONt9e${(-b1YfJ4&NXlD9R50$Xu~Bxadm9N8}`B`v9xwJ+SQ*D}{sbmWN@ zlwxlj%px;ye=(%od}kS3z)9adjMOhpecXz)r}kf-MnWzwV#_)H#H6)*ggcD(pnPdoUKS+&Bd3 zR=R2%yOdK`a1qIz^88+`-5UQg=2(~i$f_3W@n7i`70I5>d`sJFY+^q#TCT znX8|oYGKwgpwfu6dn5kTd70XvsA=66F2|3Csvy)LAwWYOF;3jp;=_ud( zDS=M+^U9E9^IJQyA9>vomXEfMj!oA6EB>9#9z#W5<(fK04;3=DFI0l4XEaopus-Ps zZ2J0wNOAW z>CMAWM8`4VeH+~6rPZ#mYw-FeqEGn1hDKL?qr=r$I<2C*G-7D_k|86;kXFy|fYO?h zZU_5=*V0g1Q8lf!x-O&CRl|f4>0CdlqNbv{sXCbIY7J0BeRK0%^%brfcPaJ*5rcf> z%%#s-gs!9HR_Yh5m;);9*xr#6-()O*%pp{3QkYRXD;#2mY`t}%j1rz>t44GgI( zt#7DkXe@=0ZiWY7q^>)9O;c4>H}w8xuI{D}ao5&(wao5@%hFI+F|#}M=Qg{lDoX6G z?o?S^RdwaAZlnz;E^Q2^(^(AAZII}ysLILkSj$7nb~QBkC?WcsOwrdLY%q_J(?~b+ zpKF*OkkX&5Xk^z3Mm~;a7#PUBofwGFiGc_Kh7j1@IG_5)>HW|T%pRcI+c@7tF>-|H za{@er4M%9?G}4Xy=Nje*qzv*Ms8NKUXymu}W(Ju1c~0DWczyL_Mu&o=-;9bHcuAtC zH_+Rg>aK7$w=lyxOv6VEKM^G`Bf`wX z!H>A0*YjGS4+6`m$CxmTTGkB{R12x;@&g0V8L?{+$+9>1QI7U9*A!trp@ z)8(fN-se{R=QR4b{%eSn=n2keMpYk<6_!VMR0Vx6?0ZC}-zw;;^zV>+t;&-m{i+m; zF!v9_rWlW^ps(D#s)DE|QPm~$`gI+)2uG5H-IKf)?EFQA&w`z)J?QG=J*l8{?$LCY zY1sWH;p0@{xzrJ!ZyLJ&zTuaBpleO=Z{wZ*!e>*318JUZ1ifl=U3)0 zG=61!Ey5N_*qrB47WC52t1O7xgEEWHyN%V$qo-(?B&>4_OWY&q;|0l6>y65G)e865{_iO3YQE*Ogpcd|%8Z8kADJ)sif7!{ zfqG$GyAg`*9pSxZ;59c&UdH~F=kvf2DMT;opGw+JVi{W?zFUV&?~ zQOmJbVQcGY*3#8p?QO!MHoulrx0mmNZ`{ex*9p6>^XoWedj*~vj7nainV*th63+Rm@bxMh#7U5ib^{fh&-PT3tg zJ#!m9V)+t0o!k=8bjlv#dz3dSIdH2@yrVa8M#9UEKZo%4YDb#mRKT}MAyh5x=ETU~VsPBl! zHpJPq?9?0w>8E}=^|NUyjucu-6>TGlwgu7X#}jLM4n*1FNtBcNVhj2XC=$gwf@BPk z1^nn37ZpV|NU+mvT^c&n^!!ER!&dFek1H=uOiap+&BQC(qH#3l%&v@z!n()^gjv}$ zJpG&$QbLBnpWf74WFs-DsIIs=u^~6NP%H`Jclr4RaPheD=uk>|b|MgZ&&N zHMyv~IH_u8PHbX>vpTU3XOao3D>7T(zgNYO->RRnn0~pJAJ)jO@%op?KKh{=>_agtCTF&|@*AtB&Gh()m=)m9I2k`3 zwU<9PDY4BR1KZjzkMr0wMJ)Uk{Qk2^2=LDg;_p5_O3K|{A1iJ4?zhYy9|>0{LwWrh ziKo+fP|gyIViQ~Z>qdW;pWo5YTYmq#!5{1A7u}J~zo=h#)#94G24j5Ko9jL2Rc6aL z2|wjSv!7;E(S$e?q`_Tz-Cg!%6t{^}EM8t9-Vn^;G-$9VgqrzJ8tG zu>RS8ela7mbAAdwzW~2s{gDCw_#pn^{E|)UbtmP`_QzvvURF*X{MUZ@2Cb*t{1CLBq{93vbTIetw%HzEi&I!ru_!&-3|~Bht^G@GJOp z{l3){<;%Cpe*Q!~sdcZvqNu=Go|~KK%!x^DDG_bCQ7~mqcANJ+a`mLxCEsJ^Vp3v* zJE`2jOGJAD@wc6fU(aWOdJ)iXk0$=8lkt-(Uq1A&orsQ^#Gjsx(xv;8XZ>CLiM%u^ zv$;6nPjP`YUi3Tx>{kmRwwl5Mn-l)*$gQ!#fouujjH(RM-=!YV+LGd8 zij!IcyvYu5Mvjm2=2M$q$><{2DW9fO-xJS?sP~yu-MBZ^(0U`MbzZMtbPWD#qf@lC z6cwcU>uXV7K!%t}`r`}9el*dmKRAB$`>MM-v8gCFFR*r66yg>43f8qb-{ zsHM3@`7tfI%cK0@ zt|8a8P4Mo=E~d93w2b#jJyiwjJ#N*Q-&P!3)sUBz*NA)@Q=Dguc4Hs7$YS4Ru%73B zPHV&^8h?l}uf4qWFL-@={211kUs#Y@hn!Jaq>nge``71~M)rqHZ@z(2>gcG0em^p- zFQ&XSsj9IgKUz$sm?3+Y*=o@;*5mH;all@gxIfnw7vwdHiKvOX{uGZt8P2cU_)iX; z-?aR^@?u?+J0wU6u@?k6OS``r$sg(*yZGUI>HQ6i>zfiyazd!lpH*?Rq z=bU@bJ@=e@C;7}0a}rE?lVUXO3rjcc)0vn)3?p|L#wc-9GsP|ple)Xgz_N$(X8`j~ z)dN4A(*!K-JsWuHxw*g@$@c-zy}JN7ck*Ijmw!9}d|<(Yz`p$-0Y0&L1#pnK3b_8r z)4+J=v%pusUkej?IV#zWLE`VA{+?;QQ<-VE(Exz~gbZ1Iu;`z%TNxz}i!(!1Hq^ z0;S%Wz^-d@fXkBefu@5+z*Uof0UZ1fJ8;8-GGIb~Cvf}bX}~+g>A<~5YJroS4ZtJc z&jS8@`5fTi2Hgv+d}TiH%=m@C#^XzXKh!J-{!0Ha(757J;KQ*if&G8`B=DJ>XMjUb zJO_MX);i#*@J+y-@(V!g$QOa{zViw&=jX2jKRCS;XrK2c@H5jM1DjmEeqR;^)Y;n( z($mCHv>nn7qem}O7gLw$NY=;1n>YRS@sX}$UJjCH@Bx@YQZcW+(cvTgjJdi$#CcfL4(k0RHk z4eP&r-*dJ5U1OgBA}KjhxPo={`t+OELrUfUHN*cB{x z1@?w_PcX9&zklhy;bmfPc;ipq8*QjR_1;*@YU)NjtV_)?@dBRqk}y^;%>W2DhOw-& z^F{-wm)}EMSxTH=g|kc^UgX=uS^fUOhIqa51)9wJpqEjHsnqHZQSR&@aGBqtEzBa~ zeZ;n`2eK};O4_vM()k(KjpxbPJqO0BxWW+%7>Wz+4Y8@dif93 z`AK3|5<3kb$CB1B2a?gf)ZPk^XgcmYX*l0q2SM%>5osjMLjW#|*kb{5SM-s-i#8K|?etJZl0&SqGZ%JaMZ4ax!tg=?jj0j5yl>b}5XFciZ}^OD^@rGjwM6 z#s#7mZ`4hV-ZoTlf4a;r8%^^Ny>EL?JuO`*dX=wk^i1U}CaZ+Ug!N)}hxO9C_2ht+ z0O@W3X9L&%1qH2a1Y1}^uofJ5Z|W08>;;HgRylKn0o0}7OS$lF9N5C1Fzkfmcw?Re zD74C44nXb_#u}9)T)bf)vxC@^0UkHop;11iM(od^%f6lrP6FFb;i23N;ApS7j}4?4 z*@w2f4j@sG$m5AU5Nu}2br`ezMU12eXv}rQ#oSo;{%8t?8tO><#65u6h)z~{dH}_O zs12>3zslYY= z#y&E-XPPzfncQLh(rqt(RDav`S!p8&J(4~ocf!jhFW(hcm_F*+_oluwVEFTY{(JMT z>*I$;_gS)caN_#A5}!CUWaQ0H=4?C~J8I+c+Bg0Zm+U=~Y&DuLbWJzy^#tyUF43?q zbmij(9jpH#2Jt4pjTx%Bm+47<{ho5K^f5W$Et>lQfU*!A;a`Jkar47_F~~4_Pazcf0#HFaPCxqjC{*- zf9ArH8B!}R2S~_DdI15(Dj!iqORr*uw}8f|g@Fs0wq_jE5Lp| z>HLARu)P^==GR1|O2ZX_WB-yuS^kQM6~x96^okYW4kL{p4+x8APNFG21+YgE7rI&L zaDT`t7tm%_!TcWLk{e{|V#nTO-J2+;xjo<(fF|*v$wwl?IEG?YA>Yc=0RpAxYDBr3 zdYPw*xf~=N{`DkNhbZx?Wk;foZ!ZIp`OJh>Qa(l7y#XEX#S1qMFGI)*@@~AIdksM= zH+X_pkuH;M)o@UQR_*Q$+92wsg0_R_=#gXBsy3=Q@*Lk(M5_sw_4oHPn`!&m4c zS>cmRgTyNRw#2ls+(UHtmU2IiImx=;?TJ-zZ@3VHKse}J54HA3vQk#C?h7Z8#CAM3 zp4FeO2$NZ+&r`agth^}7kQLAMXrL7bC z3d)*{>84KVw;8<=cWAmjN0|C+@6LK6N2-VcYg!<8AL_j->-OpBMvHULODG?$&OP?% ztheq!U!f1O4#ObhaOW}&k$Q!t68{O6_|L#mYl!_HI9p5MIKv#n@kj_C%Gi7NV!ndc zx#Q>~|6OHEdgc(sI7*844*RS_4WvW5=G3PfhoWcEfd4^W{2rB)n$~z@w`-kOSIN%fHTzH8{k1y#KM(N z;u9`)p!uU~^j#URetfp6eFqwRe>|uC{z$cf`|hvSJCGBGZ#E-2xj+8)Havugjbrgp zq(%!e!2{88#B$#^f>`zi33xCg9={zAY3<5H&g|hR4Z^?vG?5bMCWY$6uCqhYK{+2ul}XxLX+ZY)?rB3I%XQ|#mp*adf}|;j zb0fibCzfL!r>;g};1X7kLNb&O9S|`9&ar(o_;Smq$d=N@;|4ox&tj18xMORO&;(`& z3WE~A4P1BfTyT}71<4B?ei~e$0)! zU|+pr2oj`yc`|A0|8GN@(XA^?1c_@nAJ)VQ15+_6#av)OC0sJ>4m9kG&l`{tjTJFS zojv|$Xky)e=!u-oxLp{TGm!ozt+w$gMLCftd7EE=q#(*!p-h+)Fm1 z7c=n0O=t*Hb{bZ&OiBSw^5`N=L*DU9*g|%|g+wG>ru^G5r~cmUR0uzM9+s6cX?V9E zUxtdnefJtF0w-+06{*|3`c+f}seE7>TyO8UG?rYO85VaVQGHgMS)9sZY-O%h*fq843~ap214^_Dm49U3=zgeV1fRozk(x>4QDh zgI=b-ntCt_nVwtlHmI$Dx9p{tAl70uv- zKnN2>^!+C2Y~eLgX|NA2zs{)N2u<7N~c$Qt;`A`Wvr{IoEFM_ z&jyF9vdrQLWygs%yFttyH=xg`Z zxJN6grEBGTYWfGXbR#cn#Z`h#AVNSO41U-4r?$3xSF{7YyQ}`z z_V-Y<5@BkefJ|@A5^5#2bgg_(P5*$F9=-+Ds_+xF@Hn^oiKeGh(}u|0axQ zf6QM;MGeD8UfA_~7U+bHrM06?7+NbEB6!6}X4?EoiF%)GLuEpn>E@kItJLGSbFmx0 z_6Gi3B-v^6%YWa8Ncsa351;-};0ktv^O?ug;+_%TbmLbHG;z&ey=Us^J|5Wzg)O{479BY{?&P|8~=JH|4FJh5Zd)cx}C=S zg~so*lSY2^UHrlv@A|asQ(kCeMqhMmtCz=4afF>;Hkm(jcQk!`yS#Xa8W&JWk zmo8$jj(1Ptw@&edIr3{e?vbJ6w6z&e5!9Ml>*W7g;WyKc4}+IC6FB2&jeJ?j@2K<} z>F`{%2P99e*BaY4jh{E|zhkVe`_8%eeJ;PT4!bY-h^L*`u4;Z+wck{Sov1lCYOTz# z;g3we%2wLC?Z6CvX(er^3#w$j#Ls~Y$( zXZo#l++V%}zIG;mYvi{z`fYUBUx8bL*2ub9{JEy9Yox8WPB-(rn*ByP>@DB@Mmvv} zX7lrB`%QG%S=vjJ*35-D{DC=F+e}-BDRcRjxqdSpc39wgr=81!dv*L#N#{4yVW(+t zhuShg$G6w#>G)OibnU3WS}j`7kd~GA>G+H7(h__l^-P>}T!4-b?`zLU{8v@&WITs~ zFX!v{9rLfE@BFs3Q})=}wm`?vTM)3-;c#em+tx%aBnTu@hg|Yh~`5jZi$-KC9WzyrLDhQ zcvMq_9(_Q^Z-3xY5gM^!oem!`cs8PCnXWIxL`6p$#hjeXg0Y^Dn~|Fa#L-W4#Y9mQ zv+<99@ZKnzD@2a8Da|b6=6(bF-^ltZEG^7AVLzOw1ROL`xY((1%L!wwikOl^Mz>n4uadG5>6btoMwV^?;o}YL;%vFwp zBxi}umYSECfseDrBZ!xiH8m~{<02;zW@b(Cw6n*P5po26)K~S5d?cn8*OXM*>hki6 z#IhiHhhKhDXnC@dI>A+zYRkz*kGux2y$Va-7=!}2X0b^%1;_hlskUj2#G-~uf4n%+=Z|d9{M8`U_|HmO(~6xXsjg|+iMG0&Dq9WKBm-1OY?eBI zi)d>e>0QrD%!k(6Jhdb-so7DO5no)8G_^d>GpF1J?|N2Pvc39z{=~*;<yMK^R2KX$0F3pQQr7Q;%T)Wl(WR*L|dbO+-T46%UkQ(EAJmS zT2h@~r`;cxhFZ^hRhTDTL7sBBsLI>S&e9PX>e^=pBq`I*`Ih+oU-(Z2XC&B7v5jC=Eo8Z?^ylcMe)v#Yc^qlV^EQkCJ- z##3>FSH8kBJ!m{3^Fz>hQi}>)$orH#E#+Q$iBeDQVVUrvbZqEnU`nF z$xfKiSSFhD;^4~atY+_efCV4MC0bEB)$_+C{5Ks+ z%+*DO<{ZScHLu!?05YdQGHz;+{WkT0#+I6tP?FjdkWI5fGInH~H=ml-N=65{R{1oY z+MfMvL%q+qvlQp1Y8r2BZtL-?MMo2_W?DsaV{zdGe|;^^59kmx$bNDW`Hwn!?Fa84 z^}JeIWt&-?m>(EB4GQ}e=L$8{a>d`B=pCW^66(c~VVPfqIWq zHTpM~B)aPIQ}gSQZxc%L&GDs}2M)5BcLluXIiJ%Qafrqb_L$dS-uf3jK6U?S##c~O zIH3kPV`{O=vCi_3&zejAhfZ(4fl+FxsDgSv(u^;`S)S^uFDr-_C(xcDe^;2N<4s$S zL+9gwzcO%st|=+ZuNQ5oiK_kao_Nx%U&#HR7FfUBf_!I*YRQu6wQupp6HQ^_W%3Vl zc}_}EVU8;wb1BZYQ2L|sTPT=tA%W>4^1B*$(4%?{e6z&~6zO6_!Qua*Gbim2XKA%o zFCF=@eih!Mzq+KV%meQJ#nG{@v%PezsLf<-QEB1PF~YAKs{wWyXVkje>2~m z?>@eJ?{~jDQ;vmFajHRUaO(}bb-9M!8Ur(gVO$Z(j5}p+ffT_oSvwn8j(f9o0kGsy zEAWGBI)IMBR|5|{bRBR(#tp#l_uK@$?#gArh-0?_Z@Z}rICS`Lf%iVP1~^h$3*7jp z2Y^QB?|@JL^e}MhicP?mM{Wk@ZhZo{f7(;P%7fd0A2sa&I<;=#*LUp%wk5p+{O5D8 z0hbiN2^{#@+rU+e-T@AaJ^*yNJ^)@k&I8>1$|u0|S)T)U96kgzFFp)>&2SW0wDB9@ zyOV!lx#TPN{;aI~GY3x%5olZd0`POg%Pg1CxN&zOCDb>Q zU#F{qp=^6olT4QwLxdqBK92K0#GNnyVwuD+REA-yk2uc4Ww%BXIYO3gVq)B&rAw+_ zaAuspa%r_`WBHghx6ax5Wye(4N~h(LyYn~PIrrtc&s@>Hrr!F+w=F%=RMsWddLsEH?Sv^967Tq5ZyDoq9410 z!>+*I@a+la>b-BBcyIU~VsH5JPuUy2sK4so@C-EE8kuX@;XOBwu<_6?m#j338jg{5 zoYv+7Ft-5Y27voU8eTznj{#h(0piU%j&ThZncH+RT4qx;$1Eo5767vX;5i16p8>e; z0l1&Uj7NyRAAC%72jh;;W;}ysNO(b)h>=n2`Wr;qw$h{{0Qp0(Y{!l3S%7#DqkW_^ z+Q`_VG4X9(oR)tHkKAtn7>{DlUgkXr;dg=}f2ZR(k?Trm(lQ@|=#C)q-xBBmVjSjp zB5C4B(LBR|(*nAXNWTD(0YbqB>w(UzyPrG~gk!&lh1O`+8CF^dyyew<+zx znaStoTA%!=b@By;+2bay%z5~nOV(FDQMvitWZ~Yo=WZP_=J)Uaqw~27jEV6>mhZao zXvg$R?t1;p-YMW!@WMSf|Z3%vG9}4QClabzO+$!&M%D zwYlIKj<>F4;F+RJ)XG1R*8juMg(TK8F9K{A;Q?ca z=916%#qm{m0dd@{G5jIqgk>1d6n`Ay3!~_ZV+a*4EIU%_J7FV6h`ionk7rlum#yOl z?jQH&S(#mTX5`$NJE$bHt7_ugH`ctjYs&0TJKk-dxc}Tyw#3^n`t0)ci`H$-o^)vV zEswspwXu1};V+*{j$ghdV?QKQ=pqeA5xPz#bYCc;lQ(!nhoyVHqxdz>>pJ(VAbReC z@5m?0(iMLzzGKq6SW(NLg(vQKVjd5GqY}05pK-?=pkW#Irj{Q5h}8F3$-NYR*HPO2 zSCUip7{Zkhat=Vwr{M@_jK&S3AbKO|8bezr0rX7JdCCpBk)~2%YR8SM1HddG`X&Hh z1iBM99%Kb4-qMv2r?u75gL?sPCH=m^olVBP(YTS90YnP84JA(NL568v*8}7Su~qqJ zeIb4Z*As^L8-Ix9E;!X6;-1v7AuvX7AhSp4wcvsC|3DLpUfhO@t9%sopjCg~!XgH5u`zn_>mMK0 z#OQ5Ly|W4JJfD9|7sI{WbOT798qbqpv5gw_0tx7v-Gz($_fyt^HK~G?Ies0-ZqSRD z@3~JC$MHGmvfX+)`}xhpdcWj?G{&=mUCGJc&0%-zT^)ON(X`6 ze2$Eh(}!*+>;DmzVC48KKG%J(t^F(8#PL~F`=>5QaOwFUK7L7K)Vsd;%QKiM=S^T; zoaeQLtc`Ph@c>Nn+;Fq5oJ*atPm{>mx}IUXIbQc~Nb^h`JF1*J|D9DV&)syK+Ix1- zdPor0-JZ~`_gu61De_+Cr;mW^rkBs%xtfj9UYgBjbDr~-C1CFUcVI?}hTYAH z+I?hV(OTBY@dbO>LcM#}kFRS)PW?OGw5>-1C-*Z)#Ig!f$6b^;p&$qEs^6Cgu$v2$O997m0Po0L)G?3lwnOo zE*%H-dcTdOlTu90fAKP0Q3_FurUKk4s6eA|W1|*?&qd=PM<;4s<53*lg?MHL*+q?t zoJF%F8cqYaCll>b%=e(5@+3#cFm#|Y6*OTC-Aw@S6BU7Lw62oqm}nJ%0gBLrG0R~IwNoI;j5v7r;BCxw9ZyXWj2E%sCNzd|kl@0M z3`T?iceJP#uhC^|-Dt8fYa1hzNNOF^sI8-eLc|dO#tx8O3^N)&nF*=x(Mw^hj9()o z=B?B+CS9UYEcH6xJqydPfL8%rNu{S~|X)j5B#ywSGvZ>?Rg)Y98>*ITLQih>&NYw&UPCn756a^l%zDTmzu{Q5GKDgO5NC*k7xOKK`E zuJ>15jElH7Zyb#-Ty4UIzx3iXT;#XbUrNLo-;T${_M111xOmogNw~PXa;MNUi-@-f zz30d0r{W_1u3|EFyK86~+o87=9n3&gawq?N0%*2@@1f3n>Stesn&H0S0ybMOP8eyX zGFz09gkrAQj*eJ<4HeyWm%wh|nCJI{F!W~Bb5UM9QDfv>jtAIn9P{M_lyh6m0Elzv zMO#si#HaLZoL>G?ycDuM-G8B?+HGd{a`ME7SR-dUr-1!J&;KBsMm zAWK&cM>%w@eG+xm6<^GX9J9I=YPUUx$|%0DiHc{`H;^GtIfSAm-bWwUl%D>Lb+ai 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json new file mode 100644 index 000000000..ffef1dd05 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.265", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105369", + "VER":"504", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-17T00:00:00Z", + "end_date":"2025-10-18T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z4 Depth", + "domain":"D07", + "name":"CFGLOC105369", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth4", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json new file mode 100644 index 000000000..3cf237c1b --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.265", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-17T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.265 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105369", + "remove_date":"2025-10-18T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105369", + "subLocation":"depth4", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"504" + } + ], + "source_id": 20037, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..d8b9bdba4ed98ce5a3e015a5e2a6aaffd70148ae GIT binary patch literal 17309 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-18_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-18_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..9aa63717f96b1de3ca3c3380c64aae7d28ce9d79 GIT binary patch literal 18425 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 S{`0WE=0DUI&}PH`WAuL-pf$h% literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json new file mode 100644 index 000000000..b05200eb6 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.025", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105363", + "VER":"501", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-18T00:00:00Z", + "end_date":"2025-10-19T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z1 Depth", + "domain":"D07", + "name":"CFGLOC105363", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth1", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json new file mode 100644 index 000000000..5057e87e3 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.025", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-18T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.005 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105363", + "remove_date":"2025-10-19T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105363", + "subLocation":"depth1", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"501" + } + ], + "source_id": 24772, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24773_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24773_locations.json new file mode 100644 index 000000000..08b566fa0 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24773_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.025", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-18T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.009 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105363", + "remove_date":"2025-10-19T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105363", + "subLocation":"depth1", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"501" + } + ], + "source_id": 24772, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-18_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-18_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..fa4c3d7b19a64addb058ea485e1d4c123b7ca93b GIT binary patch literal 18387 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-18_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-18_ST_30_minute_030.parquet new file mode 100644 index 0000000000000000000000000000000000000000..9b544ae69a1509ff83aeb0b7a536ed7d7bfc778d GIT binary patch literal 13413 zcmeHOdvqJsnIEN+Jx-M1q>jLoZykQN?KHc)4K<#+AA)qQ`oB#N{ zhF!`nySkg?e16kj5FB~wOAzeK@9F_T*!3F#rbpLqg@8p9j{|sr<+pnQJbUZSAl~)s zKLX{_wFE(2yd?o(^5AtLtiN&UCjcHwEs^@3HRYKLyHo=ca8~S08&Bz?)Yr>&N=R?)?Bp59JL2s6P`0@fEftP$&1UkAnE{ z@n-?t``1?uV!bPL7uGAD`y8mbs?H%*Va4z2ULPV0$L?J{jHiy#wd*z&!RP zA)8vkO!8_0vos5qVVL44=U{ZJa^{=#TC1n1=8@w16Kfq;H#GYBH~4m+tB8 z+28%dSn%WV>2Whqyo9(35o0e!EK9^#L`=h0NbzD~?>=^xxJfIBo8(m?Zqu+A7`G#e zdRq<|@zKo&6P|k84M?%rnky-5|L`p&T5`ehQk9-%zCu=GVGqW{fqsn1jcs|Xx?}~W zru*`Vvi_qIRRtUW9`c(_-1#I%X6cV#Bf?q}#@JOGG^{#4 zGCyCfDay+)t~yyp9%1IbT4u^pSLjq%Z7`Y;A9(HgkrI;-pJ`aEUPxWNnGqjmRf7

{qxdWsA$sd5cfr4b>?!cJ5joMJ+*xeXopHh&L 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/CFGLOC105365.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/CFGLOC105365.json new file mode 100644 index 000000000..37cf4e86d --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/CFGLOC105365.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.065", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105365", + "VER":"502", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-18T00:00:00Z", + "end_date":"2025-10-19T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z2 Depth", + "domain":"D07", + "name":"CFGLOC105365", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth2", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/prt_18574_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/prt_18574_locations.json new file mode 100644 index 000000000..9842f91dc --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/prt_18574_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.065", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-18T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.065 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105365", + "remove_date":"2025-10-19T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105365", + "subLocation":"depth2", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"502" + } + ], + "source_id": 18574, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-18_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-18_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..8c7b323981d628e012f2c41d7ca722361937c50a GIT binary patch literal 17968 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-18_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-18_ST_30_minute_030.parquet new file mode 100644 index 0000000000000000000000000000000000000000..934804e95272520e9fc46e2f6c2d95e7caa9f3bc GIT binary patch literal 13375 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 BMrr^6 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json new file mode 100644 index 000000000..749748023 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/CFGLOC105367.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.165", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105367", + "VER":"503", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-18T00:00:00Z", + "end_date":"2025-10-19T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z3 Depth", + "domain":"D07", + "name":"CFGLOC105367", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth3", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json new file mode 100644 index 000000000..bdad30f8c --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/location/prt_20057_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.165", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-18T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.165 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105367", + "remove_date":"2025-10-19T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105367", + "subLocation":"depth3", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"503" + } + ], + "source_id": 20057, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..03224a3aecb02b858e82010f37cc7812fecea005 GIT binary patch literal 17987 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_30_minute_030.parquet new file mode 100644 index 0000000000000000000000000000000000000000..85fa664075e31384db5f455d8c69839a379eed07 GIT binary patch literal 13291 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 N%*T+{ivN@6e*tv1^@sof literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json new file mode 100644 index 000000000..cd4a19f59 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.365", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105371", + "VER":"505", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-18T00:00:00Z", + "end_date":"2025-10-19T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z5 Depth", + "domain":"D07", + "name":"CFGLOC105371", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth5", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json new file mode 100644 index 000000000..e92450df0 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.365", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-18T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975, + "z_offset": -0.365 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105371", + "remove_date":"2025-10-19T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-19T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-18T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105371", + "subLocation":"depth5", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"505" + } + ], + "source_id": 23800, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..6ea13df5fe7054c0960361145f8cff5694100ae3 GIT binary patch literal 18427 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 SjN|w7j~UsWVbbv5{`?;Wwz_Tr literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_30_minute_030.parquet new file mode 100644 index 0000000000000000000000000000000000000000..0c49543b9df78868d0068bfde4d052fce8322643 GIT binary patch literal 11808 zcmeHNdu&_P8NW`G)NRr>Exn%8Wo>x$5=h!OPLn2CS$(cwc{r})CVq*H*p6enu^l&Y z;x=MahS-J}6oe3e0Ao?bN>wq4rm4#xtcyxTHIO!#v}t3giq?ujkx*cWMW_q=&bhDS zYv(~zsF1j-uYJDrec$hUo$s7`Qx|eKu*K|TDf?NmgY9RTZ45KHu8yf*|4FT0}}y|(xL znG$Uc*LX(^>sh$}F<^N8waubZrvm-~9L~7@Ry%l!8I#r%7P8z4><_>33L$f%$6qWf;8j z>m6yBKk?t6zA&T;vG>yGZ;$oGAxlK1}m zhy67t2I9}f&b*ZP;9P#*3^-pTZzN;dZ!%UOV_L{qiMB<|7c+V3S4-qgoPoTFQ6+iH zqh3$m`dOcYEhnqZPz4IAO~b7wjkfrW51_D2$De6~T4ScKmS_!=&s=RY0S88u}%lOkyugF0v!EUUu!YJ_vFZd zR*hCW<^Kc>=>N-kV!-Ulm)lGQ4ImuE0HQ$Wf$9cjSWck=Ws{*8?(25B7%;)cs0_ei znEU4<4ukgXYi$~B`Rp_QgD*q%eTzZ_Ui^Fs0~kC*pojv|02Or!16;Wgcs=mKLEtpK z^@sCd0aMz13M`me==g&5~}?pfEX*q7Oft_u)p)7^t56$a)}o)G)@ z#9l4zAZ8aFB)p=7Dk?Exxg28yOk2Aj#|BL4n|}cY%pQLS45)6p^AQr^p3fgIYyf5# z3?RIs0V?Ve23VvB*Fab`b(5D{I1{$yM7R&KJ6ZL!K0JiWU%0@@49PBLnv_-;=2xgN zQz-fl3cEy8?Kfc!ms>ngU8WP4>j?hET1+KZUbgX4f{RA_0{9i|b_ZKTR=5@~FmeIL zHdd$ZB6v#iM!}$|z_OuQ=pveaynt_lev+et_A4>+n-u$L>Q`>RiEVbUS`zRSvodKS_fD-C*_&1?8NVhCl8*~*buFzC)WyRY5TGif7 zi2pb7fhy|PEZG~sR`FrjA_sIP{6q+Mb~C$8=}^u6KwPgUod;%%VgAcv{-%r})XF{p zgWNXdVsGB@<}1k!6c?1BaD35#`%r5-c85}n`eIB~4#dg`$YnLc?x*H;rb3BNP@l-rHmQ+)N#}sOX#On!;#Dl4D1Qs2r*esEqV@X$LRM4`m@34xZ=Vh&0mHI_)^2w*~v0468`8=}D+06eDI*TY!hE*nk9 zhjRQ8D73@|!lTG+4G*WpNF1jvqv^E#7K&fdvUxn1h!2>9InK#MTE1N3RD{ER?UT9U;2`dgS*$NgfL>h)(Dip{Q<=g^!ZI<_J?$=~mIFRlO2f-lx2NBSAe_#9i6WE!-kNsorud@$REARFRk^RSzRTGaB8j zhP$BSuCY~m>NN+H0X147TKRoYaso1QO3%IWEx`%Zn~WviZxnVyDm58P?yVHo=^fm; zN&!pt(qf5M8OvkQ{aQj<&~Q^#-0Zi-3Xq@QaEUMV(qc6zLg$fCCX2YKo!rcKZXAlT zuMi9&@9*v6W_HOK^a*H*$G7D@q4y-ALsM9%Z{-%MzZ@(ocA{UmaWl8cSoF&Y#V?w= zLVc_ERNAXMxP{wgq%}v7+NTM~qDBV=_b|uJ>=r;&uO>uyRMZvRq4s_W1nTYK8gBYd z*^^bgl~}$OAPfzwFgT%qfj7IwcPyOHy9+Zz~<_>={gyNE~V=#Fs*c! zqW##*&F_&R)*LM5d`4l+)pOJJHx?r=R8tMyyg|lTbEu?qB<+Qfn>NY_YmO9sHlwz+UE>mIb~ z`Z=xcMay>h3r3Z`9J$?xylui;5&f*L9$&_22STU^KKSZFD2wp?H->A*~alUPu^EcmBAkPannqkVmW zUcWJMcpwEnL$GSc>+1>IVSKmjt96kvk79%hj+3~N0o-tmP#~EY^LvlE-BG?LR~`-5 z4vO-}h=^JqVha8yJqEwW<^+G@BEK2gdYkI&TVU2|A+F)TOJ3q%I7f(&h1&UHBHO)R zpIgvTt}Z$=IkK4b$fA0D@(}d8EuhQp_a-1G2FTAyv*3RRYIlkH zdY149=i2TZ52c*xaHBgBN;;bapUe^2ADczgC&trE99H~+(gJ^ugp5rSL7&qQ@HQQZ zcod(IGbGy+SJ~5vouKN=O6n`u9(U4~4%u*yp{mBA$gs^(3li<)kx(C`2&woG)!{}y z9W;6l^^h8YrKHGbSCDsLJW;;K?{iMz*oI_zsCQ@`z_JuREidOLCCfv-=gO<=TUZ}P zTUN-2rAy>DqxOmYx%G_$Lf02w+I-6I<7K-ca?P@Q*Q(`ZyFua6dSN}Ak#i+^T&n!o zfcF@+KKP(4k0x4{w|`ic@3Y$`!VP47cggaW)ypfjV{Vq^?JLO>`zHqM9x?V{4zeiz zcdjH)>?iVy{U}&3^fNQ+UcJ0xKi?(GyHxUd{RQ_vJ{$0L%Ih)E*Tm!c9UN~I*AL2y z`lR|L`vJ9n+}P`|-E%x_OkiKe{6-H|PiCWRkNrq%PCg6w2bDd6SP=R?>79r6i}D?4 z*d^L0_A9aosQy4yw|B-u$$^C2d-?rP+Sum~WLQZaFBMT<&W%-(M=n@em}e<`N?y)Q zyCUDMk}ufb+MVOzdtboYknL-954nAv&Vbi82B;`AF5*Y|;d*FwKw3UO##ZQ!(6@cO zEz-_=pnoUADcnRYW1_qnby>vtSk0tq3x^t|eQLLS}_24A7H z;J^`uY7&nE*hpc2kFH=J*(Ab>eO|w>Gwp}HAwXvMBtCk9HZyD;@Qd273)eGcUqdWn zPhn935j?TY2fB4{qsQmSM zJ+TlV!4U%vUVi*7Tv78r$sV(C{!#BwWPgCYa>#AB83G=6I^^v`X$e0n^m7Z=XSHQu zEvNlP>$n@%;Xu-J9H>1A4vY{gJ3h}4yI}FkN$nXXQHH}TVI^O1Bj>kvrvY+dpmAXe z>?h}5_?DdLz~@}JNPX}jujhEs>x?-JP;aR~A`EPdzB(NoIXV`}>0wU6u@?k6OS``r$sg(*yZGUI>HQ6i>zfiyazd!lpH*?Rq z=bU@bJ@=e@C;7}0a}rE?lVUXO3rjcc)0vn)3?p|L#wc-9GsP|ple)Xgz_N$(X8`j~ z)dN4A(*!K-JsWuHxw*g@$@c-zy}JN7ck*Ijmw!9}d|<(Yz`p$-0Y0&L1#pnK3b_8r z)4+J=v%pusUkej?IV#zWLE`VA{+?;QQ<-VE(Exz~gbZ1Iu;`z%TNxz}i!(!1Hq^ z0;S%Wz^-d@fXkBefu@5+z*Uof0UZ1fJ8;8-GGIb~Cvf}bX}~+g>A<~5YJroS4ZtJc z&jS8@`5fTi2Hgv+d}TiH%=m@C#^XzXKh!J-{!0Ha(757J;KQ*if&G8`B=DJ>XMjUb zJO_MX);i#*@J+y-@(V!g$QOa{zViw&=jX2jKRCS;XrK2c@H5jM1DjmEeqR;^)Y;n( z($mCHv>nn7qem}O7gLw$NY=;1n>YRS@sX}$UJjCH@Bx@YQZcW+(cvTgjJdi$#CcfL4(k0RHk z4eP&r-*dJ5U1OgBA}KjhxPo={`t+OELrUfUHN*cB{x z1@?w_PcX9&zklhy;bmfPc;ipq8*QjR_1;*@YU)NjtV_)?@dBRqk}y^;%>W2DhOw-& z^F{-wm)}EMSxTH=g|kc^UgX=uS^fUOhIqa51)9wJpqEjHsnqHZQSR&@aGBqtEzBa~ zeZ;n`2eK};O4_vM()k(KjpxbPJqO0BxWW+%7>Wz+4Y8@dif93 z`AK3|5<3kb$CB1B2a?gf)ZPk^XgcmYX*l0q2SM%>5osjMLjW#|*kb{5SM-s-i#8K|?etJZl0&SqGZ%JaMZ4ax!tg=?jj0j5yl>b}5XFciZ}^OD^@rGjwM6 z#s#7mZ`4hV-ZoTlf4a;r8%^^Ny>EL?JuO`*dX=wk^i1U}CaZ+Ug!N)}hxO9C_2ht+ z0O@W3X9L&%1qH2a1Y1}^uofJ5Z|W08>;;HgRylKn0o0}7OS$lF9N5C1Fzkfmcw?Re zD74C44nXb_#u}9)T)bf)vxC@^0UkHop;11iM(od^%f6lrP6FFb;i23N;ApS7j}4?4 z*@w2f4j@sG$m5AU5Nu}2br`ezMU12eXv}rQ#oSo;{%8t?8tO><#65u6h)z~{dH}_O zs12>3zslYY= z#y&E-XPPzfncQLh(rqt(RDav`S!p8&J(4~ocf!jhFW(hcm_F*+_oluwVEFTY{(JMT z>*I$;_gS)caN_#A5}!CUWaQ0H=4?C~J8I+c+Bg0Zm+U=~Y&DuLbWJzy^#tyUF43?q zbmij(9jpH#2Jt4pjTx%Bm+47<{ho5K^f5W$Et>lQfU*!A;a`Jkar47_F~~4_Pazcf0#HFaPCxqjC{*- zf9ArH8B!}R2S~_DdI15(Dj!iqORr*uw}8f|g@Fs0wq_jE5Lp| z>HLARu)P^==GR1|O2ZX_WB-yuS^kQM6~x96^okYW4kL{p4+x8APNFG21+YgE7rI&L zaDT`t7tm%_!TcWLk{e{|V#nTO-J2+;xjo<(fF|*v$wwl?IEG?YA>Yc=0RpAxYDBr3 zdYPw*xf~=N{`DkNhbZx?Wk;foZ!ZIp`OJh>Qa(l7y#XEX#S1qMFGI)*@@~AIdksM= zH+X_pkuH;M)o@UQR_*Q$+92wsg0_R_=#gXBsy3=Q@*Lk(M5_sw_4oHPn`!&m4c zS>cmRgTyNRw#2ls+(UHtmU2IiImx=;?TJ-zZ@3VHKse}J54HA3vQk#C?h7Z8#CAM3 zp4FeO2$NZ+&r`agth^}7kQLAMXrL7bC z3d)*{>84KVw;8<=cWAmjN0|C+@6LK6N2-VcYg!<8AL_j->-OpBMvHULODG?$&OP?% ztheq!U!f1O4#ObhaOW}&k$Q!t68{O6_|L#mYl!_HI9p5MIKv#n@kj_C%Gi7NV!ndc zx#Q>~|6OHEdgc(sI7*844*RS_4WvW5=G3PfhoWcEfd4^W{2rB)n$~z@w`-kOSIN%fHTzH8{k1y#KM(N z;u9`)p!uU~^j#URetfp6eFqwRe>|uC{z$cf`|hvSJCGBGZ#E-2xj+8)Havugjbrgp zq(%!e!2{88#B$#^f>`zi33xCg9={zAY3<5H&g|hR4Z^?vG?5bMCWY$6uCqhYK{+2ul}XxLX+ZY)?rB3I%XQ|#mp*adf}|;j zb0fibCzfL!r>;g};1X7kLNb&O9S|`9&ar(o_;Smq$d=N@;|4ox&tj18xMORO&;(`& z3WE~A4P1BfTyT}71<4B?ei~e$0)! zU|+pr2oj`yc`|A0|8GN@(XA^?1c_@nAJ)VQ15+_6#av)OC0sJ>4m9kG&l`{tjTJFS zojv|$Xky)e=!u-oxLp{TGm!ozt+w$gMLCftd7EE=q#(*!p-h+)Fm1 z7c=n0O=t*Hb{bZ&OiBSw^5`N=L*DU9*g|%|g+wG>ru^G5r~cmUR0uzM9+s6cX?V9E zUxtdnefJtF0w-+06{*|3`c+f}seE7>TyO8UG?rYO85VaVQGHgMS)9sZY-O%h*fq843~ap214^_Dm49U3=zgeV1fRozk(x>4QDh zgI=b-ntCt_nVwtlHmI$Dx9p{tAl70uv- zKnN2>^!+C2Y~eLgX|NA2zs{)N2u<7N~c$Qt;`A`Wvr{IoEFM_ z&jyF9vdrQLWygs%yFttyH=xg`Z zxJN6grEBGTYWfGXbR#cn#Z`h#AVNSO41U-4r?$3xSF{7YyQ}`z z_V-Y<5@BkefJ|@A5^5#2bgg_(P5*$F9=-+Ds_+xF@Hn^oiKeGh(}u|0axQ zf6QM;MGeD8UfA_~7U+bHrM06?7+NbEB6!6}X4?EoiF%)GLuEpn>E@kItJLGSbFmx0 z_6Gi3B-v^6%YWa8Ncsa351;-};0ktv^O?ug;+_%TbmLbHG;z&ey=Us^J|5Wzg)O{479BY{?&P|8~=JH|4FJh5Zd)cx}C=S zg~so*lSY2^UHrlv@A|asQ(kCeMqhMmtCz=4afF>;Hkm(jcQk!`yS#Xa8W&JWk zmo8$jj(1Ptw@&edIr3{e?vbJ6w6z&e5!9Ml>*W7g;WyKc4}+IC6FB2&jeJ?j@2K<} z>F`{%2P99e*BaY4jh{E|zhkVe`_8%eeJ;PT4!bY-h^L*`u4;Z+wck{Sov1lCYOTz# z;g3we%2wLC?Z6CvX(er^3#w$j#Ls~Y$( zXZo#l++V%}zIG;mYvi{z`fYUBUx8bL*2ub9{JEy9Yox8WPB-(rn*ByP>@DB@Mmvv} zX7lrB`%QG%S=vjJ*35-D{DC=F+e}-BDRcRjxqdSpc39wgr=81!dv*L#N#{4yVW(+t zhuShg$G6w#>G)OibnU3WS}j`7kd~GA>G+H7(h__l^-P>}T!4-b?`zLU{8v@&WITs~ zFX!v{9rLfE@BFs3Q})=}wm`?vTM)3-;c#em+tx%aBnTu@hg|Yh~`5jZi$-KC9WzyrLDhQ zcvMq_9(_Q^Z-3xY5gM^!oem!`cs8PCnXWIxL`6p$#hjeXg0Y^Dn~|Fa#L-W4#Y9mQ zv+<99@ZKnzD@2a8Da|b6=6(bF-^ltZEG^7AVLzOw1ROL`xY((1%L!wwikOl^Mz>n4uadG5>6btoMwV^?;o}YL;%vFwp zBxi}umYSECfseDrBZ!xiH8m~{<02;zW@b(Cw6n*P5po26)K~S5d?cn8*OXM*>hki6 z#IhiHhhKhDXnC@dI>A+zYRkz*kGux2y$Va-7=!}2X0b^%1;_hlskUj2#G-~uf4n%+=Z|d9{M8`U_|HmO(~6xXsjg|+iMG0&Dq9WKBm-1OY?eBI zi)d>e>0QrD%!k(6Jhdb-so7DO5no)8G_^d>GpF1J?|N2Pvc39z{=~*;<yMK^R2KX$0F3pQQr7Q;%T)Wl(WR*L|dbO+-T46%UkQ(EAJmS zT2h@~r`;cxhFZ^hRhTDTL7sBBsLI>S&e9PX>e^=pBq`I*`Ih+oU-(Z2XC&B7v5jC=Eo8Z?^ylcMe)v#Yc^qlV^EQkCJ- z##3>FSH8kBJ!m{3^Fz>hQi}>)$orH#E#+Q$iBeDQVVUrvbZqEnU`nF z$xfKiSSFhD;^4~atY+_efCV4MC0bEB)$_+C{5Ks+ z%+*DO<{ZScHLu!?05YdQGHz;+{WkT0#+I6tP?FjdkWI5fGInH~H=ml-N=65{R{1oY z+MfMvL%q+qvlQp1Y8r2BZtL-?MMo2_W?DsaV{zdGe|;^^59kmx$bNDW`Hwn!?Fa84 z^}JeIWt&-?m>(EB4GQ}e=L$8{a>d`B=pCW^66(c~VVPfqIWq zHTpM~B)aPIQ}gSQZxc%L&GDs}2M)5BcLluXIiJ%Qafrqb_L$dS-uf3jK6U?S##c~O zIH3kPV`{O=vCi_3&zejAhfZ(4fl+FxsDgSv(u^;`S)S^uFDr-_C(xcDe^;2N<4s$S zL+9gwzcO%st|=+ZuNQ5oiK_kao_Nx%U&#HR7FfUBf_!I*YRQu6wQupp6HQ^_W%3Vl zc}_}EVU8;wb1BZYQ2L|sTPT=tA%W>4^1B*$(4%?{e6z&~6zO6_!Qua*Gbim2XKA%o zFCF=@eih!Mzq+KV%meQJqlwOWTA31zaAvNt&duG)>#|VV%jNp(#xt_6h6` zo1;53=)hQY^|<3XOx9%_}LKLTLlg)$%3 z16lt7U{#&k4COI{qDV_$)Fih!zEQ~%?cCOi=v-u01PnOo5S?|8RzBzPv+f#j=XTRku{(e`_ zp78#Ik@JVAhRsCb62c|~jJ*-CsR3gVFb%s_3JPP$*?EPqNh=7O< zUHUSn@$rA5J)Qe7`qm?D*~FX&u=v(?OqpZ7w0NQuqxm)$#`uvQN55mpc+xUTU`xmgI(6f9P+rM7;a5hUiD20@$X*`;vnUOm)I zys`k(54EK6jyW*P*LKOMGO^4vZ$? zW5XDIdxL~I*bJV}*$MsoHJDERC9RonCG0{>ql-0=6c1i1S52_dUtS=f>)moqp8DCh z$>IBRS@qd7@VD=SKazGVqLVm%8}Z9A6RXR2V@xa@rTM&F7~_vWh0*ZtzhY!s6BrX5 zMb=k0ugJhMhso6VCzP7eqql?QGH7HDBkn|yrVWm;A_OPC|7|%q8Q5zK&SXY# z%sHG`K_?BQ=AN4|GG|HgsGU-bshNw>Fo_@)n{WBlXog(h4=9NfoBsnQy(9$b5(SCr zIz(H){g1S`>tT$smncYySA-zNyJ)_-1tSx2fb_sR4EHU^P)|-aYZhVjo!4kt_Ryaw zM&^8EJj<**OZdEZY5pY2O6MEW8db$?Oh zwc){Bme~lI5#nffin)+5K7*Yc@MICUtbTHmpum zYImv)kI=GFyHCx`(H>JrPk`*O_B}N-QN}W#fSozqpmD1Whq2A9D``{v%$OOzp=C|c z5R8Sa`e%)o-fmkuMY_8%x*Pj$$&&0jBQ)BB4Js2O&0Ba)Cg zx)l9KeQq3?oA|GjJhC+Uv(K%Rm-hRpP|BKZz%g&Fsshf;KSk$@AJ=X+`uEk^0=4TS zAef^jjD{cFPZgS_#TX4WVT|2L&DVTrm&T`y7Jl2JYGV`i6PhR+|K~^Hqmmt1HFzG= z#MtQ5C;}bR>0F~S=?uI5B~%}dAEM&ec-i+fVO{*@d+VTw?{C8mr1i0b{8CfQ#)^MU>RJzKOl-VlQq!i3UtI4%12*g~N97sj zJX++_`I@6EGzF!P+_sclcPtf(@S9S5Y;r}& z83Lk76$nUS0i@UBH2mA?_)f2FiGRB}$*Dbgw*o^Y_Gt!K{$Dl!8~#C*eJZh+ z-^d?0BUu$p^lI||M*iH$pZ_=gnbBv};W(i$iF9S0uV0k9GFJM4%qbAAhFDc&{j1tM zO`E!LOnh+}e%1cDuKd$pPhi(HuP21X^&UTZJxM{BrC*7@OcpM=%&le$vWJ7=P@i94 zgCs7>D##rk@ehsI{3D^}p1x4g;<{Z63rk6$xI8b^-!<*xT+r%<2YMpSp}xWTkiVbN z6Ka8s>gn(48|}-)`nRUAdC9$A|4@&=KNx~BU@U8cOLgZ^+-@9kTm-B8~OpP7&kw4-Y3XT49fiPr?faLqo$o!y_T^S5hkRO8Nzg z{V6VU!C+8HE+CY2EbUMb^p8d&X#|8wBOpYIK-=w46F{(Xdk)~#?s>z5J-uo6h!`b> zR@jrGSr{DXmprjeshJY5q+g)epW-rq%pd9Lvij4ElaWe#U2~* zHW$-y?3ejF$C6U9?^D6%>FFp?icyw|Ldf@Z+{eOSQ>J`?7v6fNV43Nq%$1ButGT3@ zSF^b{7IE+7k}*?0Axke;8K0YH5R+NQNl8D=<{n6^uxxnk-B1tw~wG z&*olT#T_<^vNFCLNU}1pGssf*YeC8js{FW$o2=wwm68-XlTt4)Nh#^rtCAvi9)*;N zwOnG2EJgW=ASFeKok2?KTv5oFT*t-M$ueebiSkSmJG4U9#9wiVJHHxPQ+zkMo{O!Q zWhuRMO}@)KQ=?uwG{&Q=Bzf#l@;*A+vW@@-C&c!@(u&vWQtbD|J6p$e46;G3WJ_ zF~wCAHC&=vmN9ErCGU|+D{8q|tt?^Io>JaT6;dYZxkTOdmNLa#lP)gilBLYrTd6y= z(h@h9Xpm*h+F#21x1#Ge=XY?28@X6xA-+<~aDPzT)D<2f4v%}dPp^VQ=3QT~mgp~f zxD%e4HIQEb6b6Wg{plv|VAJ(;mMlnNrzmJo3wNPe7BuU~r@g%>f`8h}9rVg#W*z+G z8<9f7o;L16>-Cc$y=5r`oNnh1w$ENb@>-mHa}y+p_uh8oIV%xcZTwP9Gm|}=VFFgQdpqlJ)VZvJH@Xe1@U|%{VKYvd7f{? zzx1o+`ElOT&D(jto40lgIE#hn>uF}?dD_bZPCkKb0mjyA8Rz*PKFIS`<4z!22wusz z6GsOqg5U`vzzvPyZ5_8j;$|MSRo#I3dV)=)Moo7W_V+>tgaUSt+rYmXYVi?L!@&dQ zhY4OzYSec_Ehqv0?F3uL0o%rjbc6>h=QvndstC3b?81b%_F~|>XaFiXj=Q%Qjo`~S zR?{ZYNFtPeHkvv*1_OQe;g*(;=Jtw6Q&&H1hO?HB8I8iWCK_$4mb(Sm!iNPc3>W++ z02s(ed`Dkow7qq!$J1?TOsD4umT#8mw+bYg^zbR{?em)2y>=Ju>5=wZd3*C}qj4=( zt89X4RPqXMA ztE9h`ugj2+Xy7M$S(vWEXsLkRgB?|_aKLT}2UofTKCZDLQGT~A(|*AS$_eS$K+i73 z!Qbk!ftRDbHG-I!5TBt+(f(@Q>6Z94UcnxUwZkA#chi-W8Db>B-QihKx|;SCx=o>H2v3?1KS2 z#TZ#Nh9o1MyKTIrpM@9mLr6k}e+1USm6kz&g}0$m$PtuMihg_s`Z|&)(KoiYxweyU z12R4G9jyaft|VVcFUO`|rboV~(`V+lSRWPkaWNjYT4}$PcWz&kp5NpUWqwgrUJ(>rFO7xdrWd7!8t4_O^_c((;BB4o>G zdxck#Pu2=qAIFw;Y4M!CKV<0B5%#0+3%#>pjYMC~54a`$q<$q7fucVMterLCKwnox z?!A^ZNNuvYnm5fvPo*N!%dx3S(erMU7VWu`da$3CT-;rQHHBsBBL~rG5 zZIXYgtU}WkFEy$D%?szz_CQ5s*z53)1gloB2zV`~AZ$fy;lk04Y?7`5q@;Mhch8`o za7YAG`n9&V)eN@dZ0HbHEPZtK0yis;4$Kti_4N6y&~HUJLk5#n3Ufw_H!HYtFn)vmIvD_s>VF1!>&H> zHeefhTo~ah2i<1`y->VzP#XsXFXJ*RY!x0Dg!`M*WkM`WO1W5q{=&T%&!iF6bkD_4 z$PY`P)w|8#>I&DHkZ=7Rks!7)1%^TY&^@D}k+R_6V3~htXkgqhMt@lw=r^obS8gg_ Wfxpk-KM(uA{D=7*(%kqzhW;1pJ_q&y literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..990cf8175659bbcc81bd59d42f0ef256a3eb5006 GIT binary patch literal 17525 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%0DiHc{`H;^GtIfSAm-bWwUl%D>Lb+ai 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-17_ST_30_minute_030.parquet new file mode 100644 index 0000000000000000000000000000000000000000..07e0b6ebe6e7ef39a7e2c5d9a561af3edda11384 GIT binary patch literal 13311 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 by-dHX41b@&e;)RK`49C!(AJ9o_2+*9G)MPA literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/data/temp-soil_GRSM005503_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..13081052e9d8302ccbc013e8750a74484304c4c2 GIT binary patch literal 18147 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%0DiHc{`H;^GtIfSAm-bWwUl%D>Lb+ai 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json new file mode 100644 index 000000000..ffef1dd05 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.265", + "FreqSamp":"0.1", + "HOR":"005", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105369", + "VER":"504", + "geometry":null, + "location":"plot5", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-17T00:00:00Z", + "end_date":"2025-10-18T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP5, Z4 Depth", + "domain":"D07", + "name":"CFGLOC105369", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth4", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json new file mode 100644 index 000000000..8355c31c6 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json @@ -0,0 +1,407 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.265", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"005", + "location":"plot5", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-17T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.50135, + 35.6892, + 576.39 + ], + [ + -83.5014028, + 35.6892, + 575.2 + ], + [ + -83.5014028, + 35.6892444, + 575.2 + ], + [ + -83.5013472, + 35.6892444, + 576.39 + ], + [ + -83.50135, + 35.6892, + 576.39 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "576.39" + ], + [ + "ElevSoilC2", + "575.20" + ], + [ + "ElevSoilC3", + "575.20" + ], + [ + "ElevSoilC4", + "576.39" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6892000" + ], + [ + "LatSoilC2", + "35.6892000" + ], + [ + "LatSoilC3", + "35.6892444" + ], + [ + "LatSoilC4", + "35.6892444" + ], + [ + "locDisturb", + "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" + ], + [ + "LonSoilC1", + "-83.5013500" + ], + [ + "LonSoilC2", + "-83.5014028" + ], + [ + "LonSoilC3", + "-83.5014028" + ], + [ + "LonSoilC4", + "-83.5013472" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105351" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 270 + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0 + + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105351" + }, + "type":"Feature" + }, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 1.285, + "y_offset": 2.975 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105369", + "remove_date":"2025-10-18T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105369", + "subLocation":"depth4", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"504" + } + ], + "source_id": 20037, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..d8b9bdba4ed98ce5a3e015a5e2a6aaffd70148ae GIT binary patch literal 17309 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 0) { + expect_true(all(grepl("^temp-soil_", result$sensor_id))) + + # Check that depths are numeric and negative (below surface) + expect_true(all(is.numeric(result$depth_m))) + expect_true(all(result$depth_m <= 0, na.rm = TRUE)) + + # Check that paths exist + expect_true(all(file.exists(result$data_path))) + expect_true(all(file.exists(result$location_path))) + } + } else { + skip("Test data not available") + } +}) + +# Test with empty directory (no temperature sensor data) +test_that("Test def.load.temp.sensors with no data", { + # Use fake location + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/2025/10/14/") + + # Only run this test if the test data exists + + result <- def.load.temp.sensors(DirTemp = DirTemp, log = NULL) + + # Check that we got a data frame back + expect_equal(nrow(result), 0) + +}) + +# Test with multiple location files for same sensor (should average depths) +test_that("Test def.load.temp.sensors with more than one depth", { + # Use the actual test data + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/") + + # Only run this test if the test data exists + if (dir.exists(DirTemp)) { + result <- def.load.temp.sensors(DirTemp = DirTemp, log = NULL) + # Should average multiple depths for same sensor + expect_equal(result$depth_m[result$sensor_id=="temp-soil_GRSM005501"], mean(c(-.005, -.009))) + } else { + skip("Test data not available") + } +}) + +# Test with no location files +test_that("Test def.load.temp.sensors no location data found", { + # Use the test data + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/no_locations/2025/10/17/conc-h2o-soil-salinity_GRSM005501/") + + # Only run this test if the test data exists + if (dir.exists(DirTemp)) { + result <- def.load.temp.sensors(DirTemp = DirTemp, log = NULL) + # Should return empty data frame + expect_equal(nrow(result), 0) + } else { + skip("Test data not available") + } +}) + +# Test with missing z_offset in location file (should result in NA depth) +test_that("Test def.load.temp.sensors with missing z_offset", { + # Use the actual test data + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/") + # Only run this test if the test data exists + if (dir.exists(DirTemp)) { + result <- def.load.temp.sensors(DirTemp = DirTemp, log = NULL) + # Should have NA in depth_m column + expect_true(any(is.na(result$depth_m))) + } else { + skip("Test data not available") + } +}) + + +######################################################################################################## +# Test 4: def.calc.temp.flags +######################################################################################################## +test_that("Test def.calc.temp.flags basic functionality", { + # Create temp directory for test data + tmpDir <- tempdir() + testDataPath <- file.path(tmpDir, "test_temp_data.parquet") + + # Create test temperature data + testTempData <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00", + "2025-10-17 00:02:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00", + "2025-10-17 00:03:00"), tz = "UTC"), + soilTempMean = c(5.0, -2.0, 3.0), # Temperature values + soilTempExpUncert = c(1.0, 1.0, 1.0), # Uncertainty values + finalQF = c(0L, 0L, 0L) # All good data + ) + + # Write test data + arrow::write_parquet(testTempData, testDataPath) + + # Create sensor info + sensorInfo <- list( + closest = data.frame( + sensor_id = "temp-soil_TEST001", + depth_m = -0.20, + data_path = testDataPath, + location_path = "/fake/path.json", + stringsAsFactors = FALSE + ), + neighbors = list( + higher = NULL, + lower = NULL + ) + ) + + # Calculate flags + result <- def.calc.temp.flags(sensorInfo = sensorInfo, log = NULL) + + # Check structure + expect_true(all(c("startDateTime", "endDateTime", "temp_flag") %in% names(result))) + expect_equal(nrow(result), 3) + + # Check flag logic: temp_flag = 1 if soilTempMean < soilTempExpUncert + # Row 1: 5.0 < 1.0 = FALSE -> 0 + # Row 2: -2.0 < 1.0 = TRUE -> 1 + # Row 3: 3.0 < 1.0 = FALSE -> 0 + expect_equal(result$temp_flag, c(0L, 1L, 0L)) + + # Cleanup + unlink(testDataPath) +}) + +test_that("Test def.calc.temp.flags neighbor too far", { + # Create temp directory for test data + tmpDir <- tempdir() + testDataPath1 <- file.path(tmpDir, "test_temp_data1.parquet") + testDataPath2 <- file.path(tmpDir, "test_temp_data2.parquet") + testDataPath3 <- file.path(tmpDir, "test_temp_data3.parquet") + + # Create test temperature data with some flagged intervals + testTempData1 <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + soilTempMean = c(5.0, -2.0), + soilTempExpUncert = c(1.0, 1.0), + finalQF = c(1L, 1L) # Both intervals are flagged + ) + + # Neighbor sensors with good data + testTempData2 <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + soilTempMean = c(4.5, 0.5), + soilTempExpUncert = c(1.0, 1.0), + finalQF = c(0L, 0L) # All good + ) + + testTempData3 <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + soilTempMean = c(4.8, 0.3), + soilTempExpUncert = c(1.0, 1.0), + finalQF = c(0L, 0L) # All good + ) + + # Write test data + arrow::write_parquet(testTempData1, testDataPath1) + arrow::write_parquet(testTempData2, testDataPath2) + arrow::write_parquet(testTempData3, testDataPath3) + + # Create sensor info with neighbors + sensorInfo <- list( + closest = data.frame( + sensor_id = "temp-soil_TEST001", + depth_m = -0.20, + data_path = testDataPath1, + location_path = "/fake/path1.json", + stringsAsFactors = FALSE + ), + neighbors = list( + higher = data.frame( + sensor_id = "temp-soil_TEST002", + depth_m = -0.10, + data_path = testDataPath2, + location_path = "/fake/path2.json", + stringsAsFactors = FALSE + ), + lower = data.frame( + sensor_id = "temp-soil_TEST003", + depth_m = -1.5, + data_path = testDataPath3, + location_path = "/fake/path3.json", + stringsAsFactors = FALSE + ) + ) + ) + + # Calculate flags with distance check + result <- def.calc.temp.flags( + sensorInfo = sensorInfo, + targetDepth = -0.20, + distThreshold = 0.15, + log = NULL + ) + + # Check structure + expect_equal(nrow(result), 2) + + # First interval: primary sensor flagged and neighbor too far (>0.15m) + expect_equal(result$temp_flag[1], -1L) + + + # Cleanup + unlink(c(testDataPath1, testDataPath2, testDataPath3)) +}) + + +test_that("Test def.calc.temp.flags with flagged primary sensor", { + # Create temp directory for test data + tmpDir <- tempdir() + testDataPath1 <- file.path(tmpDir, "test_temp_data1.parquet") + testDataPath2 <- file.path(tmpDir, "test_temp_data2.parquet") + testDataPath3 <- file.path(tmpDir, "test_temp_data3.parquet") + + # Create test temperature data with some flagged intervals + testTempData1 <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + soilTempMean = c(5.0, -2.0), + soilTempExpUncert = c(1.0, 1.0), + finalQF = c(0L, 1L) # Second interval is flagged + ) + + # Neighbor sensors with good data + testTempData2 <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + soilTempMean = c(4.5, 0.5), + soilTempExpUncert = c(1.0, 1.0), + finalQF = c(0L, 0L) # All good + ) + + testTempData3 <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + soilTempMean = c(4.8, 0.3), + soilTempExpUncert = c(1.0, 1.0), + finalQF = c(0L, 0L) # All good + ) + + # Write test data + arrow::write_parquet(testTempData1, testDataPath1) + arrow::write_parquet(testTempData2, testDataPath2) + arrow::write_parquet(testTempData3, testDataPath3) + + # Create sensor info with neighbors + sensorInfo <- list( + closest = data.frame( + sensor_id = "temp-soil_TEST001", + depth_m = -0.20, + data_path = testDataPath1, + location_path = "/fake/path1.json", + stringsAsFactors = FALSE + ), + neighbors = list( + higher = data.frame( + sensor_id = "temp-soil_TEST002", + depth_m = -0.10, + data_path = testDataPath2, + location_path = "/fake/path2.json", + stringsAsFactors = FALSE + ), + lower = data.frame( + sensor_id = "temp-soil_TEST003", + depth_m = -0.30, + data_path = testDataPath3, + location_path = "/fake/path3.json", + stringsAsFactors = FALSE + ) + ) + ) + + # Calculate flags with distance check + result <- def.calc.temp.flags( + sensorInfo = sensorInfo, + targetDepth = -0.20, + distThreshold = 0.15, + log = NULL + ) + + # Check structure + expect_equal(nrow(result), 2) + + # First interval: primary sensor is good, should use it + expect_equal(result$temp_flag[1], 0L) # 5.0 < 1.0 = FALSE -> 0 + + # Second interval: primary sensor is flagged, should use neighbor average + # Neighbor avg: ((0.5 - 1.0) + (0.3 - 1.0)) / 2 = (-0.5 + -0.7) / 2 = -0.6 + # avgZeroCheck < 1.0 = TRUE -> 1 + expect_equal(result$temp_flag[2], 1L) + + # Cleanup + unlink(c(testDataPath1, testDataPath2, testDataPath3)) +}) + +######################################################################################################## +# Test 5: def.apply.temp.flags +######################################################################################################## +test_that("Test def.apply.temp.flags joins flags correctly", { + # Create high-frequency soil moisture data + dataSm <- data.frame( + readout_time = as.POSIXct(c( + "2025-10-17 00:00:05", "2025-10-17 00:00:15", "2025-10-17 00:00:25", + "2025-10-17 00:01:05", "2025-10-17 00:01:15", "2025-10-17 00:01:25" + ), tz = "UTC"), + depth01SoilMoisture = c(0.25, 0.26, 0.24, 0.27, 0.25, 0.26), + tempTestDepth01QF = rep(-1L, 6) # Initialize to -1 + ) + + # Create minute-interval temperature flags + tempData <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + temp_flag = c(0L, 1L) + ) + + # Apply flags + result <- def.apply.temp.flags( + dataSm = dataSm, + tempData = tempData, + qfColName = "tempTestDepth01QF", + log = NULL + ) + + # Check that flags were applied correctly + # First 3 rows fall in first minute interval (flag = 0) + expect_equal(result$tempTestDepth01QF[1:3], c(0L, 0L, 0L)) + + # Last 3 rows fall in second minute interval (flag = 1) + expect_equal(result$tempTestDepth01QF[4:6], c(1L, 1L, 1L)) +}) + +test_that("Test def.apply.temp.flags handles missing intervals", { + # Create data with some points outside the temp data intervals + dataSm <- data.frame( + readout_time = as.POSIXct(c( + "2025-10-17 00:00:05", # Inside first interval + "2025-10-17 00:02:05", # Outside any interval + "2025-10-17 00:01:05" # Inside second interval + ), tz = "UTC"), + depth01SoilMoisture = c(0.25, 0.26, 0.27), + tempTestDepth01QF = rep(-1L, 3) + ) + + tempData <- data.frame( + startDateTime = as.POSIXct(c("2025-10-17 00:00:00", "2025-10-17 00:01:00"), tz = "UTC"), + endDateTime = as.POSIXct(c("2025-10-17 00:01:00", "2025-10-17 00:02:00"), tz = "UTC"), + temp_flag = c(0L, 1L) + ) + + result <- def.apply.temp.flags( + dataSm = dataSm, + tempData = tempData, + qfColName = "tempTestDepth01QF", + log = NULL + ) + + # First row should get flag 0 + expect_equal(result$tempTestDepth01QF[1], 0L) + + # Second row has no matching interval, should remain -1 + expect_equal(result$tempTestDepth01QF[2], -1L) + + # Third row should get flag 1 + expect_equal(result$tempTestDepth01QF[3], 1L) +}) + + + +######################################################################################################## +# Test 6: Integration tests with real data - various edge cases and failure scenarios +######################################################################################################## +test_that("Integration test with good data (baseline)", { + # Use actual test data paths + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501") + DirOutBase <- file.path(tempdir(), "test_output") + + # Only run if test data exists + if (dir.exists(DirIn) && dir.exists(DirTemp)) { + # Clean output directory + if (dir.exists(DirOutBase)) { + unlink(DirOutBase, recursive = TRUE) + } + + # Run the wrap function + + wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + ) + + # Check that output was created + expect_true(dir.exists(DirOutBase)) + + # Check that flags directory was created + flagsDir <- file.path(DirOutBase, "enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags") + expect_true(dir.exists(flagsDir)) + + # Check that flag file exists + flagFiles <- list.files(flagsDir, pattern = "flagsPlausibility.parquet") + expect_equal(length(flagFiles), 1) + + # Read and validate the output + flagData <- arrow::read_parquet(file.path(flagsDir, flagFiles[1])) + + # Check that tempTest columns were added + tempTestCols <- names(flagData)[grepl("tempTestDepth", names(flagData))] + expect_true(length(tempTestCols) == 8) + + # Check that flags have valid values (0, 1, or -1) + for (col in tempTestCols) { + expect_true(all(flagData[[col]] %in% c(-1L, 0L, 1L))) + } + + # Check that timestamps are present + expect_true("readout_time" %in% names(flagData)) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + } else { + skip("Test data not available") + } +}) + +# Test with missing depth values in soil moisture data +test_that("Integration test with missing soil moisture depths", { + # Use actual test data paths + # Soil moisture data missing depth information + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + # Temperature data is good + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501") + DirOutBase <- file.path(tempdir(), "test_output") + + # Only run if test data exists + if (dir.exists(DirIn) && dir.exists(DirTemp)) { + # Clean output directory + if (dir.exists(DirOutBase)) { + unlink(DirOutBase, recursive = TRUE) + } + + # Run the wrap function + + wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + ) + + # Check that output was created + expect_true(dir.exists(DirOutBase)) + + # Check that flags directory was created + flagsDir <- file.path(DirOutBase, "enviroscan/tests/no_depths/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags") + expect_true(dir.exists(flagsDir)) + + # Check that flag file exists + flagFiles <- list.files(flagsDir, pattern = "flagsPlausibility.parquet") + expect_equal(length(flagFiles), 1) + + # Read and validate the output + flagData <- arrow::read_parquet(file.path(flagsDir, flagFiles[1])) + + # Check that data is all -1 due to missing depth information + expect_true(all(flagData$tempTestDepth01QF), -1) + + tempTestCols <- names(flagData)[grepl("tempTestDepth", names(flagData))] + + # Check that flags have valid values (0, 1, or -1) + for (col in tempTestCols) { + expect_true(all(flagData[[col]] %in% c(-1L, 0L, 1L))) + } + + # Check that timestamps are present + expect_true("readout_time" %in% names(flagData)) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + } else { + skip("Test data not available") + } +}) + + +# Test with freezing temperature conditions +test_that("Integration test with all freezing data", { + # Use actual test data paths + # Good soil moisture data + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + # Temperature data with all freezing values + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/") + DirOutBase <- file.path(tempdir(), "test_output") + + # Only run if test data exists + if (dir.exists(DirIn) && dir.exists(DirTemp)) { + # Clean output directory + if (dir.exists(DirOutBase)) { + unlink(DirOutBase, recursive = TRUE) + } + + # Run the wrap function + + wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + ) + + # Check that output was created + expect_true(dir.exists(DirOutBase)) + + # Check that flags directory was created + flagsDir <- file.path(DirOutBase, "enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags") + expect_true(dir.exists(flagsDir)) + + # Check that flag file exists + flagFiles <- list.files(flagsDir, pattern = "flagsPlausibility.parquet") + expect_equal(length(flagFiles), 1) + + # Read and validate the output + flagData <- arrow::read_parquet(file.path(flagsDir, flagFiles[1])) + + # Check that data was flagged as frozen + expect_true(all(flagData$tempTestDepth02QF), 1) + + tempTestCols <- names(flagData)[grepl("tempTestDepth", names(flagData))] + + # Check that flags have valid values (0, 1, or -1) + for (col in tempTestCols) { + expect_true(all(flagData[[col]] %in% c(-1L, 0L, 1L))) + } + + # Check that timestamps are present + expect_true("readout_time" %in% names(flagData)) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + } else { + skip("Test data not available") + } +}) + +# Test with missing temperature data directory +test_that("Integration test with no temperature data", { + # Use actual test data paths + # Good soil moisture data + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + # Non-existent temperature data path + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/no_temp_data/2020/10/14") + DirOutBase <- file.path(tempdir(), "test_output") + + # Run the wrap function + + wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + ) + + # Check that output was created + expect_true(dir.exists(DirOutBase)) + + # Check that flags directory was created + flagsDir <- file.path(DirOutBase, "enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags") + expect_true(dir.exists(flagsDir)) + + # Check that flag file exists + flagFiles <- list.files(flagsDir, pattern = "flagsPlausibility.parquet") + expect_equal(length(flagFiles), 1) + + # Read and validate the output + flagData <- arrow::read_parquet(file.path(flagsDir, flagFiles[1])) + + + tempTestCols <- names(flagData)[grepl("tempTestDepth", names(flagData))] + + # All flags should be -1 when no temperature data is available + for (col in tempTestCols) { + expect_true(all(flagData[[col]] %in% c(-1L))) + } + + # Check that all 8 columns are present + expect_true(length(tempTestCols)==8) + + # Check that timestamps are present + expect_true("readout_time" %in% names(flagData)) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + +}) + + +# Test with missing z_offset in temperature sensor location files +test_that("Integration test with missing z_offset", { + # Use actual test data paths + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + # Temperature data with location files missing z_offset field + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/no_z_offset/2025/10/17/conc-h2o-soil-salinity_GRSM005501/") + DirOutBase <- file.path(tempdir(), "test_output") + + # Run the wrap function + + wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + ) + + # Check that output was created + expect_true(dir.exists(DirOutBase)) + + # Check that flags directory was created + flagsDir <- file.path(DirOutBase, "enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/") + expect_true(dir.exists(flagsDir)) + + # Check that flag file exists + flagFiles <- list.files(flagsDir, pattern = "flagsPlausibility.parquet") + expect_equal(length(flagFiles), 1) + + # Read and validate the output + flagData <- arrow::read_parquet(file.path(flagsDir, flagFiles[1])) + + tempTestCols <- names(flagData)[grepl("tempTestDepth", names(flagData))] + + # Flags should have valid values (may have some -1 for missing z_offset) + for (col in tempTestCols) { + expect_true(all(flagData[[col]] %in% c(0L, 1L,-1L))) + } + + # Check that all 8 columns are present + expect_true(length(tempTestCols)==8) + + # Check that timestamps are present + expect_true("readout_time" %in% names(flagData)) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + +}) + + +test_that("Integration test with missing threshold file (expect error)", { + # Use actual test data paths + # Soil moisture data without threshold configuration file + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + # Temperature data path (test should fail before reaching here) + DirTemp <- file.path(getwd(), "pfs/envscn_temp_flags/temp/tests/no_temp_data/2020/10/14") + DirOutBase <- file.path(tempdir(), "test_output") + + # Run the wrap function + + expect_error(wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + )) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + +}) From fca90a7a36f8e4c860721ceca4d87909b59c7fd9 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Fri, 20 Feb 2026 13:43:49 -0700 Subject: [PATCH 24/46] delete extra test data --- .../group/CFGLOC105367.json | 27 - ...M005501_2025-10-17_ST_1_minute_001.parquet | Bin 18420 -> 0 bytes .../location/CFGLOC105363.json | 39 - .../location/prt_24772_locations.json | 409 ------ ...M005502_2025-10-17_ST_1_minute_001.parquet | Bin 18423 -> 0 bytes .../location/CFGLOC105365.json | 39 - .../location/prt_18574_locations.json | 409 ------ ...M005506_2025-10-17_ST_1_minute_001.parquet | Bin 18503 -> 0 bytes .../location/CFGLOC105373.json | 39 - .../location/prt_16828_locations.json | 409 ------ ...enviroscan_CFGLOC105360_2025-10-18.parquet | Bin 15760 -> 0 bytes ...n_CFGLOC105360_2025-10-18_flagsCal.parquet | Bin 10861 -> 0 bytes .../CFGLOC105360/location/CFGLOC105360.json | 22 - .../location/enviroscan_46446_locations.json | 96 -- ...GLOC105360_2025-10-18_uncertaintyCoef.json | 1202 ----------------- ...C105360_2025-10-18_uncertaintyData.parquet | Bin 26194 -> 0 bytes ...M005504_2025-10-18_ST_1_minute_001.parquet | Bin 17987 -> 0 bytes ...005504_2025-10-18_ST_30_minute_030.parquet | Bin 13291 -> 0 bytes .../location/CFGLOC105369.json | 39 - .../location/prt_20037_locations.json | 409 ------ ...M005505_2025-10-18_ST_1_minute_001.parquet | Bin 17728 -> 0 bytes ...005505_2025-10-18_ST_30_minute_030.parquet | Bin 13262 -> 0 bytes .../location/CFGLOC105371.json | 39 - .../location/prt_23800_locations.json | 409 ------ ...M005506_2025-10-18_ST_1_minute_001.parquet | Bin 18427 -> 0 bytes ...005506_2025-10-18_ST_30_minute_030.parquet | Bin 11808 -> 0 bytes 26 files changed, 3587 deletions(-) delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-17_ST_1_minute_001.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-17_ST_1_minute_001.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/CFGLOC105365.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/location/prt_18574_locations.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-17_ST_1_minute_001.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/CFGLOC105373.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/prt_16828_locations.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/data/enviroscan_CFGLOC105360_2025-10-18.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags/enviroscan_CFGLOC105360_2025-10-18_flagsCal.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/CFGLOC105360.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/location/enviroscan_46446_locations.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/uncertainty_coef/enviroscan_CFGLOC105360_2025-10-18_uncertaintyCoef.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/uncertainty_data/enviroscan_CFGLOC105360_2025-10-18_uncertaintyData.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_1_minute_001.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_30_minute_030.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/CFGLOC105369.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/location/prt_20037_locations.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-18_ST_1_minute_001.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/data/temp-soil_GRSM005505_2025-10-18_ST_30_minute_030.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_1_minute_001.parquet delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_30_minute_030.parquet diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json deleted file mode 100644 index 7787fe84d..000000000 --- a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/all_freezing/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005503/group/CFGLOC105367.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "features": [ - { - "domain": "D07", - "geometry": null, - "HOR": "005", - "properties": { - "active_periods": [ - { - "start_date": "2025-10-17T00:00:00Z", - "end_date": "2025-10-18T00:00:00Z" - } - ], - "data_product_ID": [ - "DP1.00041.001" - ], - "group": "temp-soil_GRSM005503", - "name": "CFGLOC105367" - }, - "site": "GRSM", - "type": "Feature", - "VER": "503", - "visibility_code": "public" - } - ], - "type": "FeatureCollection" -} \ No newline at end of file diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/data/temp-soil_GRSM005501_2025-10-17_ST_1_minute_001.parquet deleted file mode 100644 index 8307df09f1386dec54b1232698f40677cd751dbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18420 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 h*BXK#P@!#{0$+@0kGV%ZW`46+EirD}F diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json deleted file mode 100644 index cb01e58ab..000000000 --- a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/CFGLOC105363.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "features":[ - { - "AngNedZaxs":"NA", - "AngPtch":"0", - "Cal$PrdLab":"one time before installation", - "Data Rate":"0.1", - "DistZaxsLvlMeasSoil":"-0.025", - "FreqSamp":"0.1", - "HOR":"005", - "IS Processing Default Start Date":"2017-08-20T00:00:00Z", - "Required Asset Management Location Code":"CFGLOC105363", - "VER":"501", - "geometry":null, - "location":"plot5", - "properties":{ - "active_periods":[ - { - "start_date":"2025-10-17T00:00:00Z", - "end_date":"2025-10-18T00:00:00Z" - } - ], - "context":[ - - ], - "description":"Great Smoky Mountains Soil Temp Profile SP5, Z1 Depth", - "domain":"D07", - "name":"CFGLOC105363", - "site":"GRSM", - "type":"CONFIG" - }, - "subLocation":"depth1", - "subSystem":"temp-profile", - "system":"soil", - "type":"Feature" - } - ], - "type":"FeatureCollection" -} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json deleted file mode 100644 index acb77a1f0..000000000 --- a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005501/location/prt_24772_locations.json +++ /dev/null @@ -1,409 +0,0 @@ -{ - "features":[ - { - "AngNedZaxs":"NA", - "AngPtch":"0", - "asset_manufacturer":"Thermometrics", - "asset_model":"R000-00000011", - "asset_software_version":null, - "Cal$PrdLab":"one time before installation", - "Data Rate":"0.1", - "DistZaxsLvlMeasSoil":"-0.025", - "FreqSamp":"0.1", - "geometry":null, - "HOR":"005", - "location":"plot5", - "properties":{ - "context":[ - - ], - "domain":"D07", - "install_date":"2025-10-17T00:00:00Z", - "locations":{ - "features":[ - { - "geometry":null, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-18T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "State province", - "TN" - ], - [ - "UTM Zone", - "17N" - ] - ], - "reference_location":{ - "geometry":null, - "properties":{ - "locations":{ - "features":[ - { - "geometry":{ - "coordinates":[ - [ - -83.50135, - 35.6892, - 576.39 - ], - [ - -83.5014028, - 35.6892, - 575.2 - ], - [ - -83.5014028, - 35.6892444, - 575.2 - ], - [ - -83.5013472, - 35.6892444, - 576.39 - ], - [ - -83.50135, - 35.6892, - 576.39 - ] - ], - "type":"Polygon" - }, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-18T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "AngAzXaxsRefeSoil", - "C4" - ], - [ - "AngAzYaxsRefeSoil", - "C2" - ], - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "ElevSoilC1", - "576.39" - ], - [ - "ElevSoilC2", - "575.20" - ], - [ - "ElevSoilC3", - "575.20" - ], - [ - "ElevSoilC4", - "576.39" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "LatSoilC1", - "35.6892000" - ], - [ - "LatSoilC2", - "35.6892000" - ], - [ - "LatSoilC3", - "35.6892444" - ], - [ - "LatSoilC4", - "35.6892444" - ], - [ - "locDisturb", - "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" - ], - [ - "LonSoilC1", - "-83.5013500" - ], - [ - "LonSoilC2", - "-83.5014028" - ], - [ - "LonSoilC3", - "-83.5014028" - ], - [ - "LonSoilC4", - "-83.5013472" - ], - [ - "Orientation Number", - "2" - ], - [ - "RefeSoil", - "C1" - ], - [ - "Required Asset Management Location Code", - "SOILPL105351" - ], - [ - "State province", - "TN" - ], - [ - "UTM Zone", - "17N" - ], - [ - "x Azimuth Angle", - 3 - ], - [ - "y Azimuth Angle", - 270 - ] - ], - "reference_location":null, - "start_date":"2025-10-17T00:00:00Z", - "x_offset": 0, - "y_offset": 0, - "z_offset": 0 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - }, - "name":"SOILPL105351" - }, - "type":"Feature" - }, - "start_date":"2025-10-17T00:00:00Z", - "x_offset": 1.285, - "y_offset": 2.975, - "z_offset": -0.005 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - }, - "name":"CFGLOC105363", - "remove_date":"2025-10-18T00:00:00Z", - "site":"GRSM", - "site_location":{ - "features":[ - { - "geometry":{ - "coordinates":[ - -83.50195, - 35.68896, - 575.396725 - ], - "type":"Point" - }, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-18T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "AERONET_XREF", - "NEON_GRSM" - ], - [ - "AngAzWindPrim", - "140-320" - ], - [ - "AngAzWindScnd", - "NA" - ], - [ - "AngAzWindTrty", - "NA" - ], - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "DEIMS-SDR Site ID", - "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" - ], - [ - "DistZaxsCnpy", - "31" - ], - [ - "DistZaxsDisp", - "20.2" - ], - [ - "Dom", - "D07" - ], - [ - "DURATION", - "Gradient" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "HABITAT", - "Terrestrial" - ], - [ - "IS Processing Default Start Date", - "2017-08-20T00:00:00Z" - ], - [ - "NEONSCI_FIELD_SITE_URL_PATH", - "great-smoky-mountains-national-park-twin-creeks" - ], - [ - "Pf$AngEnuXaxs", - "NA" - ], - [ - "Pf$AngEnuYaxs", - "NA" - ], - [ - "Pf$Ofst", - "NA" - ], - [ - "PrdDcmpMax", - "3" - ], - [ - "PrdIncrAgrDcmp", - "1" - ], - [ - "PrdIncrCalc", - "1" - ], - [ - "PrdIncrPf", - "1" - ], - [ - "PrdWndwAgrDcmp", - "5" - ], - [ - "PrdWndwCalc", - "3" - ], - [ - "PrdWndwPf", - "9" - ], - [ - "PresAtmSite", - "93630" - ], - [ - "Private", - "FALSE" - ], - [ - "Read", - "HDF5" - ], - [ - "Required Asset Management Location Code", - "GRSM" - ], - [ - "Site Timezone", - "US/Eastern" - ], - [ - "State Abbreviation", - "TN" - ], - [ - "State province", - "TN" - ], - [ - "TypeEco", - "Hardwood deciduous forest dominated by oaks and maples" - ], - [ - "TypeSoil", - "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" - ], - [ - "UTM Zone", - "17N" - ], - [ - "ZoneTime", - "EST" - ] - ], - "reference_location":null, - "start_date":"2025-10-17T00:00:00Z", - "x_offset": 0, - "y_offset": 0, - "z_offset": 0 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - } - }, - "Required Asset Management Location Code":"CFGLOC105363", - "subLocation":"depth1", - "subSystem":"temp-profile", - "system":"soil", - "type":"Feature", - "VER":"501" - } - ], - "source_id": 24772, - "source_type":"prt", - "type":"FeatureCollection" -} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005502/data/temp-soil_GRSM005502_2025-10-17_ST_1_minute_001.parquet deleted file mode 100644 index 0570c0dcece2f8dafa1354835ba986d4548dbd11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18423 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 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_1_minute_001.parquet deleted file mode 100644 index 03224a3aecb02b858e82010f37cc7812fecea005..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17987 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 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005504/data/temp-soil_GRSM005504_2025-10-18_ST_30_minute_030.parquet deleted file mode 100644 index 85fa664075e31384db5f455d8c69839a379eed07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13291 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 N%*T+{ivN@6e*tv1^@sof diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json deleted file mode 100644 index cd4a19f59..000000000 --- a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/CFGLOC105371.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "features":[ - { - "AngNedZaxs":"NA", - "AngPtch":"0", - "Cal$PrdLab":"one time before installation", - "Data Rate":"0.1", - "DistZaxsLvlMeasSoil":"-0.365", - "FreqSamp":"0.1", - "HOR":"005", - "IS Processing Default Start Date":"2017-08-20T00:00:00Z", - "Required Asset Management Location Code":"CFGLOC105371", - "VER":"505", - "geometry":null, - "location":"plot5", - "properties":{ - "active_periods":[ - { - "start_date":"2025-10-18T00:00:00Z", - "end_date":"2025-10-19T00:00:00Z" - } - ], - "context":[ - - ], - "description":"Great Smoky Mountains Soil Temp Profile SP5, Z5 Depth", - "domain":"D07", - "name":"CFGLOC105371", - "site":"GRSM", - "type":"CONFIG" - }, - "subLocation":"depth5", - "subSystem":"temp-profile", - "system":"soil", - "type":"Feature" - } - ], - "type":"FeatureCollection" -} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json deleted file mode 100644 index e92450df0..000000000 --- a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005505/location/prt_23800_locations.json +++ /dev/null @@ -1,409 +0,0 @@ -{ - "features":[ - { - "AngNedZaxs":"NA", - "AngPtch":"0", - "asset_manufacturer":"Thermometrics", - "asset_model":"R000-00000011", - "asset_software_version":null, - "Cal$PrdLab":"one time before installation", - "Data Rate":"0.1", - "DistZaxsLvlMeasSoil":"-0.365", - "FreqSamp":"0.1", - "geometry":null, - "HOR":"005", - "location":"plot5", - "properties":{ - "context":[ - - ], - "domain":"D07", - "install_date":"2025-10-18T00:00:00Z", - "locations":{ - "features":[ - { - "geometry":null, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-19T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "State province", - "TN" - ], - [ - "UTM Zone", - "17N" - ] - ], - "reference_location":{ - "geometry":null, - "properties":{ - "locations":{ - "features":[ - { - "geometry":{ - "coordinates":[ - [ - -83.50135, - 35.6892, - 576.39 - ], - [ - -83.5014028, - 35.6892, - 575.2 - ], - [ - -83.5014028, - 35.6892444, - 575.2 - ], - [ - -83.5013472, - 35.6892444, - 576.39 - ], - [ - -83.50135, - 35.6892, - 576.39 - ] - ], - "type":"Polygon" - }, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-19T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "AngAzXaxsRefeSoil", - "C4" - ], - [ - "AngAzYaxsRefeSoil", - "C2" - ], - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "ElevSoilC1", - "576.39" - ], - [ - "ElevSoilC2", - "575.20" - ], - [ - "ElevSoilC3", - "575.20" - ], - [ - "ElevSoilC4", - "576.39" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "LatSoilC1", - "35.6892000" - ], - [ - "LatSoilC2", - "35.6892000" - ], - [ - "LatSoilC3", - "35.6892444" - ], - [ - "LatSoilC4", - "35.6892444" - ], - [ - "locDisturb", - "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" - ], - [ - "LonSoilC1", - "-83.5013500" - ], - [ - "LonSoilC2", - "-83.5014028" - ], - [ - "LonSoilC3", - "-83.5014028" - ], - [ - "LonSoilC4", - "-83.5013472" - ], - [ - "Orientation Number", - "2" - ], - [ - "RefeSoil", - "C1" - ], - [ - "Required Asset Management Location Code", - "SOILPL105351" - ], - [ - "State province", - "TN" - ], - [ - "UTM Zone", - "17N" - ], - [ - "x Azimuth Angle", - 3 - ], - [ - "y Azimuth Angle", - 270 - ] - ], - "reference_location":null, - "start_date":"2025-10-18T00:00:00Z", - "x_offset": 0, - "y_offset": 0, - "z_offset": 0 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - }, - "name":"SOILPL105351" - }, - "type":"Feature" - }, - "start_date":"2025-10-18T00:00:00Z", - "x_offset": 1.285, - "y_offset": 2.975, - "z_offset": -0.365 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - }, - "name":"CFGLOC105371", - "remove_date":"2025-10-19T00:00:00Z", - "site":"GRSM", - "site_location":{ - "features":[ - { - "geometry":{ - "coordinates":[ - -83.50195, - 35.68896, - 575.396725 - ], - "type":"Point" - }, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-19T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "AERONET_XREF", - "NEON_GRSM" - ], - [ - "AngAzWindPrim", - "140-320" - ], - [ - "AngAzWindScnd", - "NA" - ], - [ - "AngAzWindTrty", - "NA" - ], - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "DEIMS-SDR Site ID", - "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" - ], - [ - "DistZaxsCnpy", - "31" - ], - [ - "DistZaxsDisp", - "20.2" - ], - [ - "Dom", - "D07" - ], - [ - "DURATION", - "Gradient" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "HABITAT", - "Terrestrial" - ], - [ - "IS Processing Default Start Date", - "2017-08-20T00:00:00Z" - ], - [ - "NEONSCI_FIELD_SITE_URL_PATH", - "great-smoky-mountains-national-park-twin-creeks" - ], - [ - "Pf$AngEnuXaxs", - "NA" - ], - [ - "Pf$AngEnuYaxs", - "NA" - ], - [ - "Pf$Ofst", - "NA" - ], - [ - "PrdDcmpMax", - "3" - ], - [ - "PrdIncrAgrDcmp", - "1" - ], - [ - "PrdIncrCalc", - "1" - ], - [ - "PrdIncrPf", - "1" - ], - [ - "PrdWndwAgrDcmp", - "5" - ], - [ - "PrdWndwCalc", - "3" - ], - [ - "PrdWndwPf", - "9" - ], - [ - "PresAtmSite", - "93630" - ], - [ - "Private", - "FALSE" - ], - [ - "Read", - "HDF5" - ], - [ - "Required Asset Management Location Code", - "GRSM" - ], - [ - "Site Timezone", - "US/Eastern" - ], - [ - "State Abbreviation", - "TN" - ], - [ - "State province", - "TN" - ], - [ - "TypeEco", - "Hardwood deciduous forest dominated by oaks and maples" - ], - [ - "TypeSoil", - "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" - ], - [ - "UTM Zone", - "17N" - ], - [ - "ZoneTime", - "EST" - ] - ], - "reference_location":null, - "start_date":"2025-10-18T00:00:00Z", - "x_offset": 0, - "y_offset": 0, - "z_offset": 0 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - } - }, - "Required Asset Management Location Code":"CFGLOC105371", - "subLocation":"depth5", - "subSystem":"temp-profile", - "system":"soil", - "type":"Feature", - "VER":"505" - } - ], - "source_id": 23800, - "source_type":"prt", - "type":"FeatureCollection" -} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_1_minute_001.parquet deleted file mode 100644 index 6ea13df5fe7054c0960361145f8cff5694100ae3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18427 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 SjN|w7j~UsWVbbv5{`?;Wwz_Tr diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/data/temp-soil_GRSM005506_2025-10-18_ST_30_minute_030.parquet deleted file mode 100644 index 0c49543b9df78868d0068bfde4d052fce8322643..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11808 zcmeHNdu&_P8NW`G)NRr>Exn%8Wo>x$5=h!OPLn2CS$(cwc{r})CVq*H*p6enu^l&Y z;x=MahS-J}6oe3e0Ao?bN>wq4rm4#xtcyxTHIO!#v}t3giq?ujkx*cWMW_q=&bhDS zYv(~zsF1j-uYJDrec$hUo$s7`Qx|eKu*K|TDf?NmgY9RTZ45KHu8yf*|4FT0}}y|(xL znG$Uc*LX(^>sh$}F<^N8waubZrvm-~9L~7@Ry%l!8I#r%7P8z4><_>33L$f%$6qWf;8j z>m6yBKk?t6zA&T;vG>yGZ;$oGAxlK1}m zhy67t2I9}f&b*ZP;9P#*3^-pTZzN;dZ!%UOV_L{qiMB<|7c+V3S4-qgoPoTFQ6+iH zqh3$m`dOcYEhnqZPz4IAO~b7wjkfrW51_D2$De6~T4ScKmS_!=&s=RY0S88u}%lOkyugF0v!EUUu!YJ_vFZd zR*hCW<^Kc>=>N-kV!-Ulm)lGQ4ImuE0HQ$Wf$9cjSWck=Ws{*8?(25B7%;)cs0_ei znEU4<4ukgXYi$~B`Rp_QgD*q%eTzZ_Ui^Fs0~kC*pojv|02Or!16;Wgcs=mKLEtpK z^@sCd0aMz13M`me==g&5~}?pfEX*q7Oft_u)p)7^t56$a)}o)G)@ z#9l4zAZ8aFB)p=7Dk?Exxg28yOk2Aj#|BL4n|}cY%pQLS45)6p^AQr^p3fgIYyf5# z3?RIs0V?Ve23VvB*Fab`b(5D{I1{$yM7R&KJ6ZL!K0JiWU%0@@49PBLnv_-;=2xgN zQz-fl3cEy8?Kfc!ms>ngU8WP4>j?hET1+KZUbgX4f{RA_0{9i|b_ZKTR=5@~FmeIL zHdd$ZB6v#iM!}$|z_OuQ=pveaynt_lev+et_A4>+n-u$L>Q`>RiEVbUS`zRSvodKS_fD-C*_&1?8NVhCl8*~*buFzC)WyRY5TGif7 zi2pb7fhy|PEZG~sR`FrjA_sIP{6q+Mb~C$8=}^u6KwPgUod;%%VgAcv{-%r})XF{p zgWNXdVsGB@<}1k!6c?1BaD35#`%r5-c85}n`eIB~4#dg`$YnLc?x*H;rb3BNP@l-rHmQ+)N#}sOX#On!;#Dl4D1Qs2r*esEqV@X$LRM4`m@34xZ=Vh&0mHI_)^2w*~v0468`8=}D+06eDI*TY!hE*nk9 zhjRQ8D73@|!lTG+4G*WpNF1jvqv^E#7K&fdvUxn1h!2>9InK#MTE1N3RD{ER?UT9U;2`dgS*$NgfL>h)(Dip{Q<=g^!ZI<_J?$=~mIFRlO2f-lx2NBSAe_#9i6WE!-kNsorud@$REARFRk^RSzRTGaB8j zhP$BSuCY~m>NN+H0X147TKRoYaso1QO3%IWEx`%Zn~WviZxnVyDm58P?yVHo=^fm; zN&!pt(qf5M8OvkQ{aQj<&~Q^#-0Zi-3Xq@QaEUMV(qc6zLg$fCCX2YKo!rcKZXAlT zuMi9&@9*v6W_HOK^a*H*$G7D@q4y-ALsM9%Z{-%MzZ@(ocA{UmaWl8cSoF&Y#V?w= zLVc_ERNAXMxP{wgq%}v7+NTM~qDBV=_b|uJ>=r;&uO>uyRMZvRq4s_W1nTYK8gBYd z*^^bgl~}$OAPfzwFgT%qfj7IwcPyOHy9+Zz~<_>={gyNE~V=#Fs*c! zqW##*&F_&R)*LM5d`4l+)pOJJHx?r=R8tMyyg|lTbEu?qB<+Qfn>NY_YmO9sHlwz+UE>mIb~ z`Z=xcMay>h3r3Z`9J$?xylui;5&f*L9$&_22STU^KKSZFD2wp?H->A*~alUPu^EcmBAkPannqkVmW zUcWJMcpwEnL$GSc>+1>IVSKmjt96kvk79%hj+3~N0o-tmP#~EY^LvlE-BG?LR~`-5 z4vO-}h=^JqVha8yJqEwW<^+G@BEK2gdYkI&TVU2|A+F)TOJ3q%I7f(&h1&UHBHO)R zpIgvTt}Z$=IkK4b$fA0D@(}d8EuhQp_a-1G2FTAyv*3RRYIlkH zdY149=i2TZ52c*xaHBgBN;;bapUe^2ADczgC&trE99H~+(gJ^ugp5rSL7&qQ@HQQZ zcod(IGbGy+SJ~5vouKN=O6n`u9(U4~4%u*yp{mBA$gs^(3li<)kx(C`2&woG)!{}y z9W;6l^^h8YrKHGbSCDsLJW;;K?{iMz*oI_zsCQ@`z_JuREidOLCCfv-=gO<=TUZ}P zTUN-2rAy>DqxOmYx%G_$Lf02w+I-6I<7K-ca?P@Q*Q(`ZyFua6dSN}Ak#i+^T&n!o zfcF@+KKP(4k0x4{w|`ic@3Y$`!VP47cggaW)ypfjV{Vq^?JLO>`zHqM9x?V{4zeiz zcdjH)>?iVy{U}&3^fNQ+UcJ0xKi?(GyHxUd{RQ_vJ{$0L%Ih)E*Tm!c9UN~I*AL2y z`lR|L`vJ9n+}P`|-E%x_OkiKe{6-H|PiCWRkNrq%PCg6w2bDd6SP=R?>79r6i}D?4 z*d^L0_A9aosQy4yw|B-u$$^C2d-?rP+Sum~WLQZaFBMT<&W%-(M=n@em}e<`N?y)Q zyCUDMk}ufb+MVOzdtboYknL-954nAv&Vbi82B;`AF5*Y|;d*FwKw3UO##ZQ!(6@cO zEz-_=pnoUADcnRYW1_qnby>vtSk0tq3x^t|eQLLS}_24A7H z;J^`uY7&nE*hpc2kFH=J*(Ab>eO|w>Gwp}HAwXvMBtCk9HZyD;@Qd273)eGcUqdWn zPhn935j?TY2fB4{qsQmSM zJ+TlV!4U%vUVi*7Tv78r$sV(C{!#BwWPgCYa>#AB83G=6I^^v`X$e0n^m7Z=XSHQu zEvNlP>$n@%;Xu-J9H>1A4vY{gJ3h}4yI}FkN$nXXQHH}TVI^O1Bj>kvrvY+dpmAXe z>?h}5_?DdLz~@}JNPX}jujhEs>x?-JP;aR~A`EPdzB(NoIXV`}> Date: Fri, 20 Feb 2026 13:50:41 -0700 Subject: [PATCH 25/46] remove more test data --- .../location/CFGLOC105373.json | 39 -- .../location/prt_16828_locations.json | 409 ------------------ 2 files changed, 448 deletions(-) delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/CFGLOC105373.json delete mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/prt_16828_locations.json diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/CFGLOC105373.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/CFGLOC105373.json deleted file mode 100644 index 4ea70550c..000000000 --- a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/CFGLOC105373.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "features":[ - { - "AngNedZaxs":"NA", - "AngPtch":"0", - "Cal$PrdLab":"one time before installation", - "Data Rate":"0.1", - "DistZaxsLvlMeasSoil":"-0.465", - "FreqSamp":"0.1", - "HOR":"005", - "IS Processing Default Start Date":"2017-08-20T00:00:00Z", - "Required Asset Management Location Code":"CFGLOC105373", - "VER":"506", - "geometry":null, - "location":"plot5", - "properties":{ - "active_periods":[ - { - "start_date":"2025-10-18T00:00:00Z", - "end_date":"2025-10-19T00:00:00Z" - } - ], - "context":[ - - ], - "description":"Great Smoky Mountains Soil Temp Profile SP5, Z6 Depth", - "domain":"D07", - "name":"CFGLOC105373", - "site":"GRSM", - "type":"CONFIG" - }, - "subLocation":"depth6", - "subSystem":"temp-profile", - "system":"soil", - "type":"Feature" - } - ], - "type":"FeatureCollection" -} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/prt_16828_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/prt_16828_locations.json deleted file mode 100644 index 89b966df0..000000000 --- a/flow/tests/testthat/pfs/envscn_temp_flags/temp/tests/multiple_depth/2025/10/18/conc-h2o-soil-salinity_GRSM005501/temp-soil_GRSM005506/location/prt_16828_locations.json +++ /dev/null @@ -1,409 +0,0 @@ -{ - "features":[ - { - "AngNedZaxs":"NA", - "AngPtch":"0", - "asset_manufacturer":"Thermometrics", - "asset_model":"R000-00000011", - "asset_software_version":null, - "Cal$PrdLab":"one time before installation", - "Data Rate":"0.1", - "DistZaxsLvlMeasSoil":"-0.465", - "FreqSamp":"0.1", - "geometry":null, - "HOR":"005", - "location":"plot5", - "properties":{ - "context":[ - - ], - "domain":"D07", - "install_date":"2025-10-18T00:00:00Z", - "locations":{ - "features":[ - { - "geometry":null, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-19T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "State province", - "TN" - ], - [ - "UTM Zone", - "17N" - ] - ], - "reference_location":{ - "geometry":null, - "properties":{ - "locations":{ - "features":[ - { - "geometry":{ - "coordinates":[ - [ - -83.50135, - 35.6892, - 576.39 - ], - [ - -83.5014028, - 35.6892, - 575.2 - ], - [ - -83.5014028, - 35.6892444, - 575.2 - ], - [ - -83.5013472, - 35.6892444, - 576.39 - ], - [ - -83.50135, - 35.6892, - 576.39 - ] - ], - "type":"Polygon" - }, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-19T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "AngAzXaxsRefeSoil", - "C4" - ], - [ - "AngAzYaxsRefeSoil", - "C2" - ], - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "ElevSoilC1", - "576.39" - ], - [ - "ElevSoilC2", - "575.20" - ], - [ - "ElevSoilC3", - "575.20" - ], - [ - "ElevSoilC4", - "576.39" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "LatSoilC1", - "35.6892000" - ], - [ - "LatSoilC2", - "35.6892000" - ], - [ - "LatSoilC3", - "35.6892444" - ], - [ - "LatSoilC4", - "35.6892444" - ], - [ - "locDisturb", - "1.32,2.3,NA and 0.160,3.175,NA and 0.160,2.725,NA and 1.720,3.175,NA and 1.72,2.725,NA" - ], - [ - "LonSoilC1", - "-83.5013500" - ], - [ - "LonSoilC2", - "-83.5014028" - ], - [ - "LonSoilC3", - "-83.5014028" - ], - [ - "LonSoilC4", - "-83.5013472" - ], - [ - "Orientation Number", - "2" - ], - [ - "RefeSoil", - "C1" - ], - [ - "Required Asset Management Location Code", - "SOILPL105351" - ], - [ - "State province", - "TN" - ], - [ - "UTM Zone", - "17N" - ], - [ - "x Azimuth Angle", - 3 - ], - [ - "y Azimuth Angle", - 270 - ] - ], - "reference_location":null, - "start_date":"2025-10-18T00:00:00Z", - "x_offset": 0, - "y_offset": 0, - "z_offset": 0 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - }, - "name":"SOILPL105351" - }, - "type":"Feature" - }, - "start_date":"2025-10-18T00:00:00Z", - "x_offset": 1.285, - "y_offset": 2.975, - "z_offset": -0.465 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - }, - "name":"CFGLOC105373", - "remove_date":"2025-10-19T00:00:00Z", - "site":"GRSM", - "site_location":{ - "features":[ - { - "geometry":{ - "coordinates":[ - -83.50195, - 35.68896, - 575.396725 - ], - "type":"Point" - }, - "properties":{ - "alpha": 0, - "beta": 0, - "end_date":"2025-10-19T00:00:00Z", - "gamma": 0, - "location_properties":[ - [ - "AERONET_XREF", - "NEON_GRSM" - ], - [ - "AngAzWindPrim", - "140-320" - ], - [ - "AngAzWindScnd", - "NA" - ], - [ - "AngAzWindTrty", - "NA" - ], - [ - "Country", - "USA" - ], - [ - "County", - "Sevier" - ], - [ - "DEIMS-SDR Site ID", - "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" - ], - [ - "DistZaxsCnpy", - "31" - ], - [ - "DistZaxsDisp", - "20.2" - ], - [ - "Dom", - "D07" - ], - [ - "DURATION", - "Gradient" - ], - [ - "Geodetic datum", - "WGS84" - ], - [ - "HABITAT", - "Terrestrial" - ], - [ - "IS Processing Default Start Date", - "2017-08-20T00:00:00Z" - ], - [ - "NEONSCI_FIELD_SITE_URL_PATH", - "great-smoky-mountains-national-park-twin-creeks" - ], - [ - "Pf$AngEnuXaxs", - "NA" - ], - [ - "Pf$AngEnuYaxs", - "NA" - ], - [ - "Pf$Ofst", - "NA" - ], - [ - "PrdDcmpMax", - "3" - ], - [ - "PrdIncrAgrDcmp", - "1" - ], - [ - "PrdIncrCalc", - "1" - ], - [ - "PrdIncrPf", - "1" - ], - [ - "PrdWndwAgrDcmp", - "5" - ], - [ - "PrdWndwCalc", - "3" - ], - [ - "PrdWndwPf", - "9" - ], - [ - "PresAtmSite", - "93630" - ], - [ - "Private", - "FALSE" - ], - [ - "Read", - "HDF5" - ], - [ - "Required Asset Management Location Code", - "GRSM" - ], - [ - "Site Timezone", - "US/Eastern" - ], - [ - "State Abbreviation", - "TN" - ], - [ - "State province", - "TN" - ], - [ - "TypeEco", - "Hardwood deciduous forest dominated by oaks and maples" - ], - [ - "TypeSoil", - "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" - ], - [ - "UTM Zone", - "17N" - ], - [ - "ZoneTime", - "EST" - ] - ], - "reference_location":null, - "start_date":"2025-10-18T00:00:00Z", - "x_offset": 0, - "y_offset": 0, - "z_offset": 0 - }, - "type":"Feature" - } - ], - "type":"FeatureCollection" - } - }, - "Required Asset Management Location Code":"CFGLOC105373", - "subLocation":"depth6", - "subSystem":"temp-profile", - "system":"soil", - "type":"Feature", - "VER":"506" - } - ], - "source_id": 16828, - "source_type":"prt", - "type":"FeatureCollection" -} From 7cf97bfca4d5ac3af0f4bd1223c017e782728a0b Mon Sep 17 00:00:00 2001 From: burlingamet Date: Fri, 20 Feb 2026 14:35:45 -0700 Subject: [PATCH 26/46] updating unit tests to work with multiple contexts. --- .../tests/test_threshold_loader.py | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/modules/threshold_loader/tests/test_threshold_loader.py b/modules/threshold_loader/tests/test_threshold_loader.py index b681e9ecd..904f74fdd 100644 --- a/modules/threshold_loader/tests/test_threshold_loader.py +++ b/modules/threshold_loader/tests/test_threshold_loader.py @@ -38,7 +38,7 @@ def get_thresholds(term) -> Iterator[Threshold]: number_value=10, string_value='value') - load_thresholds(get_thresholds, self.out_path, 'term_name', 'context1|context2') + load_thresholds(get_thresholds, self.out_path, 'term_name', ['context1|context2']) expected_path = self.out_path.joinpath('thresholds.json') self.assertTrue(expected_path.exists()) with open(expected_path, 'r') as threshold_file: @@ -69,6 +69,90 @@ def get_thresholds(term) -> Iterator[Threshold]: self.assertTrue(string_value == 'value') print(json.dumps(json_data, indent=2, sort_keys=False)) + def test_multiple_contexts(self): + + def get_thresholds(term) -> Iterator[Threshold]: + """ + Mock function yielding thresholds with different contexts. + """ + # Threshold with context1|context2 + yield Threshold(threshold_name='threshold_for_context1_2', + term_name=term, + location_name='CPER', + context=['context1', 'context2'], + start_date='2024-01-01', + end_date='2024-12-31', + is_date_constrained=True, + start_day_of_year=1, + end_day_of_year=365, + number_value=10, + string_value='value1') + + # Threshold with context3 only + yield Threshold(threshold_name='threshold_for_context3', + term_name=term, + location_name='CPER', + context=['context3'], + start_date='2024-01-01', + end_date='2024-12-31', + is_date_constrained=True, + start_day_of_year=1, + end_day_of_year=365, + number_value=20, + string_value='value2') + + # Threshold with no context + yield Threshold(threshold_name='threshold_no_context', + term_name=term, + location_name='CPER', + context=[], + start_date='2024-01-01', + end_date='2024-12-31', + is_date_constrained=True, + start_day_of_year=1, + end_day_of_year=365, + number_value=30, + string_value='value3') + + # Threshold with different context that shouldn't match + yield Threshold(threshold_name='threshold_for_other_context', + term_name=term, + location_name='CPER', + context=['otherContext'], + start_date='2024-01-01', + end_date='2024-12-31', + is_date_constrained=True, + start_day_of_year=1, + end_day_of_year=365, + number_value=40, + string_value='value4') + + # Test with multiple context sets + load_thresholds(get_thresholds, self.out_path, 'term_name', ['context1|context2', 'context3']) + expected_path = self.out_path.joinpath('thresholds.json') + self.assertTrue(expected_path.exists()) + + with open(expected_path, 'r') as threshold_file: + json_data = json.load(threshold_file) + thresholds = json_data['thresholds'] + + # Should have 2 thresholds: one matching context1|context2, one matching context3 + self.assertEqual(len(thresholds), 2) + + # Get threshold names + threshold_names = [t['threshold_name'] for t in thresholds] + + # Should include both matching thresholds + self.assertIn('threshold_for_context1_2', threshold_names) + self.assertIn('threshold_for_context3', threshold_names) + + # Should NOT include non-matching thresholds + self.assertNotIn('threshold_no_context', threshold_names) + self.assertNotIn('threshold_for_other_context', threshold_names) + + print("\nMultiple contexts test results:") + print(json.dumps(json_data, indent=2, sort_keys=False)) + @unittest.skip('Integration test skipped due to long process time.') def test_main(self): self.configure_mount() From 183d60805837663901baea671def3b12a044370d Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 23 Feb 2026 11:53:14 -0700 Subject: [PATCH 27/46] renv lock update --- flow/flow.envscn.temp.flags/renv.lock | 132 ++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/flow/flow.envscn.temp.flags/renv.lock b/flow/flow.envscn.temp.flags/renv.lock index 24ea1ef1f..c1bd54e93 100644 --- a/flow/flow.envscn.temp.flags/renv.lock +++ b/flow/flow.envscn.temp.flags/renv.lock @@ -9,6 +9,24 @@ ] }, "Packages": { + "R6": { + "Package": "R6", + "Version": "2.6.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "d4335fe7207f1c01ab8c41762f5840d4", + "Requirements": [] + }, + "askpass": { + "Package": "askpass", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "c39f4155b3ceb1a9a2799d700fbd4b6a", + "Requirements": [ + "sys" + ] + }, "codetools": { "Package": "codetools", "Version": "0.2-18", @@ -17,6 +35,38 @@ "Hash": "019388fc48e48b3da0d3a76ff94608a8", "Requirements": [] }, + "covr": { + "Package": "covr", + "Version": "3.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "6d80a9fc3c0c8473153b54fa54719dfd", + "Requirements": [ + "crayon", + "digest", + "httr", + "jsonlite", + "rex", + "withr", + "yaml" + ] + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "859d96e65ef198fd43e82b9628d593ef", + "Requirements": [] + }, + "curl": { + "Package": "curl", + "Version": "5.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "9123f3ef96a2c1a93927d828b2fe7d4c", + "Requirements": [] + }, "data.table": { "Package": "data.table", "Version": "1.18.0", @@ -25,6 +75,14 @@ "Hash": "498817481016697c065f66fe14f3bcce", "Requirements": [] }, + "digest": { + "Package": "digest", + "Version": "0.6.29", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "cf6b206a045a684728c3267ef7596190", + "Requirements": [] + }, "doParallel": { "Package": "doParallel", "Version": "1.0.17", @@ -55,6 +113,20 @@ "Hash": "7eb1e342eee7e0a7449c49cdaa526d39", "Requirements": [] }, + "httr": { + "Package": "httr", + "Version": "1.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "ac107251d9d9fd72f0ca8049988f1d7f", + "Requirements": [ + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ] + }, "iterators": { "Package": "iterators", "Version": "1.0.14", @@ -71,6 +143,32 @@ "Hash": "b0776f526d36d8bd4a3344a88fe165c4", "Requirements": [] }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "d908914ae53b04d4c0c0fd72ecc35370", + "Requirements": [] + }, + "mime": { + "Package": "mime", + "Version": "0.13", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "0ec19f34c72fab674d8f2b4b1c6410e1", + "Requirements": [] + }, + "openssl": { + "Package": "openssl", + "Version": "2.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "bc54d87ebf858b28de18df4bca6528d3", + "Requirements": [ + "askpass" + ] + }, "renv": { "Package": "renv", "Version": "0.16.0", @@ -83,6 +181,40 @@ "RemoteSha": "0e3aab27a928eb261819a3fc45a3ee2b4ba902a5", "Hash": "9e5e2246d73254a29a4182f4e8257c09", "Requirements": [] + }, + "rex": { + "Package": "rex", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "ae34cd56890607370665bee5bd17812f", + "Requirements": [ + "lazyeval" + ] + }, + "sys": { + "Package": "sys", + "Version": "3.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "de342ebfebdbf40477d0758d05426646", + "Requirements": [] + }, + "withr": { + "Package": "withr", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "cc2d62c76458d425210d1eb1478b30b4", + "Requirements": [] + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.12", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "7cd77cb32abd9220d744307e9fc94ffb", + "Requirements": [] } } } From ef4922638eeba11836a29b28643f511d36635f13 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 23 Feb 2026 12:54:33 -0700 Subject: [PATCH 28/46] typo in flow script. --- flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R index 808ba1c5d..d5130309d 100644 --- a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R @@ -96,7 +96,7 @@ library(doParallel) source("./wrap.envscn.temp.flags.R") source("./def.apply.temp.flags.R") source("./def.find.temp.sensor.R") -source("./def.load.temp.sensor.R") +source("./def.load.temp.sensors.R") source("./def.sort.qf.cols.R") source("./def.calc.temp.flags.R") From f1f30efd36e0275d0958a811d8646a270f0d217c Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 23 Feb 2026 14:33:23 -0700 Subject: [PATCH 29/46] testing new temp functions --- .../build_push_analyze_pad_qaqc_envscn.yml | 2 +- ...oSoilSalinity_analyze_pad_qaqc_envscn.yaml | 114 ++++++++++++++++++ ...cH2oSoilSalinity_thresh_select_ts_pad.yaml | 2 +- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml diff --git a/.github/workflows/build_push_analyze_pad_qaqc_envscn.yml b/.github/workflows/build_push_analyze_pad_qaqc_envscn.yml index 5f8535ffc..6ba29cde1 100644 --- a/.github/workflows/build_push_analyze_pad_qaqc_envscn.yml +++ b/.github/workflows/build_push_analyze_pad_qaqc_envscn.yml @@ -3,7 +3,7 @@ name: "Build-push_analyze_pad_qaqc_envscn" on: push: branches: - - 'master' + - 'swc.dev' paths: - 'modules_combined/qaqc_plau_and_radiation_custom/**' - 'modules/padded_timeseries_analyzer/**' diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml new file mode 100644 index 000000000..a6792fd04 --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml @@ -0,0 +1,114 @@ +--- +pipeline: + name: concH2oSoilSalinity_analyze_pad_qaqc_envscn +transform: + image_pull_secrets: + - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-ae444b5 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + + # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ + set -euo pipefail + IFS=$'\n\t' + + # Refresh interim directories with each datum (otherwise they persist and cause probs) + rm -rf /tmp/pfs/padded_analyzer + rm -rf /tmp/pfs/padded_analyzerCopy + mkdir -p /tmp/pfs/padded_analyzer + + # Run first module - padded_timeseries_analyzer + python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main + + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/padded_analyzer + + # Run second module - qaqc plausibility + + Rscript ./flow.qaqc.plau.R \ + DirIn=/tmp/pfs/padded_analyzerCopy \ + DirOut=/tmp/pfs/qaqc_plau \ + DirErr=/pfs/out/errored_datums \ + "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "DirSubCopy=data|location|threshold" + + ###custom script will go here for temperature test + + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/pfs/qaqc_plau /tmp/pfs/qaqc_plauCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/qaqc_plau + + Rscript ./flow.envscn.temp.flags.R \ + DirIn=/tmp/pfs/qaqc_plauCopy \ + DirOut=/pfs/out \ + DirErr=/pfs/out/errored_datums \ + DirTemp=$TEMP_PATH \ + "DirSubCopy=data|location|flags" + + EOF + env: + # Environment variables for padded timeseries analyzer + OUT_PATH: /tmp/pfs/padded_analyzer + LOG_LEVEL: DEBUG + RELATIVE_PATH_INDEX: '3' + ERR_PATH: /pfs/out/errored_datums + # Environment variables for qaqc plausibility + PARALLELIZATION_INTERNAL: '5' +input: + cross: + - pfs: + name: DATA_PATH + repo: concH2oSoilSalinity_thresh_select_ts_pad + glob: /*/*/* + - pfs: + name: TEMP_PATH + repo: concH2oSoilSalinity_group_path + glob: /*/*/*/* + # - pfs: + # name: SCHEMA_FLAGS + # repo: concH2oSoilSalinity_avro_schemas + # glob: /concH2oSoilSalinity/flags_plausibility_concH2oSoilSalinity.avsc +parallelism_spec: + constant: 5 +autoscaling: true +resource_requests: + memory: 2G + cpu: 5.5 +resource_limits: + memory: 4G + cpu: 7 +sidecar_resource_requests: + memory: 3G + cpu: 0.3 +datum_set_spec: + number: 1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml index 5216bc071..38742856d 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_thresh_select_ts_pad.yaml @@ -69,7 +69,7 @@ input: glob: /*/*/*/*/enviroscan - pfs: name: FILE_THRESHOLDS - repo: concH2oSoilSalinity_threshold_test3 + repo: concH2oSoilSalinity_threshold glob: /thresholds.json parallelism_spec: constant: 5 From 695ca48a18ed1d513e044f08ec8d48db7c975f0e Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 23 Feb 2026 15:56:37 -0700 Subject: [PATCH 30/46] attempted pipeline specs for custom envsc script --- ...oSoilSalinity_analyze_pad_qaqc_envscn.yaml | 8 +- ...ity_analyze_pad_qaqc_envscn_base_glob.yaml | 114 ++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml index a6792fd04..1e7adddde 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml @@ -82,15 +82,19 @@ transform: # Environment variables for qaqc plausibility PARALLELIZATION_INTERNAL: '5' input: - cross: + join: - pfs: name: DATA_PATH repo: concH2oSoilSalinity_thresh_select_ts_pad - glob: /*/*/* + glob: /*/*/*/*/enviroscan + join_on: $1/$2/$3/$4 - pfs: name: TEMP_PATH repo: concH2oSoilSalinity_group_path glob: /*/*/*/* + join_on: $1/$2/$3/$4 + group_by: $1/$2/$3/$4 + empty_files: false # - pfs: # name: SCHEMA_FLAGS # repo: concH2oSoilSalinity_avro_schemas diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml new file mode 100644 index 000000000..5d83ccd74 --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml @@ -0,0 +1,114 @@ +--- +pipeline: + name: concH2oSoilSalinity_analyze_pad_qaqc_envscn_v2 +transform: + image_pull_secrets: + - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-ae444b5 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + + # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ + set -euo pipefail + IFS=$'\n\t' + + # Refresh interim directories with each datum (otherwise they persist and cause probs) + rm -rf /tmp/pfs/padded_analyzer + rm -rf /tmp/pfs/padded_analyzerCopy + mkdir -p /tmp/pfs/padded_analyzer + + # Run first module - padded_timeseries_analyzer + python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main + + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/padded_analyzer + + # Run second module - qaqc plausibility + + Rscript ./flow.qaqc.plau.R \ + DirIn=/tmp/pfs/padded_analyzerCopy \ + DirOut=/tmp/pfs/qaqc_plau \ + DirErr=/pfs/out/errored_datums \ + "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "DirSubCopy=data|location|threshold" + + ###custom script will go here for temperature test + + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/pfs/qaqc_plau /tmp/pfs/qaqc_plauCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/qaqc_plau + + Rscript ./flow.envscn.temp.flags.R \ + DirIn=/tmp/pfs/qaqc_plauCopy \ + DirOut=/pfs/out \ + DirErr=/pfs/out/errored_datums \ + DirTemp=$TEMP_PATH \ + "DirSubCopy=data|location|flags" + + EOF + env: + # Environment variables for padded timeseries analyzer + OUT_PATH: /tmp/pfs/padded_analyzer + LOG_LEVEL: DEBUG + RELATIVE_PATH_INDEX: '3' + ERR_PATH: /pfs/out/errored_datums + # Environment variables for qaqc plausibility + PARALLELIZATION_INTERNAL: '5' +input: + join: + - pfs: + name: DATA_PATH + repo: concH2oSoilSalinity_thresh_select_ts_pad + glob: /*/*/*/ + - pfs: + name: TEMP_PATH + repo: concH2oSoilSalinity_group_path + glob: /*/*/*/ + # - pfs: + # name: SCHEMA_FLAGS + # repo: concH2oSoilSalinity_avro_schemas + # glob: /concH2oSoilSalinity/flags_plausibility_concH2oSoilSalinity.avsc +parallelism_spec: + constant: 5 +autoscaling: true +resource_requests: + memory: 2G + cpu: 5.5 +resource_limits: + memory: 4G + cpu: 7 +sidecar_resource_requests: + memory: 3G + cpu: 0.3 +datum_set_spec: + number: 1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class From b61d89eba7bf6c04bce96d01f4b9961f1508d7b7 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Mon, 23 Feb 2026 17:38:06 -0700 Subject: [PATCH 31/46] still erroring, but at least right number of datums... --- .../concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml index 1e7adddde..2ff7243ac 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml @@ -86,14 +86,14 @@ input: - pfs: name: DATA_PATH repo: concH2oSoilSalinity_thresh_select_ts_pad - glob: /*/*/*/*/enviroscan - join_on: $1/$2/$3/$4 + glob: /(*)/(*)/(*)/(*) + joinOn: $1/$2/$3/$4 - pfs: name: TEMP_PATH repo: concH2oSoilSalinity_group_path - glob: /*/*/*/* - join_on: $1/$2/$3/$4 - group_by: $1/$2/$3/$4 + glob: /(*)/(*)/(*)/(*) + joinOn: $1/$2/$3/$4 + groupBy: $1/$2/$3/$4 empty_files: false # - pfs: # name: SCHEMA_FLAGS From 22448557de4582f13a9b5fc1407269fcea75b6c0 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 24 Feb 2026 09:19:50 -0700 Subject: [PATCH 32/46] logging messages for debugging. --- flow/flow.envscn.temp.flags/def.load.temp.sensors.R | 1 + 1 file changed, 1 insertion(+) diff --git a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R index 228210b79..8365fef16 100644 --- a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R +++ b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R @@ -135,6 +135,7 @@ def.load.temp.sensors <- function(DirTemp, } log$info(base::paste0('Loaded ', base::nrow(sensorDepthDf), ' temperature sensors')) + log$info(base::paste0('Temp sensors found: ', sensorDepthDf$sensor_id)) return(sensorDepthDf) } From c054a9d2284ea06752aa1b3148a5c761efa42d0e Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 24 Feb 2026 16:47:06 -0700 Subject: [PATCH 33/46] add filter joiner to envscn module --- .../analyze_pad_qaqc_envscn/Dockerfile | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/modules_combined/analyze_pad_qaqc_envscn/Dockerfile b/modules_combined/analyze_pad_qaqc_envscn/Dockerfile index d3b0596da..1759d6354 100644 --- a/modules_combined/analyze_pad_qaqc_envscn/Dockerfile +++ b/modules_combined/analyze_pad_qaqc_envscn/Dockerfile @@ -31,6 +31,35 @@ RUN apt update && \ COPY ${MODULE_DIR}/${APP_DIR} ${CONTAINER_APP_DIR}/${APP_DIR} COPY ${MODULE_DIR}/${COMMON_DIR} ${CONTAINER_APP_DIR}/${COMMON_DIR} + +# Add in the python-based filter-joiner module +ARG MODULE_DIR="modules" +ARG APP_DIR="filter_joiner" +ARG COMMON_DIR="common" +ARG CONTAINER_APP_DIR="/usr/src/app" +ENV PYTHONPATH="${PYTHONPATH}:${CONTAINER_APP_DIR}" + +WORKDIR ${CONTAINER_APP_DIR} + +COPY ${MODULE_DIR}/${APP_DIR}/requirements.txt ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt + + +RUN apt update && \ + apt-get install -y --no-install-recommends \ + python3.8 && \ + apt install -y python3-pip && \ + python3 -mpip install --no-cache-dir --upgrade pip setuptools wheel && \ + python3 -mpip install --no-cache-dir -r ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt && \ + apt-get autoremove -y && \ + apt-get autoclean -y && \ + rm -rf /var/lib/apt/lists/* && \ + groupadd -g 9999 appuser && \ + useradd -r -u 9999 -g appuser appuser + +# Copy in python code +COPY ${MODULE_DIR}/${APP_DIR} ${CONTAINER_APP_DIR}/${APP_DIR} +COPY ${MODULE_DIR}/${COMMON_DIR} ${CONTAINER_APP_DIR}/${COMMON_DIR} + # Build in the threshold selection module ARG MODULE_DIR="flow" ARG APP_DIR="flow.qaqc.plau" From 0736d5130ee413f779add425bb13b9d82c5eb036 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Tue, 24 Feb 2026 17:12:59 -0700 Subject: [PATCH 34/46] dockerfile update --- .../analyze_pad_qaqc_envscn/Dockerfile | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/modules_combined/analyze_pad_qaqc_envscn/Dockerfile b/modules_combined/analyze_pad_qaqc_envscn/Dockerfile index 1759d6354..2e0f410a5 100644 --- a/modules_combined/analyze_pad_qaqc_envscn/Dockerfile +++ b/modules_combined/analyze_pad_qaqc_envscn/Dockerfile @@ -7,14 +7,16 @@ FROM us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-pack-qaqc-r:v1.1.10 ARG MODULE_DIR="modules" -ARG APP_DIR="padded_timeseries_analyzer" +ARG APP_DIR_1="padded_timeseries_analyzer" +ARG APP_DIR_2="filter_joiner" ARG COMMON_DIR="common" ARG CONTAINER_APP_DIR="/usr/src/app" ENV PYTHONPATH="${PYTHONPATH}:${CONTAINER_APP_DIR}" WORKDIR ${CONTAINER_APP_DIR} -COPY ${MODULE_DIR}/${APP_DIR}/requirements.txt ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt +COPY ${MODULE_DIR}/${APP_DIR_1}/requirements.txt ${CONTAINER_APP_DIR}/${APP_DIR_1}/requirements.txt +COPY ${MODULE_DIR}/${APP_DIR_2}/requirements.txt ${CONTAINER_APP_DIR}/${APP_DIR_2}/requirements.txt RUN apt update && \ @@ -22,42 +24,15 @@ RUN apt update && \ python3.8 && \ apt install -y python3-pip && \ python3 -mpip install --no-cache-dir --upgrade pip setuptools wheel && \ - python3 -mpip install --no-cache-dir -r ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt && \ + python3 -mpip install --no-cache-dir -r ${CONTAINER_APP_DIR}/${APP_DIR_1}/requirements.txt && \ + python3 -mpip install --no-cache-dir -r ${CONTAINER_APP_DIR}/${APP_DIR_2}/requirements.txt && \ apt-get autoremove -y && \ apt-get autoclean -y && \ rm -rf /var/lib/apt/lists/* # Copy in python code -COPY ${MODULE_DIR}/${APP_DIR} ${CONTAINER_APP_DIR}/${APP_DIR} -COPY ${MODULE_DIR}/${COMMON_DIR} ${CONTAINER_APP_DIR}/${COMMON_DIR} - - -# Add in the python-based filter-joiner module -ARG MODULE_DIR="modules" -ARG APP_DIR="filter_joiner" -ARG COMMON_DIR="common" -ARG CONTAINER_APP_DIR="/usr/src/app" -ENV PYTHONPATH="${PYTHONPATH}:${CONTAINER_APP_DIR}" - -WORKDIR ${CONTAINER_APP_DIR} - -COPY ${MODULE_DIR}/${APP_DIR}/requirements.txt ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt - - -RUN apt update && \ - apt-get install -y --no-install-recommends \ - python3.8 && \ - apt install -y python3-pip && \ - python3 -mpip install --no-cache-dir --upgrade pip setuptools wheel && \ - python3 -mpip install --no-cache-dir -r ${CONTAINER_APP_DIR}/${APP_DIR}/requirements.txt && \ - apt-get autoremove -y && \ - apt-get autoclean -y && \ - rm -rf /var/lib/apt/lists/* && \ - groupadd -g 9999 appuser && \ - useradd -r -u 9999 -g appuser appuser - -# Copy in python code -COPY ${MODULE_DIR}/${APP_DIR} ${CONTAINER_APP_DIR}/${APP_DIR} +COPY ${MODULE_DIR}/${APP_DIR_1} ${CONTAINER_APP_DIR}/${APP_DIR_1} +COPY ${MODULE_DIR}/${APP_DIR_2} ${CONTAINER_APP_DIR}/${APP_DIR_2} COPY ${MODULE_DIR}/${COMMON_DIR} ${CONTAINER_APP_DIR}/${COMMON_DIR} # Build in the threshold selection module From f1fa951eef3746832081e465199dcb7e870181f0 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 25 Feb 2026 17:34:10 -0700 Subject: [PATCH 35/46] interime pipeline to test temp pipe --- .../concH2oSoilSalinity_pad_qaqc_join.yaml | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml new file mode 100644 index 000000000..268167b81 --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml @@ -0,0 +1,150 @@ +--- +pipeline: + name: concH2oSoilSalinity_pad_qaqc_join +transform: + image_pull_secrets: + - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-0736d51 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + + # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ + set -euo pipefail + IFS=$'\n\t' + + echo "=== Starting pipeline ===" + + # Refresh interim directories with each datum (otherwise they persist and cause probs) + rm -rf /tmp/pfs/padded_analyzer + rm -rf /tmp/pfs/padded_analyzerCopy + mkdir -p /tmp/pfs/padded_analyzer + + rm -rf /tmp/qaqc_plau + rm -rf /tmp/qaqc_plauCopy + mkdir -p /tmp/qaqc_plau + + rm -rf /tmp/pfs/joined_data + rm -rf /tmp/pfs/joined_dataCopy + mkdir -p /tmp/pfs/joined_data + + # ---- Run first module - padded_timeseries_analyzer ----pc d + # This validates the padded data from thresh_select_ts_pad has complete manifests + echo "=== Running padded_timeseries_analyzer ===" + export OUT_PATH=/tmp/pfs/padded_analyzer + + python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/padded_analyzer + + echo "=== Padded analyzer complete ===" + + # Run second module - qaqc plausibility + + Rscript ./flow.qaqc.plau.R \ + DirIn=/tmp/pfs/padded_analyzerCopy \ + DirOut=/tmp/qaqc_plau \ + DirErr=/pfs/out/errored_datums \ + "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "DirSubCopy=location|threshold" + + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/qaqc_plau /tmp/qaqc_plauCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/qaqc_plau + + # ---- Run third module - filter-joiner ---- + # Now join the base qc data with temperature data to prep for second test + echo "=== Running filter_joiner to add temperature data ===" + export DATA_PATH=/tmp/qaqc_plauCopy + export OUT_PATH=/pfs/out + python3 -m filter_joiner.filter_joiner_main + + cp -rL /tmp/pfs/joined_data /tmp/pfs/joined_dataCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/joined_data + + echo "=== Filter_joiner complete ===" + + ###custom script will go here for temperature test + + EOF + env: + # Environment variables for padded timeseries analyzer + LOG_LEVEL: DEBUG + ERR_PATH: /pfs/out/errored_datums + RELATIVE_PATH_INDEX: "3" + # Environment variables for filter-joiner + CONFIG: | + --- + # Join validated padded data with temperature data + input_paths: + - path: + name: QAQC_DATA + # Filter validated output from qaqc + ##NOTE: Filter joiner came after qaqc, so no /pfs/ in glob to keep indices in line + glob_pattern: /tmp/qaqc_plauCopy/*/*/*/*/** + # Join on Y/M/D/group + join_indices: [3,4,5,6] + - path: + name: TEMP_PATH + # Filter for temperature data + glob_pattern: /pfs/TEMP_PATH/*/*/*/*/temp-soil*/** + # Join on named location (already joined below by day) + join_indices: [3,4,5,6] + + LINK_TYPE: COPY +input: + join: + - pfs: + name: DATA_PATH + repo: concH2oSoilSalinity_thresh_select_ts_pad + glob: /(*)/(*)/(*) + joinOn: $1/$2/$3 + - pfs: + name: TEMP_PATH + repo: concH2oSoilSalinity_group_path + glob: /(*)/(*)/(*) + joinOn: $1/$2/$3 + empty_files: false +parallelism_spec: + constant: 5 +autoscaling: true +resource_requests: + memory: 2G + cpu: 5.5 +resource_limits: + memory: 4G + cpu: 7 +sidecar_resource_requests: + memory: 3G + cpu: 0.3 +datum_set_spec: + number: 1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class From 45e38363afae36b32ed8de00937d6fe613fb3ae4 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 25 Feb 2026 17:34:37 -0700 Subject: [PATCH 36/46] delete test pipeline --- ...ity_analyze_pad_qaqc_envscn_base_glob.yaml | 114 ------------------ 1 file changed, 114 deletions(-) delete mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml deleted file mode 100644 index 5d83ccd74..000000000 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn_base_glob.yaml +++ /dev/null @@ -1,114 +0,0 @@ ---- -pipeline: - name: concH2oSoilSalinity_analyze_pad_qaqc_envscn_v2 -transform: - image_pull_secrets: - - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-ae444b5 - cmd: - - sh - - "-c" - - |- - /bin/bash <<'EOF' - - # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ - set -euo pipefail - IFS=$'\n\t' - - # Refresh interim directories with each datum (otherwise they persist and cause probs) - rm -rf /tmp/pfs/padded_analyzer - rm -rf /tmp/pfs/padded_analyzerCopy - mkdir -p /tmp/pfs/padded_analyzer - - # Run first module - padded_timeseries_analyzer - python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main - - # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) - cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/pfs/padded_analyzer - - # Run second module - qaqc plausibility - - Rscript ./flow.qaqc.plau.R \ - DirIn=/tmp/pfs/padded_analyzerCopy \ - DirOut=/tmp/pfs/qaqc_plau \ - DirErr=/pfs/out/errored_datums \ - "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "DirSubCopy=data|location|threshold" - - ###custom script will go here for temperature test - - # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) - cp -rL /tmp/pfs/qaqc_plau /tmp/pfs/qaqc_plauCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/pfs/qaqc_plau - - Rscript ./flow.envscn.temp.flags.R \ - DirIn=/tmp/pfs/qaqc_plauCopy \ - DirOut=/pfs/out \ - DirErr=/pfs/out/errored_datums \ - DirTemp=$TEMP_PATH \ - "DirSubCopy=data|location|flags" - - EOF - env: - # Environment variables for padded timeseries analyzer - OUT_PATH: /tmp/pfs/padded_analyzer - LOG_LEVEL: DEBUG - RELATIVE_PATH_INDEX: '3' - ERR_PATH: /pfs/out/errored_datums - # Environment variables for qaqc plausibility - PARALLELIZATION_INTERNAL: '5' -input: - join: - - pfs: - name: DATA_PATH - repo: concH2oSoilSalinity_thresh_select_ts_pad - glob: /*/*/*/ - - pfs: - name: TEMP_PATH - repo: concH2oSoilSalinity_group_path - glob: /*/*/*/ - # - pfs: - # name: SCHEMA_FLAGS - # repo: concH2oSoilSalinity_avro_schemas - # glob: /concH2oSoilSalinity/flags_plausibility_concH2oSoilSalinity.avsc -parallelism_spec: - constant: 5 -autoscaling: true -resource_requests: - memory: 2G - cpu: 5.5 -resource_limits: - memory: 4G - cpu: 7 -sidecar_resource_requests: - memory: 3G - cpu: 0.3 -datum_set_spec: - number: 1 -scheduling_spec: - node_selector: - cloud.google.com/compute-class: pach-pipeline-class From 13332cf6548777860119d4eebf78c689e971e2fe Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 25 Feb 2026 19:06:33 -0700 Subject: [PATCH 37/46] updating flow script to not need a DirTemp, but still handle one if input. --- .../def.load.temp.sensors.R | 2 +- .../flow.envscn.temp.flags.R | 30 +++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R index 8365fef16..7d4bd6d3f 100644 --- a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R +++ b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R @@ -135,7 +135,7 @@ def.load.temp.sensors <- function(DirTemp, } log$info(base::paste0('Loaded ', base::nrow(sensorDepthDf), ' temperature sensors')) - log$info(base::paste0('Temp sensors found: ', sensorDepthDf$sensor_id)) + log$debug(base::paste0('Temp sensors found: ', sensorDepthDf$sensor_id)) return(sensorDepthDf) } diff --git a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R index d5130309d..9423cf247 100644 --- a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R @@ -124,11 +124,11 @@ Para <- NameParaReqd = c( "DirIn", "DirOut", - "DirErr", - "DirTemp" + "DirErr" ), NameParaOptn = c( "DirSubCopy", + "DirTemp", "SchmQf" ), log = log @@ -139,7 +139,7 @@ Para <- log$debug(base::paste0('Input directory: ', Para$DirIn)) log$debug(base::paste0('Output directory: ', Para$DirOut)) log$debug(base::paste0('Error directory: ', Para$DirErr)) -log$debug(base::paste0('Temperature directory: ', Para$DirTemp)) +#log$debug(base::paste0('Temperature directory: ', Para$DirTemp)) # Retrieve output schema for flags FileSchmQf <- Para$SchmQf @@ -166,6 +166,17 @@ log$debug(base::paste0( base::paste0(nameDirSub, collapse = ',') )) +# Retrieve DirTemp if provided +FileDirTemp <- Para$DirTemp + +# Read in the schema +if(base::is.null(FileDirTemp) || FileDirTemp == 'NA'){ + FileDirTemp <- NULL +} else { + log$debug(base::paste0('Temperature Directory provided: ', FileDirTemp)) + +} + # Find all the input paths (datums). We will process each one. DirIn <- NEONprocIS.base::def.dir.in(DirBgn = Para$DirIn, @@ -177,13 +188,20 @@ DirIn <- doParallel::registerDoParallel(numCoreUse) foreach::foreach(idxDirIn = DirIn) %dopar% { log$info(base::paste0('Processing path to datum: ', idxDirIn)) - - # Run the wrapper function for each datum, with error routing + #if no temperature directory was provided assume it is two levels up from IdxDirIn. + if (is.null(FileDirTemp)){ + # Get the directory name two levels up + idxDirTemp <- dirname(dirname(idxDirIn)) + } else { + tempFiles <- list.files(FileDirTemp, full.names = T) + idxDirTemp <- tempFiles[grepl(tempFiles, pattern =basename(dirname(dirname(idxDirIn))))] + } + # Run the wrapper function for each datum, with error routing tryCatch( withCallingHandlers( wrap.envscn.temp.flags(DirIn=idxDirIn, DirOutBase=Para$DirOut, - DirTemp=Para$DirTemp, + DirTemp=idxDirTemp, SchmQf=FileSchmQf, DirSubCopy=DirSubCopy, log=log From 1f92c4586e357939368a3584eea24269cf2179c2 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Wed, 25 Feb 2026 19:32:07 -0700 Subject: [PATCH 38/46] remove debug line --- flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R | 1 - 1 file changed, 1 deletion(-) diff --git a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R index 9423cf247..30c478493 100644 --- a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R @@ -143,7 +143,6 @@ log$debug(base::paste0('Error directory: ', Para$DirErr)) # Retrieve output schema for flags FileSchmQf <- Para$SchmQf -log$debug(base::paste0('Output schema for bulk precipitation custom flags: ',base::paste0(FileSchmData,collapse=','))) # Read in the schema if(base::is.null(FileSchmQf) || FileSchmQf == 'NA'){ From 94324c7e03add891deda1536698becfd0d1c4746 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Thu, 26 Feb 2026 08:52:03 -0700 Subject: [PATCH 39/46] combined module with filter joiner so temp data is in with the base qaqc'd data --- ...cH2oSoilSalinity_pad_qaqc_join_custom.yaml | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml new file mode 100644 index 000000000..4a9ba0eea --- /dev/null +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml @@ -0,0 +1,154 @@ +--- +pipeline: + name: concH2oSoilSalinity_pad_qaqc_join_custom +transform: + image_pull_secrets: + - battelleecology-quay-read-all-pull-secret + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-1f92c45 + cmd: + - sh + - "-c" + - |- + /bin/bash <<'EOF' + + # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ + set -euo pipefail + IFS=$'\n\t' + + echo "=== Starting pipeline ===" + + # Refresh interim directories with each datum (otherwise they persist and cause probs) + rm -rf /tmp/pfs/padded_analyzer + rm -rf /tmp/pfs/padded_analyzerCopy + mkdir -p /tmp/pfs/padded_analyzer + + rm -rf /tmp/qaqc_plau + rm -rf /tmp/qaqc_plauCopy + mkdir -p /tmp/qaqc_plau + + rm -rf /tmp/pfs/joined_data + rm -rf /tmp/pfs/joined_dataCopy + mkdir -p /tmp/pfs/joined_data + + # ---- Run first module - padded_timeseries_analyzer ----pc d + # This validates the padded data from thresh_select_ts_pad has complete manifests + echo "=== Running padded_timeseries_analyzer ===" + export OUT_PATH=/tmp/pfs/padded_analyzer + + python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/padded_analyzer + + echo "=== Padded analyzer complete ===" + + # Run second module - qaqc plausibility + + Rscript ./flow.qaqc.plau.R \ + DirIn=/tmp/pfs/padded_analyzerCopy \ + DirOut=/tmp/qaqc_plau \ + DirErr=/pfs/out/errored_datums \ + "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ + "DirSubCopy=location|threshold" + + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) + cp -rL /tmp/qaqc_plau /tmp/qaqc_plauCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/qaqc_plau + + # ---- Run third module - filter-joiner ---- + # Now join the base qc data with temperature data to prep for second test + echo "=== Running filter_joiner to add temperature data ===" + export DATA_PATH=/tmp/qaqc_plauCopy + export OUT_PATH=/tmp/pfs/joined_data + python3 -m filter_joiner.filter_joiner_main + + cp -rL /tmp/pfs/joined_data /tmp/pfs/joined_dataCopy || : # Allow to fail without exit code (happens if step above produced no output) + rm -r -f /tmp/pfs/joined_data + + echo "=== Filter_joiner complete ===" + + Rscript ./flow.envscn.temp.flags.R \ + DirIn=/tmp/pfs/joined_dataCopy \ + DirOut=/pfs/out \ + DirErr=/pfs/out/errored_datums \ + "DirSubCopy=data|location|flags" + + EOF + env: + # Environment variables for padded timeseries analyzer + LOG_LEVEL: DEBUG + ERR_PATH: /pfs/out/errored_datums + RELATIVE_PATH_INDEX: "3" + # Environment variables for filter-joiner + CONFIG: | + --- + # Join validated padded data with temperature data + input_paths: + - path: + name: QAQC_DATA + # Filter validated output from qaqc + ##NOTE: Filter joiner came after qaqc, so no /pfs/ in glob to keep indices in line + glob_pattern: /tmp/qaqc_plauCopy/*/*/*/*/** + # Join on Y/M/D/group + join_indices: [3,4,5,6] + - path: + name: TEMP_PATH + # Filter for temperature data + glob_pattern: /pfs/TEMP_PATH/*/*/*/*/temp-soil*/** + # Join on named location (already joined below by day) + join_indices: [3,4,5,6] + + LINK_TYPE: COPY +input: + join: + - pfs: + name: DATA_PATH + repo: concH2oSoilSalinity_thresh_select_ts_pad + glob: /(*)/(*)/(*) + joinOn: $1/$2/$3 + - pfs: + name: TEMP_PATH + repo: concH2oSoilSalinity_group_path + glob: /(*)/(*)/(*) + joinOn: $1/$2/$3 + empty_files: false +parallelism_spec: + constant: 5 +autoscaling: true +resource_requests: + memory: 2G + cpu: 5.5 +resource_limits: + memory: 4G + cpu: 7 +sidecar_resource_requests: + memory: 3G + cpu: 0.3 +datum_set_spec: + number: 1 +scheduling_spec: + node_selector: + cloud.google.com/compute-class: pach-pipeline-class From 6f154b883b3b03277ad49206260ff70ecdf8bee1 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Thu, 26 Feb 2026 09:50:55 -0700 Subject: [PATCH 40/46] add tests and data --- ...enviroscan_CFGLOC105245_2025-10-17.parquet | Bin 0 -> 10275 bytes ...05245_2025-10-17_flagsPlausibility.parquet | Bin 0 -> 45074 bytes .../CFGLOC105245/threshold/thresholds.json | 5604 +++++++++++++++++ ...M001501_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 15434 bytes ...001501_2025-10-17_ST_30_minute_030.parquet | Bin 0 -> 13374 bytes .../location/CFGLOC105248.json | 39 + .../location/prt_17977_locations.json | 409 ++ ...M001502_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 15438 bytes ...001502_2025-10-17_ST_30_minute_030.parquet | Bin 0 -> 13349 bytes .../location/CFGLOC105250.json | 39 + .../location/prt_20052_locations.json | 409 ++ ...M001503_2025-10-17_ST_1_minute_001.parquet | Bin 0 -> 15367 bytes ...001503_2025-10-17_ST_30_minute_030.parquet | Bin 0 -> 13288 bytes .../location/CFGLOC105252.json | 39 + .../location/prt_24766_locations.json | 409 ++ .../testthat/test-wrap-envscn-temp-flags.R | 57 + 16 files changed, 7005 insertions(+) create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/data/enviroscan_CFGLOC105245_2025-10-17.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/flags/enviroscan_CFGLOC105245_2025-10-17_flagsPlausibility.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/threshold/thresholds.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/data/temp-soil_GRSM001501_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/data/temp-soil_GRSM001501_2025-10-17_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/CFGLOC105248.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/prt_17977_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001502/data/temp-soil_GRSM001502_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001502/data/temp-soil_GRSM001502_2025-10-17_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001502/location/CFGLOC105250.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001502/location/prt_20052_locations.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/data/temp-soil_GRSM001503_2025-10-17_ST_1_minute_001.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/data/temp-soil_GRSM001503_2025-10-17_ST_30_minute_030.parquet create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/CFGLOC105252.json create mode 100644 flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/prt_24766_locations.json diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/data/enviroscan_CFGLOC105245_2025-10-17.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/data/enviroscan_CFGLOC105245_2025-10-17.parquet new file mode 100644 index 0000000000000000000000000000000000000000..acf55f927462afec36a207248be4bc1ca6f29c9c GIT binary patch literal 10275 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/flags/enviroscan_CFGLOC105245_2025-10-17_flagsPlausibility.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/flags/enviroscan_CFGLOC105245_2025-10-17_flagsPlausibility.parquet new file mode 100644 index 0000000000000000000000000000000000000000..82de2a9fc8c7f1bc0bdcf1c89273bd2ffde501e3 GIT binary patch literal 45074 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 literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/CFGLOC105248.json b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/CFGLOC105248.json new file mode 100644 index 000000000..81f2e7167 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/CFGLOC105248.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.020", + "FreqSamp":"0.1", + "HOR":"001", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105248", + "VER":"501", + "geometry":null, + "location":"plot1", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-17T00:00:00Z", + "end_date":"2025-10-18T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP1, Z1 Depth", + "domain":"D07", + "name":"CFGLOC105248", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth1", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/prt_17977_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/prt_17977_locations.json new file mode 100644 index 000000000..03a543431 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001501/location/prt_17977_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.020", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"001", + "location":"plot1", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-17T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.5014972, + 35.6884778, + 581.91 + ], + [ + -83.5015528, + 35.6884806, + 581.91 + ], + [ + -83.50155, + 35.688525, + 581.17 + ], + [ + -83.5014944, + 35.6885222, + 581.17 + ], + [ + -83.5014972, + 35.6884778, + 581.91 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "581.9073" + ], + [ + "ElevSoilC2", + "581.9073" + ], + [ + "ElevSoilC3", + "581.1691" + ], + [ + "ElevSoilC4", + "581.1691" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6884778" + ], + [ + "LatSoilC2", + "35.6884806" + ], + [ + "LatSoilC3", + "35.6885250" + ], + [ + "LatSoilC4", + "35.6885222" + ], + [ + "locDisturb", + "1.24,3.18,NA and 0.94,2.68,NA and 1.72,2.68,NA and 1.72,3.13,NA and 2.11,2.90,NA" + ], + [ + "LonSoilC1", + "-83.5014972" + ], + [ + "LonSoilC2", + "-83.5015528" + ], + [ + "LonSoilC3", + "-83.5015500" + ], + [ + "LonSoilC4", + "-83.5014944" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105236" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 274 + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105236" + }, + "type":"Feature" + }, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 1.34, + "y_offset": 2.74, + "z_offset": -0.02 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105248", + "remove_date":"2025-10-18T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105248", + "subLocation":"depth1", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"501" + } + ], + "source_id": 17977, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001502/data/temp-soil_GRSM001502_2025-10-17_ST_1_minute_001.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001502/data/temp-soil_GRSM001502_2025-10-17_ST_1_minute_001.parquet new file mode 100644 index 0000000000000000000000000000000000000000..7e1e0a08da426ca29314bc4969b3c766682d6390 GIT binary patch literal 15438 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 WyEFbuJ}mjK-GsFfCz`^C2 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 Si!aYV+(+kdTqFJ$;Qt4tYN)FK literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/data/temp-soil_GRSM001503_2025-10-17_ST_30_minute_030.parquet b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/data/temp-soil_GRSM001503_2025-10-17_ST_30_minute_030.parquet new file mode 100644 index 0000000000000000000000000000000000000000..ac4f3ffa96bf480ad165b2d9c807498313ea0663 GIT binary patch literal 13288 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 CS^b&- literal 0 HcmV?d00001 diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/CFGLOC105252.json b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/CFGLOC105252.json new file mode 100644 index 000000000..64b21c403 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/CFGLOC105252.json @@ -0,0 +1,39 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.160", + "FreqSamp":"0.1", + "HOR":"001", + "IS Processing Default Start Date":"2017-08-20T00:00:00Z", + "Required Asset Management Location Code":"CFGLOC105252", + "VER":"503", + "geometry":null, + "location":"plot1", + "properties":{ + "active_periods":[ + { + "start_date":"2025-10-17T00:00:00Z", + "end_date":"2025-10-18T00:00:00Z" + } + ], + "context":[ + + ], + "description":"Great Smoky Mountains Soil Temp Profile SP1, Z3 Depth", + "domain":"D07", + "name":"CFGLOC105252", + "site":"GRSM", + "type":"CONFIG" + }, + "subLocation":"depth3", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature" + } + ], + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/prt_24766_locations.json b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/prt_24766_locations.json new file mode 100644 index 000000000..c87906f66 --- /dev/null +++ b/flow/tests/testthat/pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/temp-soil_GRSM001503/location/prt_24766_locations.json @@ -0,0 +1,409 @@ +{ + "features":[ + { + "AngNedZaxs":"NA", + "AngPtch":"0", + "asset_manufacturer":"Thermometrics", + "asset_model":"R000-00000011", + "asset_software_version":null, + "Cal$PrdLab":"one time before installation", + "Data Rate":"0.1", + "DistZaxsLvlMeasSoil":"-0.160", + "FreqSamp":"0.1", + "geometry":null, + "HOR":"001", + "location":"plot1", + "properties":{ + "context":[ + + ], + "domain":"D07", + "install_date":"2025-10-17T00:00:00Z", + "locations":{ + "features":[ + { + "geometry":null, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ] + ], + "reference_location":{ + "geometry":null, + "properties":{ + "locations":{ + "features":[ + { + "geometry":{ + "coordinates":[ + [ + -83.5014972, + 35.6884778, + 581.91 + ], + [ + -83.5015528, + 35.6884806, + 581.91 + ], + [ + -83.50155, + 35.688525, + 581.17 + ], + [ + -83.5014944, + 35.6885222, + 581.17 + ], + [ + -83.5014972, + 35.6884778, + 581.91 + ] + ], + "type":"Polygon" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AngAzXaxsRefeSoil", + "C4" + ], + [ + "AngAzYaxsRefeSoil", + "C2" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "ElevSoilC1", + "581.9073" + ], + [ + "ElevSoilC2", + "581.9073" + ], + [ + "ElevSoilC3", + "581.1691" + ], + [ + "ElevSoilC4", + "581.1691" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "LatSoilC1", + "35.6884778" + ], + [ + "LatSoilC2", + "35.6884806" + ], + [ + "LatSoilC3", + "35.6885250" + ], + [ + "LatSoilC4", + "35.6885222" + ], + [ + "locDisturb", + "1.24,3.18,NA and 0.94,2.68,NA and 1.72,2.68,NA and 1.72,3.13,NA and 2.11,2.90,NA" + ], + [ + "LonSoilC1", + "-83.5014972" + ], + [ + "LonSoilC2", + "-83.5015528" + ], + [ + "LonSoilC3", + "-83.5015500" + ], + [ + "LonSoilC4", + "-83.5014944" + ], + [ + "Orientation Number", + "2" + ], + [ + "RefeSoil", + "C1" + ], + [ + "Required Asset Management Location Code", + "SOILPL105236" + ], + [ + "State province", + "TN" + ], + [ + "UTM Zone", + "17N" + ], + [ + "x Azimuth Angle", + 3 + ], + [ + "y Azimuth Angle", + 274 + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"SOILPL105236" + }, + "type":"Feature" + }, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 1.34, + "y_offset": 2.74, + "z_offset": -0.16 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + }, + "name":"CFGLOC105252", + "remove_date":"2025-10-18T00:00:00Z", + "site":"GRSM", + "site_location":{ + "features":[ + { + "geometry":{ + "coordinates":[ + -83.50195, + 35.68896, + 575.396725 + ], + "type":"Point" + }, + "properties":{ + "alpha": 0, + "beta": 0, + "end_date":"2025-10-18T00:00:00Z", + "gamma": 0, + "location_properties":[ + [ + "AERONET_XREF", + "NEON_GRSM" + ], + [ + "AngAzWindPrim", + "140-320" + ], + [ + "AngAzWindScnd", + "NA" + ], + [ + "AngAzWindTrty", + "NA" + ], + [ + "Country", + "USA" + ], + [ + "County", + "Sevier" + ], + [ + "DEIMS-SDR Site ID", + "https://deims.org/30f887c3-135b-41e8-9f44-9b084ce2b3bd" + ], + [ + "DistZaxsCnpy", + "31" + ], + [ + "DistZaxsDisp", + "20.2" + ], + [ + "Dom", + "D07" + ], + [ + "DURATION", + "Gradient" + ], + [ + "Geodetic datum", + "WGS84" + ], + [ + "HABITAT", + "Terrestrial" + ], + [ + "IS Processing Default Start Date", + "2017-08-20T00:00:00Z" + ], + [ + "NEONSCI_FIELD_SITE_URL_PATH", + "great-smoky-mountains-national-park-twin-creeks" + ], + [ + "Pf$AngEnuXaxs", + "NA" + ], + [ + "Pf$AngEnuYaxs", + "NA" + ], + [ + "Pf$Ofst", + "NA" + ], + [ + "PrdDcmpMax", + "3" + ], + [ + "PrdIncrAgrDcmp", + "1" + ], + [ + "PrdIncrCalc", + "1" + ], + [ + "PrdIncrPf", + "1" + ], + [ + "PrdWndwAgrDcmp", + "5" + ], + [ + "PrdWndwCalc", + "3" + ], + [ + "PrdWndwPf", + "9" + ], + [ + "PresAtmSite", + "93630" + ], + [ + "Private", + "FALSE" + ], + [ + "Read", + "HDF5" + ], + [ + "Required Asset Management Location Code", + "GRSM" + ], + [ + "Site Timezone", + "US/Eastern" + ], + [ + "State Abbreviation", + "TN" + ], + [ + "State province", + "TN" + ], + [ + "TypeEco", + "Hardwood deciduous forest dominated by oaks and maples" + ], + [ + "TypeSoil", + "Spivey-Santeetlah-Nowhere complex, 8 to 15 percent slopes" + ], + [ + "UTM Zone", + "17N" + ], + [ + "ZoneTime", + "EST" + ] + ], + "reference_location":null, + "start_date":"2025-10-17T00:00:00Z", + "x_offset": 0, + "y_offset": 0, + "z_offset": 0 + }, + "type":"Feature" + } + ], + "type":"FeatureCollection" + } + }, + "Required Asset Management Location Code":"CFGLOC105252", + "subLocation":"depth3", + "subSystem":"temp-profile", + "system":"soil", + "type":"Feature", + "VER":"503" + } + ], + "source_id": 24766, + "source_type":"prt", + "type":"FeatureCollection" +} diff --git a/flow/tests/testthat/test-wrap-envscn-temp-flags.R b/flow/tests/testthat/test-wrap-envscn-temp-flags.R index d6a42dc9e..7ba3c0d73 100644 --- a/flow/tests/testthat/test-wrap-envscn-temp-flags.R +++ b/flow/tests/testthat/test-wrap-envscn-temp-flags.R @@ -868,3 +868,60 @@ test_that("Integration test with missing threshold file (expect error)", { unlink(DirOutBase, recursive = TRUE) }) + +test_that("Integration test with combined DirIn and DirTemp", { + # Use actual test data paths + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/") + DirOutBase <- file.path(tempdir(), "test_output") + + # Only run if test data exists + if (dir.exists(DirIn) ) { + # Clean output directory + if (dir.exists(DirOutBase)) { + unlink(DirOutBase, recursive = TRUE) + } + + #add flow logic for DirTemp + DirTemp = dirname(dirname(DirIn)) + + # Run the wrap function + wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + ) + + # Check that output was created + expect_true(dir.exists(DirOutBase)) + + # Check that flags directory was created + flagsDir <- file.path(DirOutBase, "combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/flags") + expect_true(dir.exists(flagsDir)) + + # Check that flag file exists + flagFiles <- list.files(flagsDir, pattern = "flagsPlausibility.parquet") + expect_equal(length(flagFiles), 1) + + # Read and validate the output + flagData <- arrow::read_parquet(file.path(flagsDir, flagFiles[1])) + + # Check that tempTest columns were added + tempTestCols <- names(flagData)[grepl("tempTestDepth", names(flagData))] + expect_true(length(tempTestCols) == 8) + + # Check that flags have valid values (0, 1, or -1) + for (col in tempTestCols) { + expect_true(all(flagData[[col]] %in% c(-1L, 0L, 1L))) + } + + # Check that timestamps are present + expect_true("readout_time" %in% names(flagData)) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + } else { + skip("Test data not available") + } +}) From 830e95d849ed18512a38eba3ac99f516dc92654e Mon Sep 17 00:00:00 2001 From: burlingamet Date: Thu, 26 Feb 2026 09:58:34 -0700 Subject: [PATCH 41/46] one more test using a NULL tempdir --- .../testthat/test-wrap-envscn-temp-flags.R | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/flow/tests/testthat/test-wrap-envscn-temp-flags.R b/flow/tests/testthat/test-wrap-envscn-temp-flags.R index 7ba3c0d73..97d84defc 100644 --- a/flow/tests/testthat/test-wrap-envscn-temp-flags.R +++ b/flow/tests/testthat/test-wrap-envscn-temp-flags.R @@ -869,6 +869,29 @@ test_that("Integration test with missing threshold file (expect error)", { }) +test_that("Integration test with DirTemp being NULL", { + # Use actual test data paths + # Soil moisture data without threshold configuration file + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + # Temperature data path (test should fail before reaching here) + DirTemp <- NULL + DirOutBase <- file.path(tempdir(), "test_output") + + # Run the wrap function + + expect_error(wrap.envscn.temp.flags( + DirIn = DirIn, + DirOutBase = DirOutBase, + DirTemp = DirTemp, + DirSubCopy = c("data", "location", "threshold"), + log = NULL + )) + + # Cleanup + unlink(DirOutBase, recursive = TRUE) + +}) + test_that("Integration test with combined DirIn and DirTemp", { # Use actual test data paths DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/combined/tests/2025/10/17/conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/") From 68aa895f92c143e936996ba31bc00dfd16ae4629 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Thu, 26 Feb 2026 14:27:05 -0700 Subject: [PATCH 42/46] minor adjustment to flow of data for dirIn and dirTemp, documentaiton updates. --- .../flow.envscn.temp.flags.R | 64 ++++++------------- .../wrap.envscn.temp.flags.R | 20 +++--- ...cH2oSoilSalinity_pad_qaqc_join_custom.yaml | 9 ++- 3 files changed, 38 insertions(+), 55 deletions(-) diff --git a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R index 30c478493..2175393b8 100644 --- a/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/flow.envscn.temp.flags.R @@ -4,9 +4,9 @@ #' @author #' Teresa Burlingame \email{tburlingame@battelleecology.org} \cr #' -#' @description Workflow. Compute the heater and status flags by assessing the bit rate. Only -#' flagging alarm codes of interest. Add columns to qfPlau table prior to push to QM calculation module. -#' +#' @description Workflow. Use envirscan data and prt locations to match sensors based on depth, +#' assess temperature of soil, if too cold, flag data. If nearest isn't available average neighbors and +#' check if data below 1C. #' #' General code workflow: #' Parse input parameters @@ -14,9 +14,11 @@ #' For each datum: #' Create output directories and copy (by symbolic link) unmodified components #' Read in the L0 data files into arrow datasets -#' Compute flags based on sensor status streams -#' aggregate data to 5 and 30 minutes -#' Write stats and flags output to file +#' check if data needs temperature test (threshold) +#' Calculate nearest sensors +#' check for freezing temperatures +#' flag as necessary +#' write flags to output file #' #' This script is run at the command line with the following arguments. Each argument must be a string #' in the format "Para=value", where "Para" is the intended parameter name and "value" is the value of @@ -27,18 +29,17 @@ #' #' 1. "DirIn=value", where value is the path to the input data directory. #' The input repo should be structured by source ID as follows: -#' #/pfs/BASE_REPO/#/yyyy/mm/dd/#/source-id, where # indicates any number of parent and child directories +#' #/pfs/BASE_REPO/#/yyyy/mm/dd/group/ where # indicates any number of parent and child directories #' of any name, so long as they are not 'pfs' or recognizable as the 'yyyy/mm/dd' structure which indicates #' the 4-digit year, 2-digit month, and' 2-digit day. The source-id is the unique identifier of the sensor. \cr #' -#' Nested within the path for each source ID is (at a minimum) the folder: -#' /data -#' /flags +#' Nested within the path for each location is (at a minimum) the folder: +#' /sensor/location/data +#' /sensor/location/flags +#' /temp-sensor-group/data +#' /temp-sensor-group/location #' The data/flags folders holds any number of daily data/flags files padded around the yyyy/mm/dd in the input path. #' #' -#' For example: -#' Input path = precipWeighingv2_analyze_pad_and_qaqc_plau/2025/03/31/precip-weighing-v2_HQTW900000/pluvio/CFGLOC114405 -#' #' There may be other folders at the same level as the data directory. They are ignored and not passed #' to the output unless indicated in SubDirCopy. #' @@ -49,15 +50,10 @@ #' replace the #/pfs/BASE_REPO portion of \code{DirIn}. #' #' 4. "SchmQF=value" (optional), where value is the full path to schema for the QF flags after inputing custom flags -#' -#' 5. "DirTemp=valuë", where the value is the input path for soil temperature and location data to perform test. #' #' Ensure that any schema input here matches the column order of the auto-generated schema, #' simply making any desired changes to column names. #' -#' Ensure that any schema input here matches the column order of the auto-generated schema, -#' simply making any desired changes to column names. -#' #' 6. "DirSubCopy=value" (optional), where value is the names of additional subfolders, separated by #' pipes, at the same level as the data folder that are to be copied with a #' symbolic link to the output path. May NOT include 'data'. @@ -65,7 +61,7 @@ #' Note: This script implements logging described in \code{\link[NEONprocIS.base]{def.log.init}}, #' which uses system environment variables if available. #' -#' @return A repository with the computed precipitation and flags in DirOut, where DirOut replaces BASE_REPO but +#' @return A repository with the computed enviroscan flags and data in DirOut, where DirOut replaces BASE_REPO but #' otherwise retains the child directory structure of the input path. The terminal directories of each #' sensor location folder are "data" and "flags". #' @@ -76,10 +72,9 @@ #' @examples #' Not Run - Stepping through the code in Rstudio -#' Sys.setenv(DIR_IN="DirIn=/scratch/pfs/concH2oSoilSalinity_analyze_pad_and_qaqc_plau/2025/10/17/conc-h2o-soil-salinity_GRSM001501/") +#' Sys.setenv(DIR_IN="DirIn=/scratch/pfs/concH2oSoilSalinity_pad_qaqc_join/2025/10/17/conc-h2o-soil-salinity_GRSM001501/") #' log <- NEONprocIS.base::def.log.init(Lvl = "debug") #' arg = c( "DirIn=$DIR_IN", -#' "DirTemp=/scratch/pfs/concH2oSoilSalinity_group_path/2025/10/17/conc-h2o-soil-salinity_GRSM005501/", #' "DirOut=/scratch/pfs/tb_out", #' "DirErr=/scratch/pfs/tb_out/errored_datums") #' Then copy and paste rest of workflow into the command window @@ -128,7 +123,6 @@ Para <- ), NameParaOptn = c( "DirSubCopy", - "DirTemp", "SchmQf" ), log = log @@ -159,23 +153,13 @@ log$debug(base::paste0( )) # What are the expected subdirectories of each input path -nameDirSub <- c('data','flags') +#nameDirSub <- c('data','flags') +nameDirSub <- c('enviroscan') log$debug(base::paste0( 'Minimum expected subdirectories of each datum path: ', base::paste0(nameDirSub, collapse = ',') )) -# Retrieve DirTemp if provided -FileDirTemp <- Para$DirTemp - -# Read in the schema -if(base::is.null(FileDirTemp) || FileDirTemp == 'NA'){ - FileDirTemp <- NULL -} else { - log$debug(base::paste0('Temperature Directory provided: ', FileDirTemp)) - -} - # Find all the input paths (datums). We will process each one. DirIn <- NEONprocIS.base::def.dir.in(DirBgn = Para$DirIn, @@ -187,20 +171,12 @@ DirIn <- doParallel::registerDoParallel(numCoreUse) foreach::foreach(idxDirIn = DirIn) %dopar% { log$info(base::paste0('Processing path to datum: ', idxDirIn)) - #if no temperature directory was provided assume it is two levels up from IdxDirIn. - if (is.null(FileDirTemp)){ - # Get the directory name two levels up - idxDirTemp <- dirname(dirname(idxDirIn)) - } else { - tempFiles <- list.files(FileDirTemp, full.names = T) - idxDirTemp <- tempFiles[grepl(tempFiles, pattern =basename(dirname(dirname(idxDirIn))))] - } - # Run the wrapper function for each datum, with error routing + # Run the wrapper function for each datum, with error routing tryCatch( withCallingHandlers( wrap.envscn.temp.flags(DirIn=idxDirIn, DirOutBase=Para$DirOut, - DirTemp=idxDirTemp, + DirTemp=idxDirIn, SchmQf=FileSchmQf, DirSubCopy=DirSubCopy, log=log diff --git a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R index 398fa4c6c..0e626c5a0 100644 --- a/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R +++ b/flow/flow.envscn.temp.flags/wrap.envscn.temp.flags.R @@ -54,8 +54,8 @@ #' @examples #' # NOT RUN #' -#' "DirIn=/scratch/pfs/concH2oSoilSalinity_analyze_pad_and_qaqc_plau/2025/10/17/conc-h2o-soil-salinity_GRSM001501/", -#' "DirTemp=/scratch/pfs/concH2oSoilSalinity_group_path/2025/10/17/conc-h2o-soil-salinity_GRSM005501/", +#' "DirIn=/scratch/pfs/concH2oSoilSalinity_pad_qaqc_join/2025/10/17/conc-h2o-soil-salinity_GRSM001501/", +#' "DirTemp=/scratch/pfs/concH2oSoilSalinity_pad_qaqc_join/2025/10/17/conc-h2o-soil-salinity_GRSM005501/", #' "DirOut=/scratch/pfs/tb_out", #' "DirErr=/scratch/pfs/tb_out/errored_datums" #' wrap.envscn.temp.flags(DirIn,DirOutBase,DirTemp,DirSubCopy) @@ -68,7 +68,7 @@ ############################################################################################## wrap.envscn.temp.flags <- function(DirIn, DirOutBase, - DirTemp, + DirTemp = DirIn, SchmQf = NULL, DirSubCopy = NULL, log = NULL @@ -78,11 +78,15 @@ wrap.envscn.temp.flags <- function(DirIn, if(base::is.null(log)){ log <- NEONprocIS.base::def.log.init() } + #Set variable for enviroscan Dir in + DirInEnviro <- NEONprocIS.base::def.dir.in(DirIn, nameDirSub = c('data', 'flags')) + # def dir in with 'data'/flags' + ###clean up all below to look for DirInEnviro. # Gather info about the input directory and create the output directory. - InfoDirIn <- NEONprocIS.base::def.dir.splt.pach.time(DirIn,log=log) - dirInData <- fs::path(DirIn,'data') - dirInQf <- fs::path(DirIn,'flags') + InfoDirIn <- NEONprocIS.base::def.dir.splt.pach.time(DirInEnviro,log=log) + dirInData <- fs::path(DirInEnviro,'data') + dirInQf <- fs::path(DirInEnviro,'flags') dirOut <- fs::path(DirOutBase,InfoDirIn$dirRepo) dirOutQf <- fs::path(dirOut,'flags') @@ -95,14 +99,14 @@ wrap.envscn.temp.flags <- function(DirIn, # Copy with a symbolic link the desired subfolders DirSubCopy <- base::unique(base::setdiff(DirSubCopy, c('flags'))) if(base::length(DirSubCopy) > 0){ - NEONprocIS.base::def.dir.copy.symb(DirSrc = fs::path(DirIn, DirSubCopy), + NEONprocIS.base::def.dir.copy.symb(DirSrc = fs::path(DirInEnviro, DirSubCopy), DirDest = dirOut, LnkSubObj = FALSE, log = log) } # ===== Load and validate thresholds ===== - dirInThrsh <- fs::path(DirIn, 'threshold') + dirInThrsh <- fs::path(DirInEnviro, 'threshold') fileThsh <- base::list.files(dirInThrsh) if (base::length(fileThsh) == 0) { diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml index 4a9ba0eea..aab51756e 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml @@ -30,12 +30,15 @@ transform: rm -rf /tmp/pfs/joined_dataCopy mkdir -p /tmp/pfs/joined_data - # ---- Run first module - padded_timeseries_analyzer ----pc d + # ---- Run first module - padded_timeseries_analyzer ---- # This validates the padded data from thresh_select_ts_pad has complete manifests + echo "=== Running padded_timeseries_analyzer ===" + # set outpath for pad analyzer export OUT_PATH=/tmp/pfs/padded_analyzer python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main + # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) rm -r -f /tmp/pfs/padded_analyzer @@ -80,6 +83,7 @@ transform: # ---- Run third module - filter-joiner ---- # Now join the base qc data with temperature data to prep for second test + echo "=== Running filter_joiner to add temperature data ===" export DATA_PATH=/tmp/qaqc_plauCopy export OUT_PATH=/tmp/pfs/joined_data @@ -102,6 +106,7 @@ transform: LOG_LEVEL: DEBUG ERR_PATH: /pfs/out/errored_datums RELATIVE_PATH_INDEX: "3" + LINK_TYPE: COPY # Environment variables for filter-joiner CONFIG: | --- @@ -120,8 +125,6 @@ transform: glob_pattern: /pfs/TEMP_PATH/*/*/*/*/temp-soil*/** # Join on named location (already joined below by day) join_indices: [3,4,5,6] - - LINK_TYPE: COPY input: join: - pfs: From c97793eae31669df12efe5a080b66b58401cd6ee Mon Sep 17 00:00:00 2001 From: burlingamet Date: Thu, 26 Feb 2026 14:59:20 -0700 Subject: [PATCH 43/46] better null handling and test data update --- flow/flow.envscn.temp.flags/TEST_SUMMARY.txt | 255 ++++++++++++------ .../def.load.temp.sensors.R | 20 +- .../testthat/test-wrap-envscn-temp-flags.R | 57 ++-- 3 files changed, 235 insertions(+), 97 deletions(-) diff --git a/flow/flow.envscn.temp.flags/TEST_SUMMARY.txt b/flow/flow.envscn.temp.flags/TEST_SUMMARY.txt index 86e05ecfb..edab779fe 100644 --- a/flow/flow.envscn.temp.flags/TEST_SUMMARY.txt +++ b/flow/flow.envscn.temp.flags/TEST_SUMMARY.txt @@ -3,6 +3,7 @@ # File: test-wrap-envscn-temp-flags.R # Author: Teresa Burlingame # Date: February 2026 +# Last Updated: February 26, 2026 ################################################################################ ================================================================================ @@ -12,88 +13,97 @@ UNIT TESTS (Tests 1-5) TEST 1: def.sort.qf.cols - Column Sorting Function -------------------------------------------------------------------------------- Description: Tests the sorting of quality flag columns -Test Cases: - - Correctly sorts quality flag columns with depth numbers - - Handles empty input - - Handles columns without depth numbers +Test Cases (3 tests): + 1.1 Correctly sorts quality flag columns with depth numbers + 1.2 Handles empty input + 1.3 Handles columns without depth numbers Key Validations: - Static columns (readout_time) come first - - tempTest columns are sorted numerically by depth + - tempTest columns are sorted numerically by depth (e.g., 01, 02, 03) - All columns are preserved in output + - Empty inputs return empty output without error ================================================================================ TEST 2: def.find.temp.sensor - Sensor Selection Logic -------------------------------------------------------------------------------- Description: Tests the logic for finding the closest temperature sensor -Test Cases: - - Finds closest temperature sensor to target depth - - Handles sensors with no valid depths (all NA) - - Breaks ties by preferring shallower sensors - - Identifies edge sensors (shallowest/deepest) without neighbors - - Filters out sensors with NA depths +Test Cases (5 tests): + 2.1 Finds closest sensor to target depth and identifies neighbors + 2.2 Handles sensors with all NA depths (returns NULL) + 2.3 Breaks ties by preferring shallower (less negative) sensors + 2.4 Identifies edge sensors without neighbors on one side + 2.5 Filters out sensors with NA depths from consideration Key Validations: - Returns sensor closest to target depth - - Correctly identifies higher (shallower) and lower (deeper) neighbors + - Correctly identifies higher (shallower/less negative) neighbors + - Correctly identifies lower (deeper/more negative) neighbors - Returns NULL when no valid sensors exist - - Handles boundary conditions properly + - Handles boundary conditions (shallowest/deepest sensors) + - Shallowest sensor has no higher neighbor + - Deepest sensor has no lower neighbor ================================================================================ TEST 3: def.load.temp.sensors - Temperature Sensor Data Loading -------------------------------------------------------------------------------- Description: Tests loading temperature sensor metadata from location files -Test Cases: - - Loads temperature sensor metadata successfully - - Handles empty directories (no data) - - Averages depths when multiple location files exist for same sensor - - Returns empty data frame when no location files present - - Handles missing z_offset field in location files (results in NA depth) +Test Cases (5 tests): + 3.1 Loads temperature sensor metadata successfully from test data + 3.2 Handles empty directories (returns empty data frame) + 3.3 Averages depths when multiple location files exist for same sensor + 3.4 Returns empty data frame when no location files present + 3.5 Handles missing z_offset field in location files (results in NA depth) Key Validations: - Returns data frame with required columns: sensor_id, depth_m, data_path, location_path - Sensor IDs have correct prefix (temp-soil_) - - Depths are numeric and negative (below surface) + - Depths are numeric and non-positive (at or below surface, <= 0) - File paths exist and are accessible - - Multiple depths for same sensor are averaged + - Multiple location files for same sensor result in averaged depth + - Missing z_offset field leads to NA in depth_m column ================================================================================ TEST 4: def.calc.temp.flags - Temperature Flag Calculation -------------------------------------------------------------------------------- Description: Tests the core temperature flag calculation logic -Test Cases: - - Basic flag calculation (freezing check: temp < uncertainty) - - Handles neighbors that are too far away (exceeds threshold distance) - - Falls back to neighbor data when primary sensor is flagged +Test Cases (3 tests): + 4.1 Basic flag calculation (freezing check: temp < uncertainty) + 4.2 Handles neighbors that are too far away (exceeds distance threshold) + 4.3 Falls back to neighbor data when primary sensor is flagged (finalQF != 0) Key Validations: - Flag = 1 when soilTempMean < soilTempExpUncert (potential freezing) - - Flag = 0 when temperature is above uncertainty threshold - - Flag = -1 when primary sensor is bad and neighbors too far or unavailable + - Flag = 0 when temperature is above uncertainty threshold (not frozen) + - Flag = -1 when primary sensor has bad QF and neighbors too far or unavailable - Correctly averages neighbor sensor data when needed - - Respects distance threshold for neighbor selection + - Respects distance threshold for neighbor selection (default 0.15m) + - Uses primary sensor when finalQF = 0 (good data) + - Uses neighbor average when primary sensor finalQF != 0 (flagged data) Flag Meanings: 0 = Data is good (not frozen) - 1 = Data flagged (potentially frozen) - -1 = Cannot determine (missing or unreliable data) + 1 = Data flagged (potentially frozen: soilTempMean < soilTempExpUncert) + -1 = Cannot determine (missing, unreliable, or neighbors too distant) ================================================================================ TEST 5: def.apply.temp.flags - Flag Application to High-Frequency Data -------------------------------------------------------------------------------- Description: Tests joining minute-interval flags to high-frequency soil moisture -Test Cases: - - Correctly joins flags to high-frequency data based on timestamps - - Handles timestamps outside any temperature interval +Test Cases (2 tests): + 5.1 Correctly joins flags to high-frequency data based on timestamps + 5.2 Handles timestamps outside any temperature interval (remain -1) Key Validations: - High-frequency data points correctly matched to minute intervals + - Points inside temperature intervals receive corresponding flag (0 or 1) - Points outside temperature intervals retain -1 flag - - Temporal overlap logic works correctly (foverlaps) + - Temporal overlap logic works correctly using foverlaps + - readout_time used for temporal matching ================================================================================ INTEGRATION TESTS (Test 6) @@ -107,38 +117,52 @@ All integration tests verify: - Flags have valid values only: -1, 0, or 1 - Timestamp column (readout_time) preserved +Total Integration Tests: 8 + -------------------------------------------------------------------------------- TEST 6.1: Baseline Test with Good Data -------------------------------------------------------------------------------- -Scenario: Normal operating conditions +Scenario: Normal operating conditions with complete valid data Data: - - Good soil moisture data with valid depths - - Good temperature sensor data with valid locations + - Good soil moisture data with valid depths and threshold file + - Good temperature sensor data with valid locations and z_offset +Test Data Path: + - DirIn: pfs/envscn_temp_flags/enviroscan/tests/good_data/ + - DirTemp: pfs/envscn_temp_flags/temp/tests/good_data/ Expected Result: - Flags calculated successfully - Mix of 0, 1, and -1 values based on actual conditions + - All 8 tempTestDepth columns present and populated -------------------------------------------------------------------------------- TEST 6.2: Missing Soil Moisture Depths -------------------------------------------------------------------------------- Scenario: Depth information missing from soil moisture data files Data: - - Soil moisture data WITHOUT depth metadata + - Soil moisture data WITHOUT depth metadata in location files - Good temperature sensor data +Test Data Path: + - DirIn: pfs/envscn_temp_flags/enviroscan/tests/no_depths/ + - DirTemp: pfs/envscn_temp_flags/temp/tests/good_data/ Expected Result: - - All flags set to -1 (cannot match sensors to depths) + - All flags set to -1 (cannot match sensors to depths without depth info) - Process completes without error + - tempTestDepth01QF and all other depth columns contain only -1 -------------------------------------------------------------------------------- TEST 6.3: Freezing Temperature Conditions -------------------------------------------------------------------------------- Scenario: All temperature values indicate freezing conditions Data: - - Good soil moisture data - - Temperature data with all values < uncertainty threshold + - Good soil moisture data with valid depths + - Temperature data with all values < uncertainty threshold (simulated freezing) +Test Data Path: + - DirIn: pfs/envscn_temp_flags/enviroscan/tests/good_data/ + - DirTemp: pfs/envscn_temp_flags/temp/tests/all_freezing/ Expected Result: - Most/all flags set to 1 (flagged as frozen) - Demonstrates frozen soil detection logic + - tempTestDepth02QF expected to be 1 for all rows -------------------------------------------------------------------------------- TEST 6.4: Missing Temperature Data Directory @@ -147,9 +171,13 @@ Scenario: Temperature sensor data directory does not exist Data: - Good soil moisture data - Non-existent temperature data path +Test Data Path: + - DirIn: pfs/envscn_temp_flags/enviroscan/tests/good_data/ + - DirTemp: pfs/envscn_temp_flags/temp/tests/no_temp_data/2020/10/14 Expected Result: - All flags set to -1 (no temperature data available) - Process completes without error (graceful degradation) + - All 8 tempTestDepth columns contain only -1 -------------------------------------------------------------------------------- TEST 6.5: Missing z_offset in Location Files @@ -157,22 +185,65 @@ TEST 6.5: Missing z_offset in Location Files Scenario: Temperature sensor location files lack z_offset field Data: - Good soil moisture data - - Temperature location files missing z_offset field + - Temperature location files missing z_offset field (cannot determine depth) +Test Data Path: + - DirIn: pfs/envscn_temp_flags/enviroscan/tests/good_data/ + - DirTemp: pfs/envscn_temp_flags/temp/tests/no_z_offset/ Expected Result: - Some flags may be -1 where depth cannot be determined - Process continues with available data - Valid values (0, 1, -1) only + - All 8 tempTestDepth columns present -------------------------------------------------------------------------------- TEST 6.6: Missing Threshold Configuration File -------------------------------------------------------------------------------- -Scenario: Required threshold configuration file is absent +Scenario: Required threshold configuration file is absent from soil moisture data Data: - - Soil moisture data WITHOUT threshold file - - Temperature data + - Soil moisture data WITHOUT threshold/thresholds.json file + - Temperature data present but not reached due to error +Test Data Path: + - DirIn: pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/ + - DirTemp: pfs/envscn_temp_flags/temp/tests/no_temp_data/ Expected Result: - Function throws error (threshold file is mandatory) - Error handling test validates proper failure mode + - wrap.envscn.temp.flags() exits with expect_error + +-------------------------------------------------------------------------------- +TEST 6.7: NULL DirTemp Parameter (Required for Tests) +-------------------------------------------------------------------------------- +Scenario: DirTemp parameter provided as NULL (function should handle gracefully) +Data: + - Good soil moisture data with valid depths + - DirTemp = NULL (no temperature data path specified) +Test Data Path: + - DirIn: pfs/envscn_temp_flags/enviroscan/tests/good_data/ + - DirTemp: NULL +Expected Result: + - Function runs successfully (does not error) + - All flags set to -1 (no temperature data available) + - Process completes and writes output files + - All 8 tempTestDepth columns present with -1 values +Note: This validates that temp directory is required but NULL is acceptable + +-------------------------------------------------------------------------------- +TEST 6.8: Combined DirIn and DirTemp (Filter-Joiner Output) +-------------------------------------------------------------------------------- +Scenario: Testing with data structure where enviroscan and temp-soil are + in the same parent directory (post filter-joiner combination) +Data: + - Combined directory structure with both enviroscan and temp-soil subdirs + - DirTemp is calculated as dirname(dirname(DirIn)) within test +Test Data Path: + - DirIn: pfs/envscn_temp_flags/combined/tests/2025/10/17/ + conc-h2o-soil-salinity_GRSM001501/enviroscan/CFGLOC105245/ + - DirTemp: (calculated) ...conc-h2o-soil-salinity_GRSM001501/ +Expected Result: + - Flags calculated successfully with combined data structure + - All 8 tempTestDepth columns present + - Valid flag values (0, 1, -1) throughout + - Demonstrates compatibility with filter-joiner pipeline output ================================================================================ TEST DATA ORGANIZATION @@ -183,70 +254,104 @@ Test data is organized under: tests/testthat/pfs/envscn_temp_flags/ Structure: temp/ # Temperature sensor data tests/ - good_data/ # Normal operating data - multiple_depth/ # Multiple location files per sensor + good_data/ # Normal operating data with valid depths + multiple_depth/ # Multiple location files per sensor (test averaging) no_locations/ # Missing location files - no_z_offset/ # Location files without z_offset - all_freezing/ # All temperatures below freezing + no_z_offset/ # Location files without z_offset field + all_freezing/ # All temperatures below freezing threshold 2025/10/14/ # Empty directory for "no data" test enviroscan/ # Soil moisture data tests/ - good_data/ # Normal operating data - no_depths/ # Missing depth metadata - no_thresholds/ # Missing threshold configuration + good_data/ # Normal operating data with valid depths + no_depths/ # Missing depth metadata in location files + no_thresholds/ # Missing threshold configuration file + + combined/ # Combined structure (post filter-joiner) + tests/ + 2025/10/17/ + conc-h2o-soil-salinity_GRSM001501/ + enviroscan/ # Soil moisture subdirectory + temp-soil_*/ # Temperature sensor subdirectories -Test data was copied from /scratch/pfs/ using copy_test_data.R or copy_test_data.sh +Test data was created from production data using copy scripts: + - Truncated to 50 rows per parquet file for efficiency + - Maintains realistic data structure and relationships + - Filtered to single group (GRSM001501) for combined tests ================================================================================ TESTING INSTRUCTIONS ================================================================================ To run all tests: - cd ~/github/NEON-IS-data-processing/flow/tests/testthat - Rscript run_temp_flag_tests.R + cd ~/github/NEON-IS-data-processing/flow/flow.envscn.temp.flags + Rscript run_unit_tests.R To run tests interactively in R: setwd("~/github/NEON-IS-data-processing/flow/tests/testthat") library(testthat) - test_file("test-envscn-temp-flags.R") + test_file("test-wrap-envscn-temp-flags.R") + +To run a specific test by name: + test_file("test-wrap-envscn-temp-flags.R", filter = "sort.qf.cols") + test_file("test-wrap-envscn-temp-flags.R", filter = "find.temp.sensor") + test_file("test-wrap-envscn-temp-flags.R", filter = "Integration test with good data") -To run a specific test: - test_file("test-envscn-temp-flags.R", filter = "sort.qf.cols") +To run only unit tests (skip integration tests): + test_file("test-wrap-envscn-temp-flags.R", filter = "Test def\\.") + +To run only integration tests: + test_file("test-wrap-envscn-temp-flags.R", filter = "Integration test") Before running tests: - 1. Ensure test data is copied: source("copy_test_data.R") - 2. Verify working directory is tests/testthat/ - 3. Source required function files (or use run_temp_flag_tests.R) + 1. Ensure test data exists in tests/testthat/pfs/envscn_temp_flags/ + 2. Verify working directory is correct + 3. Required libraries: testthat, data.table, arrow, jsonlite + 4. Source required function files (done automatically by run_unit_tests.R) + +Test Data Requirements: + - Test data must be in tests/testthat/pfs/envscn_temp_flags/ + - Combined test data must be in combined/tests/ subdirectory + - Tests will skip if data directories don't exist (except error tests) ================================================================================ COVERAGE SUMMARY ================================================================================ -Total Test Suites: 11 -Total Test Assertions: 47+ +Total Test Suites: 13 (5 unit test suites + 8 integration tests) +Total Test Cases: 26 + - Unit Tests: 18 test cases across 5 functions + - Integration Tests: 8 end-to-end scenarios Unit Test Coverage: - ✓ Column sorting and organization - ✓ Sensor depth matching logic - ✓ Sensor metadata loading and parsing - ✓ Flag calculation with fallback logic - ✓ Temporal flag application + ✓ Column sorting and organization (3 tests) + ✓ Sensor depth matching logic (5 tests) + ✓ Sensor metadata loading and parsing (5 tests) + ✓ Flag calculation with fallback logic (3 tests) + ✓ Temporal flag application (2 tests) Integration Test Coverage: ✓ Normal operating conditions ✓ Missing metadata (depths, locations, z_offset) - ✓ Missing data (temperature sensors) + ✓ Missing data (temperature sensors, directories) ✓ Edge conditions (freezing temperatures) ✓ Error conditions (missing required files) + ✓ NULL parameter handling (DirTemp = NULL) + ✓ Combined data structure (filter-joiner pipeline output) Functions Tested: - - def.sort.qf.cols() - - def.find.temp.sensor() - - def.load.temp.sensors() - - def.calc.temp.flags() - - def.apply.temp.flags() - - wrap.envscn.temp.flags() + - def.sort.qf.cols() - Column sorting + - def.find.temp.sensor() - Sensor matching + - def.load.temp.sensors() - Metadata loading + - def.calc.temp.flags() - Flag calculation + - def.apply.temp.flags() - Flag application + - wrap.envscn.temp.flags() - Main wrapper function + +Test Assertions: 80+ + - Structural validations (column presence, data types) + - Logical validations (flag values, neighbor selection) + - Edge case handling (empty data, missing fields) + - Error handling (required parameters, missing files) ================================================================================ END OF TEST SUMMARY diff --git a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R index 7d4bd6d3f..5dc88dcac 100644 --- a/flow/flow.envscn.temp.flags/def.load.temp.sensors.R +++ b/flow/flow.envscn.temp.flags/def.load.temp.sensors.R @@ -41,13 +41,19 @@ def.load.temp.sensors <- function(DirTemp, log <- NEONprocIS.base::def.log.init() } - # Find all temperature sensor files - filesTemp <- base::list.files(DirTemp, full.names = TRUE, recursive = TRUE) - - # Filter for 1-minute data files and location JSON files - filesTempData <- filesTemp[base::grepl("/temp-soil_[^/]+/data/[^/]+001\\.parquet$", filesTemp)] - filesTempLocation <- filesTemp[base::grepl("/temp-soil_[^/]+/location/[^/]*_locations\\.json$", filesTemp)] - + # Initialize log if not provided + if (base::is.null(DirTemp)) { + log$warn("no temperature data found, returning NULL") + filesTempData <- NULL + filesTempLocation <- NULL + } else { + # Find all temperature sensor files + filesTemp <- base::list.files(DirTemp, full.names = TRUE, recursive = TRUE) + # Filter for 1-minute data files and location JSON files + filesTempData <- filesTemp[base::grepl("/temp-soil_[^/]+/data/[^/]+001\\.parquet$", filesTemp)] + filesTempLocation <- filesTemp[base::grepl("/temp-soil_[^/]+/location/[^/]*_locations\\.json$", filesTemp)] + } + if (base::length(filesTempData) == 0) { log$warn(base::paste0('No temperature data files found in ', DirTemp)) return(data.frame(sensor_id = character(), depth_m = numeric(), diff --git a/flow/tests/testthat/test-wrap-envscn-temp-flags.R b/flow/tests/testthat/test-wrap-envscn-temp-flags.R index 97d84defc..f3b088c28 100644 --- a/flow/tests/testthat/test-wrap-envscn-temp-flags.R +++ b/flow/tests/testthat/test-wrap-envscn-temp-flags.R @@ -20,17 +20,17 @@ library(arrow) library(jsonlite) # uncomment if running directly, change dirs as needed -# Source the functions under test -# setwd("~/GitHub/NEON-IS-data-processing/flow/tests/testthat") -# -# flow_dir <- "~/GitHub/NEON-IS-data-processing/flow/flow.envscn.temp.flags/" -# -# source(file.path(flow_dir, "def.load.temp.sensors.R")) -# source(file.path(flow_dir, "def.find.temp.sensor.R")) -# source(file.path(flow_dir, "def.calc.temp.flags.R")) -# source(file.path(flow_dir, "def.apply.temp.flags.R")) -# source(file.path(flow_dir, "def.sort.qf.cols.R")) -# source(file.path(flow_dir, "wrap.envscn.temp.flags.R")) +#Source the functions under test +setwd("~/GitHub/NEON-IS-data-processing/flow/tests/testthat") + +flow_dir <- "~/GitHub/NEON-IS-data-processing/flow/flow.envscn.temp.flags/" + +source(file.path(flow_dir, "def.load.temp.sensors.R")) +source(file.path(flow_dir, "def.find.temp.sensor.R")) +source(file.path(flow_dir, "def.calc.temp.flags.R")) +source(file.path(flow_dir, "def.apply.temp.flags.R")) +source(file.path(flow_dir, "def.sort.qf.cols.R")) +source(file.path(flow_dir, "wrap.envscn.temp.flags.R")) ######################################################################################################## # Test 1: def.sort.qf.cols @@ -869,24 +869,51 @@ test_that("Integration test with missing threshold file (expect error)", { }) -test_that("Integration test with DirTemp being NULL", { +test_that("Integration test with temp directory - required for tests", { # Use actual test data paths # Soil moisture data without threshold configuration file - DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/no_thresholds/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") + DirIn <- file.path(getwd(), "pfs/envscn_temp_flags/enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/") # Temperature data path (test should fail before reaching here) DirTemp <- NULL DirOutBase <- file.path(tempdir(), "test_output") # Run the wrap function - expect_error(wrap.envscn.temp.flags( + wrap.envscn.temp.flags( DirIn = DirIn, DirOutBase = DirOutBase, DirTemp = DirTemp, DirSubCopy = c("data", "location", "threshold"), log = NULL - )) + ) + + # Check that output was created + expect_true(dir.exists(DirOutBase)) + # Check that flags directory was created + flagsDir <- file.path(DirOutBase, "enviroscan/tests/good_data/2025/10/17/conc-h2o-soil-salinity_GRSM005501/enviroscan/CFGLOC105360/flags") + expect_true(dir.exists(flagsDir)) + + # Check that flag file exists + flagFiles <- list.files(flagsDir, pattern = "flagsPlausibility.parquet") + expect_equal(length(flagFiles), 1) + + # Read and validate the output + flagData <- arrow::read_parquet(file.path(flagsDir, flagFiles[1])) + + + tempTestCols <- names(flagData)[grepl("tempTestDepth", names(flagData))] + + # All flags should be -1 when no temperature data is available + for (col in tempTestCols) { + expect_true(all(flagData[[col]] %in% c(-1L))) + } + + # Check that all 8 columns are present + expect_true(length(tempTestCols)==8) + + # Check that timestamps are present + expect_true("readout_time" %in% names(flagData)) # Cleanup unlink(DirOutBase, recursive = TRUE) From 07e10e6ef66f18db9c0731b8273db73060db2fc5 Mon Sep 17 00:00:00 2001 From: burlingamet Date: Thu, 26 Feb 2026 15:27:53 -0700 Subject: [PATCH 44/46] repo clean up --- ...oilSalinity_analyze_pad_and_qaqc_plau.yaml | 99 ------------ ...oSoilSalinity_analyze_pad_qaqc_envscn.yaml | 118 -------------- ...alinity_analyze_pad_qaqc_join_custom.yaml} | 4 +- .../concH2oSoilSalinity_pad_qaqc_join.yaml | 150 ------------------ .../pipe_list_concH2oSoilSalinity.txt | 5 +- 5 files changed, 6 insertions(+), 370 deletions(-) delete mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml delete mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml rename pipe/concH2oSoilSalinity/{concH2oSoilSalinity_pad_qaqc_join_custom.yaml => concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml} (98%) delete mode 100644 pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml deleted file mode 100644 index 82417982d..000000000 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_and_qaqc_plau.yaml +++ /dev/null @@ -1,99 +0,0 @@ ---- -pipeline: - name: concH2oSoilSalinity_analyze_pad_and_qaqc_plau -transform: - image_pull_secrets: - - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-ts-pad-anls-qaqc-plau:v2.0.0 - cmd: - - sh - - "-c" - - |- - /bin/bash <<'EOF' - - # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ - set -euo pipefail - IFS=$'\n\t' - - # Refresh interim directories with each datum (otherwise they persist and cause probs) - rm -rf /tmp/pfs/padded_analyzer - rm -rf /tmp/pfs/padded_analyzerCopy - mkdir -p /tmp/pfs/padded_analyzer - - # Run first module - padded_timeseries_analyzer - python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main - - # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) - cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/pfs/padded_analyzer - - # Run second module - qaqc plausibility - - Rscript ./flow.qaqc.plau.R \ - DirIn=/tmp/pfs/padded_analyzerCopy \ - DirOut=/pfs/out \ - DirErr=/pfs/out/errored_datums \ - "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "DirSubCopy=data|location|threshold" - - ###custom script will go here for temperature test - - EOF - env: - # Environment variables for padded timeseries analyzer - OUT_PATH: /tmp/pfs/padded_analyzer - LOG_LEVEL: INFO - RELATIVE_PATH_INDEX: '3' - ERR_PATH: /pfs/out/errored_datums - # Environment variables for qaqc plausibility - PARALLELIZATION_INTERNAL: '5' -input: - cross: - - pfs: - name: DATA_PATH - repo: concH2oSoilSalinity_thresh_select_ts_pad - glob: /*/*/* - # - pfs: - # name: SCHEMA_FLAGS - # repo: concH2oSoilSalinity_avro_schemas - # glob: /concH2oSoilSalinity/flags_plausibility_concH2oSoilSalinity.avsc -parallelism_spec: - constant: 5 -autoscaling: true -resource_requests: - memory: 2G - cpu: 5.5 -resource_limits: - memory: 4G - cpu: 7 -sidecar_resource_requests: - memory: 3G - cpu: 0.3 -datum_set_spec: - number: 1 -scheduling_spec: - node_selector: - cloud.google.com/compute-class: pach-pipeline-class diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml deleted file mode 100644 index 2ff7243ac..000000000 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_envscn.yaml +++ /dev/null @@ -1,118 +0,0 @@ ---- -pipeline: - name: concH2oSoilSalinity_analyze_pad_qaqc_envscn -transform: - image_pull_secrets: - - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-ae444b5 - cmd: - - sh - - "-c" - - |- - /bin/bash <<'EOF' - - # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ - set -euo pipefail - IFS=$'\n\t' - - # Refresh interim directories with each datum (otherwise they persist and cause probs) - rm -rf /tmp/pfs/padded_analyzer - rm -rf /tmp/pfs/padded_analyzerCopy - mkdir -p /tmp/pfs/padded_analyzer - - # Run first module - padded_timeseries_analyzer - python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main - - # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) - cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/pfs/padded_analyzer - - # Run second module - qaqc plausibility - - Rscript ./flow.qaqc.plau.R \ - DirIn=/tmp/pfs/padded_analyzerCopy \ - DirOut=/tmp/pfs/qaqc_plau \ - DirErr=/pfs/out/errored_datums \ - "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "DirSubCopy=data|location|threshold" - - ###custom script will go here for temperature test - - # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) - cp -rL /tmp/pfs/qaqc_plau /tmp/pfs/qaqc_plauCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/pfs/qaqc_plau - - Rscript ./flow.envscn.temp.flags.R \ - DirIn=/tmp/pfs/qaqc_plauCopy \ - DirOut=/pfs/out \ - DirErr=/pfs/out/errored_datums \ - DirTemp=$TEMP_PATH \ - "DirSubCopy=data|location|flags" - - EOF - env: - # Environment variables for padded timeseries analyzer - OUT_PATH: /tmp/pfs/padded_analyzer - LOG_LEVEL: DEBUG - RELATIVE_PATH_INDEX: '3' - ERR_PATH: /pfs/out/errored_datums - # Environment variables for qaqc plausibility - PARALLELIZATION_INTERNAL: '5' -input: - join: - - pfs: - name: DATA_PATH - repo: concH2oSoilSalinity_thresh_select_ts_pad - glob: /(*)/(*)/(*)/(*) - joinOn: $1/$2/$3/$4 - - pfs: - name: TEMP_PATH - repo: concH2oSoilSalinity_group_path - glob: /(*)/(*)/(*)/(*) - joinOn: $1/$2/$3/$4 - groupBy: $1/$2/$3/$4 - empty_files: false - # - pfs: - # name: SCHEMA_FLAGS - # repo: concH2oSoilSalinity_avro_schemas - # glob: /concH2oSoilSalinity/flags_plausibility_concH2oSoilSalinity.avsc -parallelism_spec: - constant: 5 -autoscaling: true -resource_requests: - memory: 2G - cpu: 5.5 -resource_limits: - memory: 4G - cpu: 7 -sidecar_resource_requests: - memory: 3G - cpu: 0.3 -datum_set_spec: - number: 1 -scheduling_spec: - node_selector: - cloud.google.com/compute-class: pach-pipeline-class diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml similarity index 98% rename from pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml rename to pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml index aab51756e..9920abe54 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join_custom.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml @@ -1,10 +1,10 @@ --- pipeline: - name: concH2oSoilSalinity_pad_qaqc_join_custom + name: concH2oSoilSalinity_analyze_pad_qaqc_join_custom transform: image_pull_secrets: - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-1f92c45 + image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-c97793e cmd: - sh - "-c" diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml deleted file mode 100644 index 268167b81..000000000 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_pad_qaqc_join.yaml +++ /dev/null @@ -1,150 +0,0 @@ ---- -pipeline: - name: concH2oSoilSalinity_pad_qaqc_join -transform: - image_pull_secrets: - - battelleecology-quay-read-all-pull-secret - image: us-central1-docker.pkg.dev/neon-shared-service/neonscience/neon-is-qaqc-plau-envscn:sha-0736d51 - cmd: - - sh - - "-c" - - |- - /bin/bash <<'EOF' - - # Use bash-scrict mode. See http://redsymbol.net/articles/unofficial-bash-strict-mode/ - set -euo pipefail - IFS=$'\n\t' - - echo "=== Starting pipeline ===" - - # Refresh interim directories with each datum (otherwise they persist and cause probs) - rm -rf /tmp/pfs/padded_analyzer - rm -rf /tmp/pfs/padded_analyzerCopy - mkdir -p /tmp/pfs/padded_analyzer - - rm -rf /tmp/qaqc_plau - rm -rf /tmp/qaqc_plauCopy - mkdir -p /tmp/qaqc_plau - - rm -rf /tmp/pfs/joined_data - rm -rf /tmp/pfs/joined_dataCopy - mkdir -p /tmp/pfs/joined_data - - # ---- Run first module - padded_timeseries_analyzer ----pc d - # This validates the padded data from thresh_select_ts_pad has complete manifests - echo "=== Running padded_timeseries_analyzer ===" - export OUT_PATH=/tmp/pfs/padded_analyzer - - python3 -m padded_timeseries_analyzer.padded_timeseries_analyzer.padded_timeseries_analyzer_main - # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) - cp -rL /tmp/pfs/padded_analyzer /tmp/pfs/padded_analyzerCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/pfs/padded_analyzer - - echo "=== Padded analyzer complete ===" - - # Run second module - qaqc plausibility - - Rscript ./flow.qaqc.plau.R \ - DirIn=/tmp/pfs/padded_analyzerCopy \ - DirOut=/tmp/qaqc_plau \ - DirErr=/pfs/out/errored_datums \ - "TermTest1=VSICDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest2=VSICDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest3=VSICDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest4=VSICDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest5=VSICDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest6=VSICDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest7=VSICDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest8=VSICDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest9=VSWCfactoryDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest10=VSWCfactoryDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest11=VSWCfactoryDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest12=VSWCfactoryDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest13=VSWCfactoryDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest14=VSWCfactoryDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest15=VSWCfactoryDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest16=VSWCfactoryDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest17=VSWCsoilSpecificDepth02:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest18=VSWCsoilSpecificDepth03:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest19=VSWCsoilSpecificDepth04:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest20=VSWCsoilSpecificDepth05:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest21=VSWCsoilSpecificDepth06:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest22=VSWCsoilSpecificDepth07:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest23=VSWCsoilSpecificDepth08:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "TermTest24=VSWCsoilSpecificDepth01:null|gap|range(rmv)|step(rmv)|persistence(rmv)|spike(rmv)" \ - "DirSubCopy=location|threshold" - - # Copy output to another interim folder to destroy links (cannot daisy chain links from pfs input to output) - cp -rL /tmp/qaqc_plau /tmp/qaqc_plauCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/qaqc_plau - - # ---- Run third module - filter-joiner ---- - # Now join the base qc data with temperature data to prep for second test - echo "=== Running filter_joiner to add temperature data ===" - export DATA_PATH=/tmp/qaqc_plauCopy - export OUT_PATH=/pfs/out - python3 -m filter_joiner.filter_joiner_main - - cp -rL /tmp/pfs/joined_data /tmp/pfs/joined_dataCopy || : # Allow to fail without exit code (happens if step above produced no output) - rm -r -f /tmp/pfs/joined_data - - echo "=== Filter_joiner complete ===" - - ###custom script will go here for temperature test - - EOF - env: - # Environment variables for padded timeseries analyzer - LOG_LEVEL: DEBUG - ERR_PATH: /pfs/out/errored_datums - RELATIVE_PATH_INDEX: "3" - # Environment variables for filter-joiner - CONFIG: | - --- - # Join validated padded data with temperature data - input_paths: - - path: - name: QAQC_DATA - # Filter validated output from qaqc - ##NOTE: Filter joiner came after qaqc, so no /pfs/ in glob to keep indices in line - glob_pattern: /tmp/qaqc_plauCopy/*/*/*/*/** - # Join on Y/M/D/group - join_indices: [3,4,5,6] - - path: - name: TEMP_PATH - # Filter for temperature data - glob_pattern: /pfs/TEMP_PATH/*/*/*/*/temp-soil*/** - # Join on named location (already joined below by day) - join_indices: [3,4,5,6] - - LINK_TYPE: COPY -input: - join: - - pfs: - name: DATA_PATH - repo: concH2oSoilSalinity_thresh_select_ts_pad - glob: /(*)/(*)/(*) - joinOn: $1/$2/$3 - - pfs: - name: TEMP_PATH - repo: concH2oSoilSalinity_group_path - glob: /(*)/(*)/(*) - joinOn: $1/$2/$3 - empty_files: false -parallelism_spec: - constant: 5 -autoscaling: true -resource_requests: - memory: 2G - cpu: 5.5 -resource_limits: - memory: 4G - cpu: 7 -sidecar_resource_requests: - memory: 3G - cpu: 0.3 -datum_set_spec: - number: 1 -scheduling_spec: - node_selector: - cloud.google.com/compute-class: pach-pipeline-class diff --git a/pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt b/pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt index a286dcda8..ba6815195 100644 --- a/pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt +++ b/pipe/concH2oSoilSalinity/pipe_list_concH2oSoilSalinity.txt @@ -1,3 +1,6 @@ concH2oSoilSalinity_group_loader.yaml concH2oSalinity_group_assignment.yaml -concH2oSalinity_group_path.yaml \ No newline at end of file +concH2oSalinity_group_path.yaml +concH2oSalinity_threshold.yaml +concH2oSalinity_thresh_select_ts_pad.yaml +concH2oSalinity_analyze_pad_qaqc_join_custom.yaml \ No newline at end of file From 2eb2addfd490cb704e5fded93478a3c6e6f2834d Mon Sep 17 00:00:00 2001 From: burlingamet Date: Thu, 26 Feb 2026 16:46:30 -0700 Subject: [PATCH 45/46] update pipeline to use new schema --- ...Salinity_analyze_pad_qaqc_join_custom.yaml | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml index 9920abe54..ba82648f7 100644 --- a/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml +++ b/pipe/concH2oSoilSalinity/concH2oSoilSalinity_analyze_pad_qaqc_join_custom.yaml @@ -98,7 +98,9 @@ transform: DirIn=/tmp/pfs/joined_dataCopy \ DirOut=/pfs/out \ DirErr=/pfs/out/errored_datums \ - "DirSubCopy=data|location|flags" + "DirSubCopy=data|location|flags" \ + SchmQf=$SCHEMA_FLAGS + EOF env: @@ -126,18 +128,23 @@ transform: # Join on named location (already joined below by day) join_indices: [3,4,5,6] input: - join: - - pfs: - name: DATA_PATH - repo: concH2oSoilSalinity_thresh_select_ts_pad - glob: /(*)/(*)/(*) - joinOn: $1/$2/$3 + cross: - pfs: - name: TEMP_PATH - repo: concH2oSoilSalinity_group_path - glob: /(*)/(*)/(*) - joinOn: $1/$2/$3 - empty_files: false + name: SCHEMA_FLAGS + repo: concH2oSoilSalinity_avro_schemas + glob: /concH2oSoilSalinity/concH2oSoilSalinity_flags_plausibility.avsc + - join: + - pfs: + name: DATA_PATH + repo: concH2oSoilSalinity_thresh_select_ts_pad + glob: /(*)/(*)/(*) + joinOn: $1/$2/$3 + - pfs: + name: TEMP_PATH + repo: concH2oSoilSalinity_group_path + glob: /(*)/(*)/(*) + joinOn: $1/$2/$3 + empty_files: false parallelism_spec: constant: 5 autoscaling: true From c797c089b918a8968c1b770827cc5116312e2c3b Mon Sep 17 00:00:00 2001 From: Teresa Burlingame Date: Mon, 2 Mar 2026 09:50:17 -0800 Subject: [PATCH 46/46] update unit tests for threshold_loader --- .../tests/test_threshold_loader.py | 290 ++++++++++++++---- 1 file changed, 222 insertions(+), 68 deletions(-) diff --git a/modules/threshold_loader/tests/test_threshold_loader.py b/modules/threshold_loader/tests/test_threshold_loader.py index 904f74fdd..4f02e58bc 100644 --- a/modules/threshold_loader/tests/test_threshold_loader.py +++ b/modules/threshold_loader/tests/test_threshold_loader.py @@ -1,4 +1,21 @@ #!/usr/bin/env python3 +""" +Test suite for threshold_loader module. + +This test suite validates the threshold loading functionality with support for multiple contexts. + +UNIT TESTS (run by default with pytest): +- test_write_file: Basic threshold loading and JSON serialization +- test_exact_context_matching: Validates exact context matching (excludes partial/superset matches) +- test_multiple_contexts_filtering: Tests filtering with multiple context sets +- test_empty_context_handling: Tests 'none' context (empty context list) +- test_duplicate_threshold_handling: Verifies duplicate thresholds are deduplicated + +INTEGRATION TESTS (skipped by default, require database access): +- test_main: Backward compatibility with single CTXT environment variable +- test_main_new: New multi-context functionality (CTXT_1, CTXT_2, CTXT_3, ...) + +""" import os import json from pathlib import Path @@ -69,92 +86,215 @@ def get_thresholds(term) -> Iterator[Threshold]: self.assertTrue(string_value == 'value') print(json.dumps(json_data, indent=2, sort_keys=False)) - def test_multiple_contexts(self): - + def test_exact_context_matching(self): + """Test that only thresholds with exact context matches are returned.""" + def get_thresholds(term) -> Iterator[Threshold]: - """ - Mock function yielding thresholds with different contexts. - """ - # Threshold with context1|context2 - yield Threshold(threshold_name='threshold_for_context1_2', - term_name=term, - location_name='CPER', - context=['context1', 'context2'], - start_date='2024-01-01', - end_date='2024-12-31', - is_date_constrained=True, - start_day_of_year=1, - end_day_of_year=365, - number_value=10, - string_value='value1') + # Exact match: soil|ion-content + yield Threshold( + threshold_name='exact_match', + term_name=term, + location_name='CPER', + context=['soil', 'ion-content'], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=100.0, + string_value=None + ) - # Threshold with context3 only - yield Threshold(threshold_name='threshold_for_context3', - term_name=term, - location_name='CPER', - context=['context3'], - start_date='2024-01-01', - end_date='2024-12-31', - is_date_constrained=True, - start_day_of_year=1, - end_day_of_year=365, - number_value=20, - string_value='value2') + # Partial match: only soil (should be excluded) + yield Threshold( + threshold_name='partial_match', + term_name=term, + location_name='CPER', + context=['soil'], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=200.0, + string_value=None + ) - # Threshold with no context - yield Threshold(threshold_name='threshold_no_context', - term_name=term, - location_name='CPER', - context=[], - start_date='2024-01-01', - end_date='2024-12-31', - is_date_constrained=True, - start_day_of_year=1, - end_day_of_year=365, - number_value=30, - string_value='value3') + # Superset: soil|ion-content|extra (should be excluded) + yield Threshold( + threshold_name='superset_match', + term_name=term, + location_name='CPER', + context=['soil', 'ion-content', 'extra'], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=300.0, + string_value=None + ) + + load_thresholds(get_thresholds, self.out_path, 'term_name', ['soil|ion-content']) + expected_path = self.out_path.joinpath('thresholds.json') + + with open(expected_path, 'r') as threshold_file: + json_data = json.load(threshold_file) + thresholds = json_data['thresholds'] - # Threshold with different context that shouldn't match - yield Threshold(threshold_name='threshold_for_other_context', - term_name=term, - location_name='CPER', - context=['otherContext'], - start_date='2024-01-01', - end_date='2024-12-31', - is_date_constrained=True, - start_day_of_year=1, - end_day_of_year=365, - number_value=40, - string_value='value4') + # Should have exactly 1 threshold + self.assertEqual(len(thresholds), 1) + self.assertEqual(thresholds[0]['threshold_name'], 'exact_match') - # Test with multiple context sets - load_thresholds(get_thresholds, self.out_path, 'term_name', ['context1|context2', 'context3']) + def test_multiple_contexts_filtering(self): + """Test filtering with multiple context sets.""" + + def get_thresholds(term) -> Iterator[Threshold]: + # Matches first context + yield Threshold( + threshold_name='match_context1', + term_name=term, + location_name='CPER', + context=['soil', 'ion-content'], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=100.0, + string_value=None + ) + + # Matches second context + yield Threshold( + threshold_name='match_context2', + term_name=term, + location_name='CPER', + context=['soil', 'water-content', 'factory'], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=200.0, + string_value=None + ) + + # Matches neither + yield Threshold( + threshold_name='no_match', + term_name=term, + location_name='CPER', + context=['water', 'generic'], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=999.0, + string_value=None + ) + + load_thresholds(get_thresholds, self.out_path, 'term_name', + ['soil|ion-content', 'soil|water-content|factory']) expected_path = self.out_path.joinpath('thresholds.json') - self.assertTrue(expected_path.exists()) with open(expected_path, 'r') as threshold_file: json_data = json.load(threshold_file) thresholds = json_data['thresholds'] - # Should have 2 thresholds: one matching context1|context2, one matching context3 + # Should have exactly 2 thresholds self.assertEqual(len(thresholds), 2) - - # Get threshold names threshold_names = [t['threshold_name'] for t in thresholds] + self.assertIn('match_context1', threshold_names) + self.assertIn('match_context2', threshold_names) + self.assertNotIn('no_match', threshold_names) + + def test_empty_context_handling(self): + """Test handling of empty contexts (none).""" + + def get_thresholds(term) -> Iterator[Threshold]: + # No context + yield Threshold( + threshold_name='no_context', + term_name=term, + location_name='CPER', + context=[], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=100.0, + string_value=None + ) - # Should include both matching thresholds - self.assertIn('threshold_for_context1_2', threshold_names) - self.assertIn('threshold_for_context3', threshold_names) + # Has context (should be excluded) + yield Threshold( + threshold_name='has_context', + term_name=term, + location_name='CPER', + context=['soil'], + start_date='2024-01-01', + end_date=None, + is_date_constrained=False, + start_day_of_year=1, + end_day_of_year=365, + number_value=200.0, + string_value=None + ) + + load_thresholds(get_thresholds, self.out_path, 'term_name', ['none']) + expected_path = self.out_path.joinpath('thresholds.json') + + with open(expected_path, 'r') as threshold_file: + json_data = json.load(threshold_file) + thresholds = json_data['thresholds'] - # Should NOT include non-matching thresholds - self.assertNotIn('threshold_no_context', threshold_names) - self.assertNotIn('threshold_for_other_context', threshold_names) + # Should have exactly 1 threshold with no context + self.assertEqual(len(thresholds), 1) + self.assertEqual(thresholds[0]['threshold_name'], 'no_context') + self.assertEqual(thresholds[0]['context'], []) + + def test_duplicate_threshold_handling(self): + """Test that duplicate thresholds are handled correctly.""" + + def get_thresholds(term) -> Iterator[Threshold]: + # Same threshold multiple times + for i in range(3): + yield Threshold( + threshold_name='duplicate_threshold', + term_name=term, + location_name='CPER', + context=['soil', 'ion-content'], + start_date='2024-01-01', + end_date='2024-12-31', + is_date_constrained=True, + start_day_of_year=1, + end_day_of_year=365, + number_value=100.0, + string_value=None + ) + + load_thresholds(get_thresholds, self.out_path, 'term_name', ['soil|ion-content']) + expected_path = self.out_path.joinpath('thresholds.json') + + with open(expected_path, 'r') as threshold_file: + json_data = json.load(threshold_file) + thresholds = json_data['thresholds'] - print("\nMultiple contexts test results:") - print(json.dumps(json_data, indent=2, sort_keys=False)) + # Should only have 1 threshold (duplicates removed) + self.assertEqual(len(thresholds), 1) + self.assertEqual(thresholds[0]['threshold_name'], 'duplicate_threshold') + + # ========== INTEGRATION TESTS (Database access required) ========== + # These tests are skipped by default to avoid requiring database credentials. + # To run them, remove the skip logic, install requirements, provide database credentials via environment variables, then run: + # python3 -m pytest threshold_loader/tests/test_threshold_loader.py -v - @unittest.skip('Integration test skipped due to long process time.') + @unittest.skip("Integration test - requires database access. See comment above for how to run.") def test_main(self): + """Test backward compatibility with single CTXT environment variable.""" self.configure_mount() os.environ['TERM'] = 'veloXaxs|veloYaxs|veloZaxs|veloSoni|tempSoni' os.environ['CTXT'] = '3Dwind' @@ -163,3 +303,17 @@ def test_main(self): threshold_loader_main.main() expected_path = Path(self.out_path, 'thresholds.json') self.assertTrue(expected_path.exists()) + + @unittest.skip("Integration test - requires database access. See comment above for how to run.") + def test_main_new(self): + """Test new multi-context functionality with CTXT_1, CTXT_2, CTXT_3 variables.""" + self.configure_mount() + os.environ['TERM'] = 'VSWCfactoryDepth02|VSICDepth02|VSWCsoilSpecificDepth02' + os.environ['CTXT_1'] = 'soil|ion-content' + os.environ['CTXT_2'] = 'soil|water-content|factory' + os.environ['CTXT_3'] = 'soil|water-content|soil-specific' + os.environ['OUT_PATH'] = str(self.out_path) + os.environ['LOG_LEVEL'] = 'DEBUG' + threshold_loader_main.main() + expected_path = Path(self.out_path, 'thresholds.json') + self.assertTrue(expected_path.exists()) \ No newline at end of file