Skip to content

Commit e2bde9a

Browse files
EliEli
authored andcommitted
Add station_output section for creating station.in, eliminated some prints
1 parent e5c6e7f commit e2bde9a

2 files changed

Lines changed: 110 additions & 9 deletions

File tree

schimpy/prepare_schism.py

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
from schimpy.small_areas import small_areas
1010
from schimpy.split_quad import split_quad
1111
from schimpy import schism_yaml
12+
from schimpy import station as station_mod
1213
from schimpy.create_vgrid_lsc2 import vgrid_gen
1314
from schimpy.mesh_volume_tvd import (
1415
refine_volume_tvd,
1516
ShorelineOptions,
1617
FloorOptions,
1718
TVDOptions,
1819
)
20+
import dms_datastore.dstore_config as configs
1921
from packaging.version import Version, InvalidVersion
2022
import datetime
2123
import importlib
@@ -456,6 +458,109 @@ def create_fluxflag(s, inputs, logger):
456458
buf = "{}\n".format(line["name"])
457459
f.write(buf)
458460

461+
def _validate_station_request(request, logger):
462+
"""Return a normalized request list validated against station_mod.station_variables."""
463+
allowed = set(station_mod.station_variables) # elev, salt, etc.
464+
if isinstance(request, str):
465+
if request.lower() == "all":
466+
return "all"
467+
request_list = [request]
468+
else:
469+
request_list = list(request)
470+
# allow 'all' anywhere
471+
if any(str(x).lower() == "all" for x in request_list):
472+
return "all"
473+
bad = [x for x in request_list if x not in allowed]
474+
if bad:
475+
logger.error(f"station_output.request contains unknown variables: {bad}. Allowed: {sorted(allowed)}")
476+
raise ValueError("Invalid station_output.request")
477+
return request_list
478+
479+
def create_station_output(inputs, logger):
480+
"""Create/aggregate station.in from the new top-level 'station_output' section."""
481+
section = inputs.get("station_output")
482+
if section is None:
483+
return
484+
# Accept either key (common misspelling safeguard)
485+
station_in_list = section.get("station_in_files")
486+
487+
name = section.get("name", "station.in")
488+
request = _validate_station_request(section.get("request", "all"), logger)
489+
out_fpath = ensure_outdir(inputs["prepro_output_dir"], name)
490+
491+
# If ANY entry is 'GENERATE' (case-insensitive), create a temp file from DBs,
492+
# then (optionally) concatenate with any additional provided files.
493+
generated_tmp = None
494+
if station_in_list is not None and any(str(x).strip().upper() == "GENERATE" for x in station_in_list):
495+
logger.info("station_output: GENERATE requested (will merge with other files if provided)")
496+
station_db = configs.config_file("station_dbase")
497+
subloc_db = configs.config_file("sublocations")
498+
if station_db is None or subloc_db is None:
499+
raise ValueError(
500+
"station_output: Could not resolve default station databases. "
501+
"Check dms_datastore config for 'station_dbase' and 'sublocations'."
502+
)
503+
# Write to a temp file inside prepro_output_dir so we can include it in the concat
504+
generated_tmp = ensure_outdir(inputs["prepro_output_dir"], "__station_generated.in")
505+
logger.info(f"station_output: GENERATE -> writing temp {generated_tmp}")
506+
station_mod.convert_db_station_in(
507+
outfile=generated_tmp,
508+
stationdb=station_db,
509+
sublocdb=subloc_db,
510+
station_request=request,
511+
default=-0.5,
512+
)
513+
514+
# Mode 2: concatenate listed station.in files
515+
import pandas as pd
516+
dfs = []
517+
if not station_in_list:
518+
raise ValueError("station_output.station_in_files must be provided (or include 'GENERATE').")
519+
# Build the effective list: generated tmp (if any) + all explicit files except the 'GENERATE' token
520+
files_to_read = []
521+
if generated_tmp is not None:
522+
files_to_read.append(generated_tmp)
523+
files_to_read.extend([f for f in station_in_list if str(f).strip().upper() != "GENERATE"])
524+
525+
for f in files_to_read:
526+
f = os.path.expanduser(str(f))
527+
if not os.path.isabs(f):
528+
# allow relative to the launch YAML directory or CWD—leave as-is
529+
pass
530+
if not os.path.exists(f):
531+
logger.error(f"station_output: cannot find file '{f}'")
532+
raise ValueError(f"station_output file not found: {f}")
533+
logger.info(f"station_output: reading {f}")
534+
dfs.append(station_mod.read_station_in(f))
535+
536+
if not dfs:
537+
raise ValueError("station_output: no station files to merge.")
538+
539+
merged = pd.concat(dfs)
540+
# Strict check: fail if any (id, subloc) appears more than once
541+
dup_mask = merged.index.duplicated(keep=False)
542+
if dup_mask.any():
543+
# Summarize the first few duplicates for a helpful error
544+
dup_keys = merged.index[dup_mask]
545+
# unique but preserve order
546+
seen = set()
547+
uniq_dups = [k for k in dup_keys if not (k in seen or seen.add(k))]
548+
sample = ", ".join([f"{k[0]}::{k[1]}" for k in uniq_dups[:10]])
549+
more = "" if len(uniq_dups) <= 10 else f" (+{len(uniq_dups)-10} more)"
550+
raise ValueError(
551+
"station_output: duplicate (station, sublocation) entries detected; "
552+
f"please remove redundancy. Examples: {sample}{more}"
553+
)
554+
555+
# Write final station.in with the requested variables; write_station_in handles row renumbering
556+
logger.info(f"station_output: writing merged file -> {out_fpath}")
557+
station_mod.write_station_in(out_fpath, merged, request=request)
558+
# Clean up generated temp if we made one
559+
if generated_tmp is not None:
560+
try:
561+
os.remove(generated_tmp)
562+
except Exception:
563+
logger.warning(f"station_output: could not remove temp file {generated_tmp}")
459564

