Commit 37a3d6a9 authored by Claude Lacoursiere's avatar Claude Lacoursiere

updated pygo

parent 33748b16
Pipeline #1074 passed with stages
in 22 minutes and 21 seconds
...@@ -8,6 +8,7 @@ from scipy import sparse ...@@ -8,6 +8,7 @@ from scipy import sparse
from scipy.linalg import expm from scipy.linalg import expm
import hashlib import hashlib
import pickle import pickle
import timeit
def join_struct_arrays2(arrays): def join_struct_arrays2(arrays):
newdtype = sum((a.dtype.descr for a in arrays), []) newdtype = sum((a.dtype.descr for a in arrays), [])
...@@ -31,7 +32,8 @@ def pack_data(t, x, v): ...@@ -31,7 +32,8 @@ def pack_data(t, x, v):
for i in range(0, v.shape[1]): for i in range(0, v.shape[1]):
newrecarray["v%d" %(i+1)] = v[:, i] newrecarray["v%d" %(i+1)] = v[:, i]
return newrecarray return newrecarray
def pickle_data(t, x, v, config):
def pickle_data(config, x, v, t):
l = {"t" : t, "x" : x, "v" : v, "config" : config} l = {"t" : t, "x" : x, "v" : v, "config" : config}
h = hashlib.md5(str(config).encode("utf8")).hexdigest() h = hashlib.md5(str(config).encode("utf8")).hexdigest()
fname = h + ".pickle" fname = h + ".pickle"
...@@ -40,7 +42,20 @@ def pickle_data(t, x, v, config): ...@@ -40,7 +42,20 @@ def pickle_data(t, x, v, config):
f.close() f.close()
return fname return fname
def get_pickle_data(t, config): class ref_data():
def __init__(self, t, x, v):
self.d = t
self.x = x
self.v = v
def interp(t):
self.ts = t
self.xs = interp.interp1d(d["t"], d["x"], kind="cubic", axis=0)(t)
self.xv = interp.interp1d(d["t"], d["v"], kind="cubic", axis=0)(t)
return self
def get_pickle_data(config, t=False):
""" Either open a file with the data or run reference simulation. """ Either open a file with the data or run reference simulation.
In all cases, the data is interpolated to the given time array. In all cases, the data is interpolated to the given time array.
""" """
...@@ -57,9 +72,7 @@ def get_pickle_data(t, config): ...@@ -57,9 +72,7 @@ def get_pickle_data(t, config):
except: except:
if float(config["k"]) == float(0): if float(config["k"]) == float(0):
s = spring_chain(config["init"], config["masses"], config["isprings"]) s = spring_chain(config["init"], config["masses"], config["isprings"])
print("doing ref")
else: else:
print("doing split")
s = split_string(config["step"], config["init"], config["masses"], s = split_string(config["step"], config["init"], config["masses"],
config["isprings"], k=config["k"]) config["isprings"], k=config["k"])
...@@ -69,8 +82,9 @@ def get_pickle_data(t, config): ...@@ -69,8 +82,9 @@ def get_pickle_data(t, config):
f = open(fname, "wb") f = open(fname, "wb")
pickle.dump(d , f) pickle.dump(d , f)
f.close() f.close()
data = ref_data(d["t"], d["x"], d["v"])
return interp.interp1d(d["t"], d["x"], kind="cubic", axis=0)(t), interp.interp1d(d["t"], d["v"], kind="cubic", axis=0)(t), d if type(t) == np.ndarray:
return data.interp(t if type(t) == np.array data.t)
""" """
implementation of a spring damper chain with alternating spring damper implementation of a spring damper chain with alternating spring damper
...@@ -225,6 +239,7 @@ class spring(): ...@@ -225,6 +239,7 @@ class spring():
self.data = self.collector(self, self.z0) self.data = self.collector(self, self.z0)
#if True:
if h == 0.0: if h == 0.0:
self.r.set_solout(self.data) self.r.set_solout(self.data)
self.r.integrate(tend) self.r.integrate(tend)
...@@ -342,16 +357,80 @@ class spring_chain(spring): ...@@ -342,16 +357,80 @@ class spring_chain(spring):
self.z0 = z0 self.z0 = z0
self.t0 = t0 self.t0 = t0
super().doit(tend, t0=self.t0) super().doit(tend, t0=self.t0, h=h)
def __call__(self, t, z):
return self.A * z
def make_config_uniform(N, step=float(0), k=float(3), tend = -1):
print("Fucking step = %g and k = %g" % (step, k))
init = np.array([[1,0,0,0]] + [[0,0,0,0]]*(N-1))
masses = np.array([[1,2]]*N) / N
isprings = np.array([[1,0]]*N)
mu = 1.0/ ( 1/ masses[0][0] + 1/masses[0][1])
min_p, max_p = get_min_max_period(masses, isprings)
if k > 0 and step > 0 :
c = mu*(k*2*math.pi/step)**2
d = 2*0.7*math.sqrt(c*mu)
csprings = [[c,d,0,0]]*N
csprings[0] = [0,0,0,0]
else:
csprings = [[0,0,0,0]]*N
config = {"N" : int(N),
"init" : init,
"masses" : masses,
"isprings" : isprings,
"csprings" : csprings,
"step" : step,
"k" : float(k),
"tend" : float(tend),
"min_p" : float(min_p),
"step" : min_p/20,
"tend" : 2*max_p if tend == -1 else tend
}
config["step"] = step if step > 0 else min_p / 10.0
return config
def measure_perf(m, N = 4, factor = 2):
n = []
t = []
S = []
C = []
tend = 10
for i in range(1,m):
c = make_config_uniform(N)
c["tend"] = tend
C += [c]
n += [ N ]
s = spring_chain(c["init"],c["masses"], c["isprings"])
S += [s]
u = timeit.Timer(lambda: s.doit(c["tend"], h=c["step"]))
t += [ u.timeit(number=1) ]
N *= factor
t = np.array(t)
n = np.array(n)
try:
sl = np.polyfit(np.log10(t), np.log10(n), 1)[1]
py.clf()
py.loglog(t, n)
py.legend(["exponent = %g" %sl])
py.title("Computational complexity")
py.show()
except:
pass
return t, n, S, C
def __call__(self, t, z):
return self.A * z
if __name__ == "__main__": if __name__ == "__main__":
N = 10 if not ( ( 'N' in vars() or 'N' in globals() ) and type(N) == type(0) ):
N = 10
init = np.array([[1,0,0,0]] + [[0,0,0,0]]*(N-1)) init = np.array([[1,0,0,0]] + [[0,0,0,0]]*(N-1))
masses = np.array([[1,2]]*N) masses = np.array([[1,2]]*N)
isprings = np.array([[1,0]]*N) isprings = np.array([[1,0]]*N)
...@@ -371,22 +450,18 @@ if __name__ == "__main__": ...@@ -371,22 +450,18 @@ if __name__ == "__main__":
"min_p" : float(min_p), "min_p" : float(min_p),
"step" : min_p/20 "step" : min_p/20
} }
eps = 1e-16 # machine precision eps = 1e-16 # machine precision
min_p, max_p = get_min_max_period(masses, isprings) min_p, max_p = get_min_max_period(masses, isprings)
tend = 2*max_p tend = 2*max_p
NPP = 10 NPP = 10
step = min_p / NPP step = min_p / NPP
if True: #s1 = spring_chain(config["init"],config["masses"], config["isprings"])
s1 = spring_chain(config["init"],config["masses"], config["isprings"]) #s1.doit(config["tend"], h=config["step"])
if False:
s0 = split_string(config["step"], config["init"],config["masses"], s0 = split_string(config["step"], config["init"],config["masses"],
config["isprings"], k=config["k"]) config["isprings"], k=config["k"])
s0.doit(config["tend"], h=config["step"]) #s0.doit(config["tend"], h=config["step"])
t0 = np.linspace(10, s0.t[-1], 200) t0 = np.linspace(10, s0.t[-1], 200)
s0.sample(t0) s0.sample(t0)
py.figure(1) py.figure(1)
......
...@@ -6,61 +6,41 @@ import hashlib ...@@ -6,61 +6,41 @@ import hashlib
implementation of a spring damper chain with alternating spring damper implementation of a spring damper chain with alternating spring damper
constants. constants.
""" """
config = {}
N = 10
init = np.array([[1,0,0,0]] + [[0,0,0,0]]*(N-1))
masses = np.array([[1,2]]*N)
isprings = np.array([[1,0]]*N)
max_samples= 1000
k = 3
mu = 1.0/ ( 1/ masses[0][0] + 1/masses[0][1])
min_p, max_p = refchain.get_min_max_period(masses, isprings)
tend = 3*max_p
config = {"N" : int(N),
"init" : init,
"masses" : masses,
"isprings" : isprings,
"k" : float(k),
"tend" : float(tend),
"min_period" : float(min_p),
"max_period" : float(max_p),
}
##
## We want to prune the following case:
##
orders = 1
factor = 5.0
npp = [10]
step = min_p / float(npp[0])
for f in range(1,orders+1):
for kinematic in [True, False]:
for filtered in [True, False]:
for parallel in [True, False]:
# kinematic and not parallel or kinematic and filtered make no sense
if not (kinematic and filtered ) and not (kinematic and not parallel):
if not kinematic:
config["k"] = k
c = mu*(k*2*math.pi/step)**2
d = 2*0.7*math.sqrt(c*mu)
csprings = [[c,d,0,0]]*N
csprings[0] = [0,0,0,0]
else:
csprings = [[0,0,0,0]]*N
config["k"] = float(0)
config["step"] = step
config["npp"] = npp[-1]
sim = chain_mass_springs(filtered, N, init, masses, isprings, csprings, kinematic=kinematic)
sim.annotations += ("confighash", hashlib.md5(str(config).encode("utf8")).hexdigest()),
sim.annotations += ("config", config),
try:
sim.simulate(tend, step, mode="mpi", parallel=parallel, datafile="chain", max_samples = max_samples)
except:
pass
step /= factor
npp += [npp[-1]*int(factor)]
def doit( N, fname, orders, k=3, factor=2, max_samples= 1000):
npp = 10 # safe reference start value
## get an initial estimate of the "reasonable" time step
config = refchain.make_config_uniform(N, k=0.0, step=0.0)
step = config["step"]
print(step)
for f in range(1,orders+1):
for kinematic in [True, False]:
for filtered in [True, False]:
for parallel in [True, False]:
if not (kinematic and filtered ) and not (kinematic and not parallel):
k0 = float(0) if kinematic else k
config = refchain.make_config_uniform(N, k=float(0)) if kinematic else refchain.make_config_uniform(N, step=step, k=k)
sim = chain_mass_springs(filtered, N, config["init"], config["masses"], config["isprings"],
config["csprings"], kinematic=kinematic)
sim.annotations += ("confighash", hashlib.md5(str(config).encode("utf8")).hexdigest()),
sim.annotations += ("config", config),
sim.annotations += ("npp", npp),
try:
sim.simulate(config["tend"], step,
mode="mpi", parallel=parallel, datafile=fname, max_samples = max_samples)
refchain.get_pickle_data(config)
except:
pass
step /= factor
npp *= int(factor)
N = 10
k = 3
orders = 3
doit(N, "chain", orders, factor=2, max_samples= 1000)
...@@ -5,7 +5,7 @@ import math ...@@ -5,7 +5,7 @@ import math
umit="../../release/umit-fmus/" umit="../../release/umit-fmus/"
pfmu = umit + "meWrapper/springs2/wrapper_springs2.fmu" pfmu = umit + "meWrapper/springs2/wrapper_springs2.fmu"
ffmu = umit + "meWrapperFiltered/springs2/wrapper_filtered_springs2.fmu" ffmu = umit + "meWrapperFiltered/springs2/wrapper_filtered_springs2.fmu"
cfmu = umit + "gsl/clutch/clutch.fmu" cfmu = umit + "gsl2/clutch2/clutch2.fmu"
class clutch(module): class clutch(module):
""" Modules with two point masses hooked by a spring. This was first """ Modules with two point masses hooked by a spring. This was first
...@@ -78,7 +78,9 @@ class two_bodies(module): ...@@ -78,7 +78,9 @@ class two_bodies(module):
### Now define simulations ### Now define simulations
def make_parameters(filtered, N, init, masses, isprings, csprings, kinematic=False): def make_parameters(filtered, N, init,
masses, isprings, csprings,
kinematic=False, order=0):
""" """
Do this as a function so we can reuse this mess for both force-velocity Do this as a function so we can reuse this mess for both force-velocity
and kinematic couplings. Both types of couplings are returned. and kinematic couplings. Both types of couplings are returned.
...@@ -123,11 +125,13 @@ def make_parameters(filtered, N, init, masses, isprings, csprings, kinematic=Fal ...@@ -123,11 +125,13 @@ def make_parameters(filtered, N, init, masses, isprings, csprings, kinematic=Fal
# couplings+= ("m%d" %i, "x1", "v1", "a1", "f1", # couplings+= ("m%d" %i, "x1", "v1", "a1", "f1",
# "m%d" %(i+1), "x2","v2","a2","f2"), # "m%d" %(i+1), "x2","v2","a2","f2"),
else: else:
for i in range(N-1): if order == 0:
# this is hard wired to go left to right, for i in range(N-1):
# output velocity, input forces signals += ("m%d" %i, "out_v2", "m%d" %(i+1), "in_v1"),
signals += ("m%d" %i, "out_v2", "m%d" %(i+1), "in_v1"), signals += ("m%d" %(i+1), "out_f1", "m%d" %(i), "in_f2"),
signals += ("m%d" %(i+1), "out_f1", "m%d" %(i), "in_f2"), else:
signals += ("m%d" %i, "out_f2", "m%d" %(i+1), "in_v1"),
signals += ("m%d" %(i+1), "out_v1", "m%d" %(i), "in_v2"),
return fmus, couplings, signals return fmus, couplings, signals
...@@ -146,12 +150,13 @@ class chain_mass_springs(simulation): ...@@ -146,12 +150,13 @@ class chain_mass_springs(simulation):
""" """
def __init__(self, filtered, N, init, masses, isprings, csprings, def __init__(self, filtered, N, init, masses, isprings, csprings,
kinematic = False, annotations=()): kinematic = False, annotations=(), order=0):
fmus, couplings, signals = make_parameters(filtered, N, init, fmus, couplings, signals = make_parameters(filtered, N, init,
masses, isprings, masses, isprings,
csprings, csprings,
kinematic=kinematic) kinematic=kinematic,
order = order)
simulation.__init__(self, fmus, simulation.__init__(self, fmus,
name = "me_wrapped_spring_damper", name = "me_wrapped_spring_damper",
......
...@@ -7,8 +7,8 @@ import pickle as pickle ...@@ -7,8 +7,8 @@ import pickle as pickle
import numpy as np import numpy as np
eps=1e-16
## this to get proper labels on plots
rcParams['text.latex.preamble'] = ["\\usepackage{amsmath}"] rcParams['text.latex.preamble'] = ["\\usepackage{amsmath}"]
rcParams['text.latex.preview'] = False rcParams['text.latex.preview'] = False
rcParams['text.latex.unicode'] = False rcParams['text.latex.unicode'] = False
...@@ -16,25 +16,32 @@ rcParams['text.usetex'] = True ...@@ -16,25 +16,32 @@ rcParams['text.usetex'] = True
def count_steps(data):
""" Determines how many different communication steps there are """
d = {}
for i in data:
d[i.get_attr("comm_step")] = i.get_attr("comm_step")
i.get_attr("comm_step")
return len(d)
def get_length(s): def get_length(s):
x = re.search("[\d]+$", s) """ Determine the length of the string from it's name which is
# there can be only one or no match here prepended as:
n = 0 "foobar%d" % N
if x : """
n = int(x.group(0)) x = re.search("[\d]+$", s)
return n # there can be only one or no match here
n = 0
if x :
n = int(x.group(0))
return n
class match_config(): class match_config():
"""Provide a functor to see if two configurations are equivalent for a
given number of fields. This is used when traversing the H5 file to
select simulations to be analyzed.
In the constructor, `c' is the reference config and `f' is a *list* of
fields to match.
There is an issue: there is no == operator for ndarrays at least, but
other objects might also have the same problem.
"""
def __init__(self, c, f): def __init__(self, c, f):
self.config = c self.config = c
self.fields = f # a list self.fields = f # a list
...@@ -64,64 +71,36 @@ def get_simulations(fname, config ): ...@@ -64,64 +71,36 @@ def get_simulations(fname, config ):
initial conditions, spring constants, and 'k' factor. initial conditions, spring constants, and 'k' factor.
""" """
f = tb.open_file(fname) f = tb.open_file(fname)
sims = {}
## kinematic coupling implies k == float(0) by definition so the simsref = {}
## 'config' dictionary for these simulation always has k == float(0) # do kinematic separately to avoid crazy logic
## which means that if we want to match a given config, then we have to mc = match_config(config, ["N", "masses", "isprings"])
## ignore k cc = [ match_value(mc, "config") ]
match_conf = match_config(config, ["N", "masses", "isprings"]) sims["kinematic"] = sorted(
kin = sorted( package_simulations( mlocate_nodes(f.root, "Group", conds = cc), f, close_file=False),
package_simulations( key=lambda y: float(y.get_attr("comm_step") ), reverse=True )
mlocate_nodes(f.root, "Group", conds = [ simsref["kinematic"] =
match_value("kinematic", "coupling"), # need the `k' parameter for the other ones
match_value(match_conf, "config"), mc.update("k")
] ), f, close_file=False),
key=lambda y: float(y.get_attr("comm_step") ), reverse=True ) for i in [ "filtered_", "plain_"]:
for k in [True, False]: # parallel or not
match_conf.update("k") # assemble the filter line
choose = cc + [match_value("signals", "coupling"),
filtered_parallel = sorted( match_value(re.compile(str(k)), "parallel"),
package_simulations(mlocate_nodes(f.root, "Group", conds = [ match_value(re.compile("%s.*"%i, re.I), "variant")]
match_value("signals", "coupling"), key = i + ("parallel" if k else "sequential")
#match_value(match_conf, "config"), sims[key] = sorted(
match_value(re.compile("filtered_.*"), "variant"), package_simulations( mlocate_nodes(f.root, "Group", conds = choose), f, close_file=False),
match_value(re.compile("True"), "parallel"), key=lambda y: float(y.get_attr("comm_step") ), reverse=True )
] ), f, close_file=False), f.close()
key=lambda y: float(y.get_attr("comm_step") ), reverse=True ) return sims
filtered_sequential = sorted(
package_simulations(mlocate_nodes(f.root, "Group", conds = [
#match_value(match_conf, "config"),
match_value("signals", "coupling"),
match_value(re.compile("filtered_.*"), "variant"),
match_value(re.compile("False"), "parallel"),
] ), f, close_file=False),
key=lambda y: float(y.get_attr("comm_step") ), reverse=True )
plain_parallel = sorted(
package_simulations(mlocate_nodes(f.root, "Group", conds = [
#match_value(match_conf, "config"),
match_value("signals", "coupling"),
match_value(re.compile("plain_.*"), "variant"),
match_value(re.compile("[Tt]rue"), "parallel"),
] ), f, close_file=False),
key=lambda y: float(y.get_attr("comm_step") ), reverse=True )
plain_sequential = sorted(
package_simulations(mlocate_nodes(f.root, "Group", conds = [
match_value("signals", "coupling"),
#match_value(match_conf, "config"),
match_value(re.compile("plain_.*"), "variant"),
match_value(re.compile("False"), "parallel"),
] ), f, close_file=True),
key=lambda y: float(y.get_attr("comm_step") ), reverse=True )
return [kin, filtered_parallel, filtered_sequential, plain_parallel, plain_sequential]
def get_violation(sim): def get_violation(sim):
""" This has a lot of hard coded stuff and should be fixed.
What it does here is provide a time sequence of
"""
n = len(sim) n = len(sim)
dx = 0. dx = 0.
dv = 0. dv = 0.
...@@ -154,6 +133,7 @@ def rms_error(pos, f, l): ...@@ -154,6 +133,7 @@ def rms_error(pos, f, l):
def convergence(f, l): def convergence(f, l):
eps=1e-16 # needed for loglog plots
lgd = [] lgd = []
figure(f) figure(f)
clf() clf()
......
...@@ -160,14 +160,20 @@ class attributes(): ...@@ -160,14 +160,20 @@ class attributes():
return None return None
class sim_data(): class sim_data():
"""
This stores the data found in the HDF5 file and provides a few
services.
"""
def __init__(self, sim, fmus, metadata): def __init__(self, sim, fmus, metadata):
self.sim = sim self.sim = sim
self.fmus = fmus self.fmus = fmus
self.metadata = metadata self.metadata = metadata
## constraint violations are stored as eqXXX_g
pos = re.compile("eq[\d]+_g") pos = re.compile("eq[\d]+_g")
l = [ x for x in list(self.sim.dtype.names) if pos.fullmatch(x)] l = [ x for x in list(self.sim.dtype.names) if pos.fullmatch(x)]
self.nc = len( l ) self.nc = len( l )
self.pos = sim[l] self.pos = sim[l]
## constraint velocity are stored as eqXXX_g
vel = re.compile("eq[\d]+_gv") vel = re.compile("eq[\d]+_gv")
l = [ x for x in list(self.sim.dtype.names) if vel.fullmatch(x)] l = [ x for x in list(self.sim.dtype.names) if vel.fullmatch(x)]
self.vel = sim[l] self.vel = sim[l]
...@@ -195,8 +201,7 @@ class sim_data(): ...@@ -195,8 +201,7 @@ class sim_data():
def set_attr(self, name, v): def set_attr(self, name, v):
self.metadata[name] = v self.metadata[name] = v
def get_n_constraints(self): def get_n_constraints(self):
return nc return self.nc
def get_rms(self, x): def get_rms(self, x):
return np.sqrt( np.power(x.view((x.dtype[0], len(x.dtype.names))), 2).mean(axis=1) ).view() return np.sqrt( np.power(x.view((x.dtype[0], len(x.dtype.names))), 2).mean(axis=1) ).view()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment