[docs]
class Node:
def __init__(self, name,record_length_hours = 365*24*19):
self.name = name
self.record_length_hours = record_length_hours
self.children = []
self.parent = []
[docs]
def add_child(self, child_node):
"""Adds a child node to this node's children list."""
self.children.append(child_node)
[docs]
def num_descendants(self):
"""Recursively counts the total number of descendants of this node."""
count = len(self.children)
for child in self.children:
count += child.num_descendants()
return count
[docs]
def add_parent(self, parent_node):
"""Adds a parent node to this node's parent list."""
self.parent.append(parent_node)
[docs]
def num_ancestors(self):
"""Recursively counts the total number of ancestors of this node."""
count = len(self.parent)
for parent in self.parent:
count += parent.num_ancestors()
return count
[docs]
def get_ancestors(self):
"""retrieves the list of ancestors of this node."""
ancestors = []
for parent in self.parent:
ancestors.append(parent)
return ancestors
[docs]
def get_descendants(self):
"""retrieves the list of descendants of this node."""
descendants = []
for child in self.children:
descendants.append(child)
return descendants
[docs]
def gen_constituents_tree():
"""
This function generates the constituent trees of Choice of constituents and Rayleigh comparison pairs
It returns a list of root nodes for subtide, diurnal, semidiurnal and terdiurnal, and a list of shallow
water constituents that have multiple parents. It is implementation from Table 1 to Table 5 in
"MANUAL FOR TIDAL HEIGHTS ANALYSIS AND PREDICTION" by M.G.G. Foreman (1996).
"""
nodes_tree_lst = []
## sub tide tree
tree_sub = Node("Z0",record_length_hours=13)
sa = Node("SA",record_length_hours=8766)
ssa = Node("SSA",record_length_hours=4383)
ssa.add_child(sa)
tree_sub.add_child(ssa)
msm = Node("MSM",record_length_hours=4942)
mm = Node("MM",record_length_hours=764)
mm.add_child(msm)
msf = Node("MSF",record_length_hours=355)
mf = Node("MF",record_length_hours=4383)
msf.add_child(mf)
msf.add_child(mm)
tree_sub.add_child(msf)
## diurnal tree
sig1 = Node("SIG1",record_length_hours=4942)
alp1 = Node("ALP1",record_length_hours=764)
twoq1 = Node("2Q1",record_length_hours=662)
twoq1.add_child(sig1)
twoq1.add_child(alp1)
rho1 = Node("RHO1",record_length_hours=4942)
q1 = Node("Q1",record_length_hours=662)
q1.add_child(rho1)
q1.add_child(twoq1)
o1 = Node("O1",record_length_hours=328)
tau1 = Node("TAU1",record_length_hours=4383)
o1.add_child(q1)
o1.add_child(tau1)
bet1 = Node("BET1",record_length_hours=4383)
chi1 = Node("CHI1",record_length_hours=4942)
no1 = Node("NO1",record_length_hours=662)
no1.add_child(chi1)
no1.add_child(bet1)
pi1= Node("PI1",record_length_hours= 8767)
p1= Node("P1",record_length_hours= 4383)
p1.add_child(pi1)
s1= Node("S1",record_length_hours= 8767)
psi1= Node("PSI1",record_length_hours= 8767)
phi1= Node("PHI1",record_length_hours= 4383)
the1= Node("THE1",record_length_hours= 4942)
j1= Node("J1",record_length_hours= 662)
oo1= Node("OO1",record_length_hours= 651)
ups1= Node("UPS1",record_length_hours= 662)
so1= Node("SO1",record_length_hours= 4383)
oo1.add_child(ups1)
oo1.add_child(so1)
j1.add_child(oo1)
j1.add_child(the1)
k1= Node("K1",record_length_hours= 24)
k1.add_child(o1)
k1.add_child(p1)
k1.add_child(j1)
k1.add_child(no1)
k1.add_child(phi1)
k1.add_child(psi1)
k1.add_child(s1)
## semidiurnal tree
m2 = Node("M2",record_length_hours=13)
oq2 = Node("OQ2",record_length_hours= 4942)
eps2 = Node("EPS2",record_length_hours= 764)
eps2.add_child(oq2)
twon2 = Node("2N2",record_length_hours= 4942)
twon2.add_child(eps2)
mu2 = Node("MU2",record_length_hours= 764)
mu2.add_child(twon2)
nu2 = Node("NU2",record_length_hours= 4942)
n2 = Node("N2",record_length_hours= 662)
n2.add_child(nu2)
n2.add_child(mu2)
gam2= Node("GAM2",record_length_hours= 11326)
h1 = Node("H1",record_length_hours= 8767)
h1.add_child(gam2)
h2 = Node("H2",record_length_hours= 8767)
mks2 = Node("MKS2",record_length_hours= 4383)
m2.add_child(n2)
m2.add_child(h1)
m2.add_child(h2)
m2.add_child(mks2)
s2= Node("S2",record_length_hours= 355)
lda2= Node("LDA2",record_length_hours= 4942)
l2= Node("L2",record_length_hours= 764)
l2.add_child(lda2)
t2= Node("T2",record_length_hours= 8767)
r2= Node("R2",record_length_hours= 8767)
msn2= Node("MSN2",record_length_hours= 4383)
eta2= Node("ETA2",record_length_hours= 662)
eta2.add_child(msn2)
k2= Node("K2",record_length_hours= 4383)
k2.add_child(eta2)
s2.add_child(k2)
s2.add_child(l2)
s2.add_child(t2)
s2.add_child(r2)
## terdiurnal tree
m3 = Node("M3",record_length_hours= 25)
mo3 = Node("MO3",record_length_hours= 656)
m3.add_child(mo3)
so3 = Node("SO3",record_length_hours= 4383)
mk3 = Node("MK3",record_length_hours= 656)
mk3.add_child(so3)
sk3 = Node("SK3",record_length_hours= 355)
mk3.add_child(sk3)
m3.add_child(mk3)
nodes_tree_lst.append(tree_sub)
nodes_tree_lst.append(k1)
nodes_tree_lst.append(m2)
nodes_tree_lst.append(s2)
nodes_tree_lst.append(m3)
## shallow water constituents
shallow_group = []
so1 = Node("SO1",record_length_hours= 4383)
so1.add_parent(s2)
so1.add_parent(o1)
shallow_group.append(so1)
mks2 = Node("MKS2",record_length_hours= 4383)
mks2.add_parent(m2)
mks2.add_parent(k2)
mks2.add_parent(s2)
shallow_group.append(mks2)
msn2 = Node("MSN2",record_length_hours= 4383)
msn2.add_parent(m2)
msn2.add_parent(s2)
msn2.add_parent(n2)
shallow_group.append(msn2)
mo3 = Node("MO3",record_length_hours= 656)
mo3.add_parent(m2)
mo3.add_parent(o1)
shallow_group.append(mo3)
so3 = Node("SO3",record_length_hours= 4383)
so3.add_parent(s2)
so3.add_parent(o1)
shallow_group.append(so3)
mk3 = Node("MK3",record_length_hours= 656)
mk3.add_parent(m2)
mk3.add_parent(k1)
sk3 = Node("SK3",record_length_hours= 355)
sk3.add_parent(s2)
sk3.add_parent(k1)
shallow_group.append(sk3)
mn4 = Node("MN4",record_length_hours= 662)
mn4.add_parent(m2)
mn4.add_parent(n2)
shallow_group.append(mn4)
m4 = Node("M4",record_length_hours= 25)
m4.add_parent(m2)
shallow_group.append(m4)
sn4 = Node("SN4",record_length_hours= 764)
sn4.add_parent(s2)
sn4.add_parent(n2)
shallow_group.append(sn4)
ms4 = Node("MS4",record_length_hours= 355)
ms4.add_parent(m2)
ms4.add_parent(s2)
shallow_group.append(ms4)
mk4 = Node("MK4",record_length_hours= 4383)
mk4.add_parent(m2)
mk4.add_parent(k2)
shallow_group.append(mk4)
s4= Node("S4",record_length_hours= 355)
s4.add_parent(s2)
shallow_group.append(s4)
sk4 = Node("SK4",record_length_hours= 4383)
sk4.add_parent(s2)
sk4.add_parent(k2)
shallow_group.append(sk4)
twomk5 = Node("2MK5",record_length_hours= 24)
twomk5.add_parent(m2)
twomk5.add_parent(k1)
shallow_group.append(twomk5)
twosk5 = Node("2SK5",record_length_hours= 178)
twosk5.add_parent(s2)
twosk5.add_parent(k1)
shallow_group.append(twosk5)
twomn6 = Node("2MN6",record_length_hours= 662)
twomn6.add_parent(m2)
twomn6.add_parent(n2)
shallow_group.append(twomn6)
m6 = Node("M6",record_length_hours= 26)
m6.add_parent(m2)
shallow_group.append(m6)
twoms6 = Node("2MS6",record_length_hours= 355)
twoms6.add_parent(m2)
twoms6.add_parent(s2)
shallow_group.append(twoms6)
twomk6 = Node("2MK6",record_length_hours= 4383)
twomk6.add_parent(m2)
twomk6.add_parent(k2)
shallow_group.append(twomk6)
twosm6= Node("2SM6",record_length_hours= 355)
twosm6.add_parent(s2)
twosm6.add_parent(m2)
shallow_group.append(twosm6)
msk6 = Node("MSK6",record_length_hours= 4383)
msk6.add_parent(m2)
msk6.add_parent(s2)
msk6.add_parent(k2)
shallow_group.append(msk6)
threemk7 = Node("3MK7",record_length_hours= 24)
threemk7.add_parent(m2)
threemk7.add_parent(k1)
shallow_group.append(threemk7)
m8 = Node("M8",record_length_hours= 26)
m8.add_parent(m2)
shallow_group.append(m8)
return nodes_tree_lst, shallow_group
[docs]
def get_constituents(record_length_hours=0):
"""
Retrieves the names of all constituents in the astro_group and shallow_group
that can be analyazed by the specified record_length_hours.
Args:
astro_group: list of root nodes for the main constituent groups (slow, diurnal, semidiurnal, terdiurnal).
shallow_group: list of nodes for shallow water constituents that have multiple parents.
record_length_hours: the length of the record in hours, used to determine which constituents can be analyzed.
Returns: A sorted list of constituent names that can be analyzed with the given record length.
"""
astro_group,shallow_group=gen_constituents_tree()
selected_constituents = set()
selected_constituents=get_astro_constituent_names(astro_group, record_length_hours)
for node in shallow_group:
if node.record_length_hours <= record_length_hours:
## get ancestors of this node and check if they satisfy the record length requirement
ancestors = node.get_ancestors()
all_ancestor_included = True
for ancestor in ancestors:
if ancestor.name not in selected_constituents:
all_ancestor_included = False
break
if all_ancestor_included:
selected_constituents.append(node.name)
return sorted(selected_constituents)
[docs]
def get_astro_constituent_names(astro_group, record_length_hours=0):
"""
Recursivly Retrieves the names of all constituents in the astro_group that can be
analyazed by the specified record_length_hours.
Args:
astro_group: list of root nodes for the main constituent groups (slow, diurnal, semidiurnal, terdiurnal).
record_length_hours: the length of the record in hours, used to determine which constituents can be analyzed.
Returns: A sorted list of constituent names that can be analyzed with the given record length.
"""
selected_constituents = set()
for root in astro_group:
if root.record_length_hours <= record_length_hours:
selected_constituents.add(root.name)
descendants = root.get_descendants()
stack = descendants[:]
while stack:
node = stack[0]
stack = stack[1:]
if node.record_length_hours <= record_length_hours:
if (not (node.name in selected_constituents)) :
selected_constituents.add(node.name)
stack.extend(node.get_descendants())
return sorted(selected_constituents)
record_length_hours = 5200
selected_constituents = get_constituents(record_length_hours)