Skip to content

Commit 2f2739c

Browse files
committed
Update tidal constituents normalize funcs
Move those two funcs out of wirte funcs
1 parent 0bdf6cf commit 2f2739c

1 file changed

Lines changed: 195 additions & 78 deletions

File tree

schimpy/bctide.py

Lines changed: 195 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -185,93 +185,210 @@ def __init__(self, hgrid,bc_yaml=None,boundary_segments=None):
185185
"none": 0,
186186
}
187187

188-
def write_bctides(self, bctides_file):
189-
190-
with open(bctides_file, "w") as outf:
191-
tt = self.date.strftime("%Y-%m-%d %H:%M")
192-
outf.write(tt)
193-
outf.write("\n")
194-
# normalize tidal-constituent input formats so downstream code can
195-
# expect a list of dicts with explicit keys.
196-
def _norm_earth(consts):
197-
out = []
198-
for item in consts:
199-
if isinstance(item, dict):
200-
# either {"name":..., "amplitude":..., ...} or {"K1": [..]}
201-
if "name" in item:
202-
name = item.get("name")
203-
amp = item.get("amplitude", item.get("amp", 0.0))
204-
node = item.get("node_factor", 1.0)
205-
freq = item.get("angular_frequency", item.get("frequency", 0.0))
206-
eqa = item.get("earth_equilibrium_argument", item.get("eqa", 0.0))
207-
else:
208-
key = next(iter(item))
209-
vals = item[key]
210-
name = key
211-
if isinstance(vals, (list, tuple)):
212-
# support common lengths used in examples
213-
if len(vals) == 5:
214-
# example: [?, amplitude, freq, node_factor, eqa]
215-
amp = vals[1]
216-
freq = vals[2]
217-
node = vals[3]
218-
eqa = vals[4]
219-
elif len(vals) == 4:
220-
# example: [amp, freq, node, eqa]
221-
amp = vals[0]
222-
freq = vals[1]
223-
node = vals[2]
224-
eqa = vals[3]
225-
else:
226-
amp = vals[0] if len(vals) > 0 else 0.0
227-
freq = vals[2] if len(vals) > 2 else 0.0
228-
node = vals[3] if len(vals) > 3 else 1.0
229-
eqa = vals[-1] if len(vals) > 0 else 0.0
230-
else:
231-
# unsupported format -> skip
232-
continue
233-
else:
234-
continue
235-
out.append({
188+
def _norm_earth(consts):
189+
out = []
190+
for item in consts:
191+
if not isinstance(item, dict):
192+
# unsupported format -> skip
193+
continue
194+
195+
# case: explicit dict with "name" key
196+
if "name" in item:
197+
name = item.get("name")
198+
amp = item.get("amplitude", item.get("amp", 0.0))
199+
node = item.get("node_factor", item.get("node", 1.0))
200+
freq = item.get("angular_frequency", item.get("frequency", 0.0))
201+
eqa = item.get(
202+
"earth_equilibrium_argument",
203+
item.get("eqa", item.get("phase", 0.0)),
204+
)
205+
out.append(
206+
{
236207
"name": name,
237208
"amplitude": amp,
238209
"node_factor": node,
239210
"angular_frequency": freq,
240211
"earth_equilibrium_argument": eqa,
241-
})
242-
return out
243-
244-
def _norm_boundary(consts):
245-
out = []
246-
for item in consts:
247-
if isinstance(item, dict):
248-
if "name" in item:
249-
name = item.get("name")
250-
freq = item.get("angular_frequency", item.get("frequency", 0.0))
251-
node = item.get("node_factor", 1.0)
252-
eqa = item.get("earth_equilibrium_argument", item.get("eqa", 0.0))
253-
else:
254-
key = next(iter(item))
255-
vals = item[key]
256-
name = key
257-
if isinstance(vals, (list, tuple)):
258-
if len(vals) >= 3:
259-
freq, node, eqa = vals[0], vals[1], vals[2]
260-
else:
261-
freq = vals[0] if len(vals) > 0 else 0.0
262-
node = vals[1] if len(vals) > 1 else 1.0
263-
eqa = vals[-1] if len(vals) > 0 else 0.0
264-
else:
265-
continue
266-
else:
267-
continue
268-
out.append({
212+
}
213+
)
214+
continue
215+
216+
# case: mapping like {K1: {...}} or {K1: [..]}
217+
key = next(iter(item))
218+
vals = item[key]
219+
name = key
220+
221+
if isinstance(vals, dict):
222+
amp = vals.get("amplitude", vals.get("amp", 0.0))
223+
node = vals.get("node_factor", vals.get("node", 1.0))
224+
freq = vals.get("angular_frequency", vals.get("frequency", 0.0))
225+
eqa = vals.get(
226+
"earth_equilibrium_argument",
227+
vals.get("eqa", vals.get("phase", 0.0)),
228+
)
229+
out.append(
230+
{
269231
"name": name,
232+
"amplitude": amp,
233+
"node_factor": node,
270234
"angular_frequency": freq,
235+
"earth_equilibrium_argument": eqa,
236+
}
237+
)
238+
continue
239+
240+
if isinstance(vals, (list, tuple)):
241+
# support common list formats used historically
242+
if len(vals) == 5:
243+
# example: [?, amplitude, freq, node_factor, eqa]
244+
amp = vals[1]
245+
freq = vals[2]
246+
node = vals[3]
247+
eqa = vals[4]
248+
elif len(vals) == 4:
249+
# example: [amp, freq, node, eqa]
250+
amp = vals[0]
251+
freq = vals[1]
252+
node = vals[2]
253+
eqa = vals[3]
254+
else:
255+
amp = vals[0] if len(vals) > 0 else 0.0
256+
freq = vals[2] if len(vals) > 2 else 0.0
257+
node = vals[3] if len(vals) > 3 else 1.0
258+
eqa = vals[-1] if len(vals) > 0 else 0.0
259+
out.append(
260+
{
261+
"name": name,
262+
"amplitude": amp,
271263
"node_factor": node,
264+
"angular_frequency": freq,
272265
"earth_equilibrium_argument": eqa,
273-
})
274-
return out
266+
}
267+
)
268+
continue
269+
270+
# unsupported nested format -> skip
271+
continue
272+
273+
return out
274+
275+
276+
def _norm_boundary(consts):
277+
"""
278+
Normalize boundary (forcing) tidal constituent specifications into list of dicts with keys:
279+
name, angular_frequency, node_factor, earth_equilibrium_argument
280+
281+
Supported input shapes (from YAML examples):
282+
- explicit dict with "name": {...}
283+
- mapping like {O1: {...}} or {O1: [freq, node, eqa]}
284+
- list/tuple forms [freq, node, eqa] when provided as the value for a key
285+
- simple string -> defaults
286+
"""
287+
out = []
288+
for item in consts or []:
289+
try:
290+
# explicit dict with 'name'
291+
if isinstance(item, dict) and "name" in item:
292+
name = item.get("name")
293+
freq = item.get("angular_frequency", item.get("frequency", 0.0))
294+
node = item.get("node_factor", item.get("node", 1.0))
295+
eqa = item.get(
296+
"earth_equilibrium_argument", item.get("eqa", item.get("phase", 0.0))
297+
)
298+
out.append(
299+
{
300+
"name": name,
301+
"angular_frequency": freq,
302+
"node_factor": node,
303+
"earth_equilibrium_argument": eqa,
304+
}
305+
)
306+
continue
307+
308+
# mapping {NAME: {...}} or {NAME: [...]}
309+
if isinstance(item, dict):
310+
key = next(iter(item))
311+
vals = item[key]
312+
name = key
313+
314+
if isinstance(vals, dict):
315+
freq = vals.get("angular_frequency", vals.get("frequency", 0.0))
316+
node = vals.get("node_factor", vals.get("node", 1.0))
317+
eqa = vals.get(
318+
"earth_equilibrium_argument", vals.get("eqa", vals.get("phase", 0.0))
319+
)
320+
out.append(
321+
{
322+
"name": name,
323+
"angular_frequency": freq,
324+
"node_factor": node,
325+
"earth_equilibrium_argument": eqa,
326+
}
327+
)
328+
continue
329+
330+
if isinstance(vals, (list, tuple)):
331+
if len(vals) >= 3:
332+
freq, node, eqa = vals[0], vals[1], vals[2]
333+
else:
334+
# partial lists: fill with defaults
335+
freq = vals[0] if len(vals) > 0 else 0.0
336+
node = vals[1] if len(vals) > 1 else 1.0
337+
eqa = vals[-1] if len(vals) > 0 else 0.0
338+
out.append(
339+
{
340+
"name": name,
341+
"angular_frequency": freq,
342+
"node_factor": node,
343+
"earth_equilibrium_argument": eqa,
344+
}
345+
)
346+
continue
347+
348+
# vals is scalar (frequency) or unsupported -> try to use as frequency
349+
if isinstance(vals, (int, float)):
350+
out.append(
351+
{
352+
"name": name,
353+
"angular_frequency": vals,
354+
"node_factor": 1.0,
355+
"earth_equilibrium_argument": 0.0,
356+
}
357+
)
358+
continue
359+
360+
# otherwise skip
361+
continue
362+
363+
# plain string -> treat as name with defaults
364+
if isinstance(item, str):
365+
out.append(
366+
{
367+
"name": item,
368+
"angular_frequency": 0.0,
369+
"node_factor": 1.0,
370+
"earth_equilibrium_argument": 0.0,
371+
}
372+
)
373+
continue
374+
375+
except Exception:
376+
# skip malformed entries
377+
continue
378+
379+
return out
380+
381+
382+
def write_bctides(self, bctides_file):
383+
384+
with open(bctides_file, "w") as outf:
385+
tt = self.date.strftime("%Y-%m-%d %H:%M")
386+
outf.write(tt)
387+
outf.write("\n")
388+
# normalize tidal-constituent input formats so downstream code can
389+
# normalize tidal-constituent input formats so downstream code can
390+
# expect a list of dicts with explicit keys.
391+
275392

276393
if self.earth_tidals and "tidal_constituents" in self.earth_tidals:
277394
self.earth_tidals["tidal_constituents"] = _norm_earth(

0 commit comments

Comments
 (0)