460565
def update_spatial_inputs(s, inputs, logger):
461566
"""Create SCHISM grid inputs.
@@ -474,6 +579,7 @@ def update_spatial_inputs(s, inputs, logger):
474579
create_prop_with_polygons(s, inputs, logger)
475580
create_structures(s, inputs, logger)
476581
create_fluxflag(s, inputs, logger)
582+
create_station_output(inputs, logger)
477583

478584

479585
def update_temporal_inputs(s, inputs):
@@ -608,6 +714,7 @@ def process_prepare_yaml(in_fname, use_logging=True):
608714
"hydraulics",
609715
"sources_sinks",
610716
"flow_outputs",
717+
"station_output",
611718
"copy_resources",
612719
] + schism_yaml.include_keywords
613720
logger.info("Processing the top level...")
@@ -659,8 +766,9 @@ def prepare_schism(args, use_logging=True):
659766
if item_exist(inputs, "copy_resources"):
660767
logger.info("Copying resources to output dir")
661768
copy_spec = inputs["copy_resources"]
769+
prepro_outdir = inputs["prepro_output_dir"]
662770
for key, item in copy_spec.items():
663-
outpath = os.path.join(outdir, item)
771+
outpath = ensure_outdir(prepro_outdir, item)
664772
if os.path.normpath(key) == os.path.normpath(outpath):
665773
continue
666774
logger.info(f"copy_resources: copying {key} to {outpath}")

schimpy/schism_setup.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ def elements_on_linestring(self, coords):
235235
"""List elements along linestring"""
236236
mesh = self._input.mesh
237237
line_segment = np.array(coords, dtype="d").flatten()
238-
print(line_segment)
239238
return mesh.find_intersecting_elems_with_line(line_segment)
240239

241240
def create_flux_regions(self, linestrings, out_fname="fluxflag.prop"):
@@ -657,13 +656,7 @@ def apply_polygons(self, polygons, default):
657656
except:
658657
self._logger.error("Polygon failed to evaluate: {}".format(name))
659658
raise
660-
# if 97346 in nodes_sel:
661-
# print(poly.type)
662-
# print("Arrived in poly {}".format(name))
663-
# for ii,isel in enumerate(nodes_sel):
664-
# if isel == 97346:
665-
# print(ii,isel,mesh.nodes[isel,:],vals[ii])
666-
# #raise ValueError("Arrived. Poly type = {}".format(poly.type))
659+
667660

668661
if nodes_sel is None or not len(nodes_sel):
669662
msg = "This polygon contains no nodes: %s" % poly.name

0 commit comments

Comments
 (0)