From 99dc739ff65f3b47859553c367292bd3b03fa75a Mon Sep 17 00:00:00 2001 From: Keck Jean-Baptiste <jean-baptiste.keck@imag.fr> Date: Sat, 16 Sep 2017 00:43:14 +0200 Subject: [PATCH] io and advec remesh fully optimized --- .../taylor_green/taylor_green_monoscale.py | 2 +- hysop/__init__.py | 2 +- hysop/backend/host/host_buffer.py | 2 +- .../operator/directional/advection_dir.py | 124 ++++++++++++------ .../backend/host/python/operator/transpose.py | 8 +- hysop/core/graph/computational_graph.py | 32 +++-- hysop/core/graph/graph.py | 24 ++-- hysop/core/memory/memory_request.py | 6 +- hysop/fields/cartesian_discrete_field.py | 29 +++- hysop/mesh/cartesian_mesh.py | 37 ++++-- hysop/operator/adapt_timestep.py | 41 +++--- hysop/operator/hdf_io.py | 94 ++++++++++--- hysop/parameters/numpy_parameter.py | 4 +- hysop/parameters/scalar_parameter.py | 3 +- hysop/problem.py | 2 +- hysop/tools/decorators.py | 4 +- hysop/tools/io_utils.py | 47 ++----- 17 files changed, 300 insertions(+), 161 deletions(-) diff --git a/examples/taylor_green/taylor_green_monoscale.py b/examples/taylor_green/taylor_green_monoscale.py index 42a77492f..0dbde7634 100644 --- a/examples/taylor_green/taylor_green_monoscale.py +++ b/examples/taylor_green/taylor_green_monoscale.py @@ -101,7 +101,7 @@ def run(npts=128+1, viscosity=1./1600., lcfl=0.125, cfl=0.5): problem = Problem(method={ComputeGranularity:0}) problem.insert(splitting, adapt_dt) - problem.dump_inputs() + problem.dump_inputs(fields=vorti) problem.build() # problem.display() diff --git a/hysop/__init__.py b/hysop/__init__.py index b29be2bef..89f06b50e 100644 --- a/hysop/__init__.py +++ b/hysop/__init__.py @@ -20,7 +20,7 @@ __VERBOSE__ = True __DEBUG__ = False __TRACE__ = False __KERNEL_DEBUG__ = False -__PROFILE__ = True +__PROFILE__ = False __ENABLE_LONG_TESTS__ = False diff --git a/hysop/backend/host/host_buffer.py b/hysop/backend/host/host_buffer.py index c95a3b133..733c5f09b 100644 --- a/hysop/backend/host/host_buffer.py +++ b/hysop/backend/host/host_buffer.py @@ -8,7 +8,7 @@ class HostBuffer(np.ndarray, Buffer): """ Host buffer class. """ - __array_priority__ = +1.0 + __array_priority__ = -1.0 def __new__(cls, size, shape=None, dtype=np.uint8, order=None, diff --git a/hysop/backend/host/python/operator/directional/advection_dir.py b/hysop/backend/host/python/operator/directional/advection_dir.py index 5e8c81e90..95fff0992 100644 --- a/hysop/backend/host/python/operator/directional/advection_dir.py +++ b/hysop/backend/host/python/operator/directional/advection_dir.py @@ -17,6 +17,7 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat @debug def __init__(self, **kwds): super(PythonDirectionalAdvection, self).__init__(**kwds) + self.is_periodic = False def get_work_properties(self): requests = super(PythonDirectionalAdvection, self).get_work_properties() @@ -55,11 +56,61 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat def setup(self, work): super(PythonDirectionalAdvection, self).setup(work=work) + self.dpos = self.dposition.handle.view(type=npw.ndarray) self.drtmp = work.get_buffer(self, 'rtmp', handle=True) self.ditmp = work.get_buffer(self, 'itmp', handle=True) self.dixtmp = work.get_buffer(self, 'ixtmp', handle=True) if self.is_inplace: self.dstmp = work.get_buffer(self, 'stmp', handle=True) + + self._prepare_apply() + + def _prepare_apply(self): + cr = self.compute_granularity + sinputs = self.dadvected_fields_in + soutputs = self.dadvected_fields_out + + velo_mesh = self.dvelocity.mesh + velo_mesh_iterator = velo_mesh.build_compute_mesh_iterator(cr) + X0 = velo_mesh.local_compute_coords[-1] + dx = velo_mesh.space_step[-1] + inv_dx = (1.0/dx) + velo_compute_view = velo_mesh.local_compute_slices + self._velocity_mesh_attributes = (velo_mesh_iterator, dx, inv_dx, velo_compute_view, X0) + + scalar_mesh = sinputs.values()[0].mesh + scalar_mesh_iterator = scalar_mesh.build_compute_mesh_iterator(cr) + sdx = scalar_mesh.space_step[-1] + inv_sdx = (1.0/sdx) + scalar_compute_view = scalar_mesh.local_compute_slices + self._scalar_mesh_attributes = (scalar_mesh_iterator, sdx, inv_sdx, scalar_compute_view) + + remesh_ghosts = self.remesh_ghosts + outer_ghosts, inner_ghosts, proc_shape = {}, {}, {} + for sout in soutputs.values(): + outer_ghosts[sout] = sout.get_outer_ghost_slices(remesh_ghosts) + inner_ghosts[sout] = sout.get_inner_ghost_slices(remesh_ghosts) + proc_shape[sout] = sout.mesh.proc_shape + self._outer_ghosts = outer_ghosts + self._inner_ghosts = inner_ghosts + self._proc_shape = proc_shape + + in_compute_slices, out_compute_slices = {}, {} + in_ghosts, out_ghosts = {}, {} + in_shapes, out_shapes = {}, {} + + for field in sinputs.keys(): + Sin = sinputs[field] + Sout = soutputs[field] + in_compute_slices[Sin] = Sin.compute_slices + in_ghosts[Sin] = Sin.ghosts + in_shapes[Sin] = Sin.shape + out_compute_slices[Sout] = Sout.compute_slices + out_ghosts[Sout] = Sout.ghosts + out_shapes[Sout] = Sout.shape + self._inout_compute_slices = (in_compute_slices, out_compute_slices) + self._inout_ghosts = (in_ghosts, out_ghosts) + self._inout_shapes = (in_shapes, out_shapes) @op_apply def apply(self, simulation, **kwds): @@ -111,22 +162,17 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat Vout[...] += Vin[Iy+(lidx,)] def _compute_advection(self, dt): - cr = self.compute_granularity - P = self.dposition - Vd = self.dvelocity[self.splitting_direction] + P = self.dpos + Vd = self.dvelocity(self.splitting_direction) rtmp = self.drtmp itmp = self.ditmp - mesh = self.dvelocity.mesh - X0 = mesh.local_compute_coords[-1] - dx = mesh.space_step[-1] - view = mesh.local_compute_slices - dim = mesh.dim - - rk_scheme = self.time_integrator + cr = self.compute_granularity + is_periodic = self.is_periodic + rk_scheme = self.time_integrator - is_periodic = False - inv_dx = (1.0/dx) + (mesh_it,dx,inv_dx,view,X0) = self._velocity_mesh_attributes + _Vd = Vd[view] Vd = Vd[view[:-1]+(slice(None),)] @@ -140,7 +186,7 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat assert Vinf*dt*inv_dx <= cfl, msg # fill position with first direction coordinates on the whole compute domain - for idx,_,I,Ig in mesh.iter_compute_axes(cr): + for (idx,_,I,Ig) in mesh_it.iter_compute_mesh(): Pi = P[idx] Vi = Vd[idx] _Vi = _Vd[idx] # Vi without ghosts @@ -202,31 +248,25 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat assert Pmax <= X0[-1] + Vmax*dt + 10*eps, msg def _compute_remesh(self, dt): - pos = self.dposition.handle + pos = self.dpos sinputs = self.dadvected_fields_in soutputs = self.dadvected_fields_out is_inplace = self.is_inplace - rtmp = self.drtmp - itmp = self.ditmp - stmp = self.dstmp + + rtmp = self.drtmp + itmp = self.ditmp + stmp = self.dstmp ixtmp = self.dixtmp cr = self.compute_granularity - mesh = sinputs.values()[0].mesh - dim = mesh.dim - dx = mesh.space_step[-1] - view = mesh.local_compute_slices + (mesh_it, dx, inv_dx, compute_view) = self._scalar_mesh_attributes scalar_advection_ghosts = self.scalar_advection_ghosts remesh_ghosts = self.remesh_ghosts remesh_kernel = self.remesh_kernel P = 1 + remesh_kernel.n/2 - G = scalar_advection_ghosts + P - 1 - assert (G == remesh_ghosts) - - is_periodic = False - inv_dx = (1.0/dx) + is_periodic = self.is_periodic R0,R1 = rtmp[:2] I0 = itmp[0] @@ -236,14 +276,18 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat is_periodic = False N = R0.shape[-1] + + (in_compute_slices, out_compute_slices) = self._inout_compute_slices + (in_ghosts, out_ghosts) = self._inout_ghosts + (in_shapes, out_shapes) = self._inout_shapes - for (idx,_,I,_) in mesh.iter_compute_axes(cr): + for (idx,_,I,_) in mesh_it.iter_compute_mesh(): Pi = pos[idx] R0[...] = Pi R0[...] *= inv_dx I0[...] = npw.floor(R0).astype(I0.dtype) R0[...] -= I0 - R0[...] *= -1 # we need -alpha + R0[...] *= -1 # we need -alpha for the left point if __debug__: Imin, Imax = I0.min(), I0.max() @@ -260,22 +304,23 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat I0[...] += 1 R0[...] += 1 R1[...] = remesh_kernel.gamma(R0) + sid=0 for field in sinputs.keys(): Sin = sinputs[field] Sout = soutputs[field] - sin_view = tuple(idx[i] + Sin.ghosts[i] for i in xrange(cr)) - sin_view += Sin.compute_slices[cr:] + sin_view = tuple(idx[i] + in_ghosts[Sin][i] for i in xrange(cr)) + sin_view += in_compute_slices[Sin][cr:] - dG = Sout.ghosts[-1] - remesh_ghosts - sout_view = tuple(idx[i] + Sout.ghosts[i] for i in xrange(cr)) - sout_view += Sout.compute_slices[cr:-1] - sout_view += (slice(dG, Sout.shape[-1]-dG),) + dG = out_ghosts[Sout][-1] - remesh_ghosts + sout_view = tuple(idx[i] + out_ghosts[Sout][i] for i in xrange(cr)) + sout_view += out_compute_slices[Sout][cr:-1] + sout_view += (slice(dG, out_shapes[Sout][-1]-dG),) for k in xrange(field.nb_components): - sin = sinputs[field].data[k].handle[sin_view] - sout = soutputs[field].data[k].handle[sout_view] + sin = Sin(k)[sin_view] + sout = Sout(k)[sout_view] Si = (S[sid] if is_inplace else sout) if (q==-P+1): Si[...] = 0.0 @@ -296,9 +341,9 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat # exchange and sum remeshed ghosts for sout in soutputs.values(): buffers = sout.buffers - outer_ghosts = sout.get_outer_ghost_slices(remesh_ghosts) - inner_ghosts = sout.get_inner_ghost_slices(remesh_ghosts) - nprocs = sout.mesh.proc_shape + outer_ghosts = self._outer_ghosts[sout] + inner_ghosts = self._inner_ghosts[sout] + nprocs = self._proc_shape[sout] for buf in buffers: lsrc, rsrc = outer_ghosts[-1] @@ -312,7 +357,6 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat sout.exchange_ghosts() - @debug def handle_method(self, method): super(PythonDirectionalAdvection, self).handle_method(method) diff --git a/hysop/backend/host/python/operator/transpose.py b/hysop/backend/host/python/operator/transpose.py index 3c43b2a38..c5c53796b 100644 --- a/hysop/backend/host/python/operator/transpose.py +++ b/hysop/backend/host/python/operator/transpose.py @@ -25,13 +25,13 @@ class PythonTranspose(TransposeOperatorBase, HostOperator): axes = self.axes if self.is_inplace: - din, dout, dtmp = self.din, self.dout, self.dtmp + din, dout, dtmp = self.din, self.dout, self.dtmp.handle assert self.din.dfield is self.dout.dfield for i in xrange(din.nb_components): - dtmp[...] = np.transpose(din[i], axes=axes) - dout[i][...] = dtmp + dtmp[...] = np.transpose(din(i), axes=axes) + dout(i)[...] = dtmp else: din, dout = self.din, self.dout assert self.din.dfield is not self.dout.dfield for i in xrange(din.nb_components): - dout[i][...] = np.transpose(din[i], axes=axes) + dout(i)[...] = np.transpose(din(i), axes=axes) diff --git a/hysop/core/graph/computational_graph.py b/hysop/core/graph/computational_graph.py index ce06b335a..fd8999c31 100644 --- a/hysop/core/graph/computational_graph.py +++ b/hysop/core/graph/computational_graph.py @@ -48,6 +48,7 @@ class ComputationalGraph(ComputationalGraphNode): self.nodes = [] self.graph = None self.graph_built = False + self.graph_is_rendering = False @graph_built def _get_profiling_info(self): @@ -339,6 +340,9 @@ class ComputationalGraph(ComputationalGraphNode): win.connect("delete_event", Gtk.main_quit) win.show_all() Gtk.main() + self.graph_is_rendering = False + + self.graph_is_rendering = True from threading import Thread display_thread = Thread(target=draw) @@ -433,23 +437,25 @@ class ComputationalGraph(ComputationalGraphNode): @debug @ready def apply(self, **kwds): + drawing = self.graph_is_rendering reduced_graph = self.reduced_graph operators = reduced_graph.vertex_properties['operators'] - active_ops = reduced_graph.vertex_properties['active_ops'] - - old_color = None - for vid in self.sorted_nodes: - if old_color: + if drawing: + active_ops = reduced_graph.vertex_properties['active_ops'] + old_color = None + for vid in self.sorted_nodes: + if old_color: + active_ops[vertex] = old_color + vertex = reduced_graph.vertex(vid) + old_color = active_ops[vertex] + active_ops[vertex] = 'red' + op = operators[vertex] + op.apply(**kwds) active_ops[vertex] = old_color - vertex = reduced_graph.vertex(vid) - old_color = active_ops[vertex] - active_ops[vertex] = 'red' - - op = operators[vertex] - op_kwds = kwds.copy() - op.apply(**op_kwds) - active_ops[vertex] = old_color + else: + for op in self.nodes: + op.apply(**kwds) @debug @ready diff --git a/hysop/core/graph/graph.py b/hysop/core/graph/graph.py index 7f68ab59f..3c138f8d0 100644 --- a/hysop/core/graph/graph.py +++ b/hysop/core/graph/graph.py @@ -25,7 +25,7 @@ if __debug__: def not_initialized(f): assert callable(f) @wraps(f) - def check(*args,**kargs): + def _not_initialized(*args,**kargs): return f(*args,**kargs) self = args[0] msg = 'Cannot call {}.{}() on node \'{}\' because {}'\ @@ -33,12 +33,12 @@ if __debug__: if self.initialized: reason='this node has already been initialized.' raise RuntimeError(msg.format(reason)) - return check + return _not_initialized def initialized(f): assert callable(f) @wraps(f) - def check(*args,**kargs): + def _initialized(*args,**kargs): self = args[0] msg = 'Cannot call {}.{}() on node \'{}\' because {}'\ .format(self.__class__.__name__,f.__name__,self.name,'{}') @@ -46,12 +46,12 @@ if __debug__: reason='this node has not been initialized yet.' raise RuntimeError(msg.format(reason)) return f(*args,**kargs) - return check + return _initialized def discretized(f): assert callable(f) @wraps(f) - def check(*args,**kargs): + def _discretized(*args,**kargs): self = args[0] msg = 'Cannot call {}.{}() on node \'{}\' because {}'\ .format(self.__class__.__name__,f.__name__,self.name,'{}') @@ -59,12 +59,12 @@ if __debug__: reason='this node has not been discretized yet.' raise RuntimeError(msg.format(reason)) return f(*args,**kargs) - return check + return _discretized def ready(f): assert callable(f) @wraps(f) - def check(*args,**kargs): + def _ready(*args,**kargs): self = args[0] msg = 'Cannot call {}.{}() on node \'{}\' because {}'\ .format(self.__class__.__name__,f.__name__, self.name,'{}') @@ -72,12 +72,12 @@ if __debug__: reason='this node has not been set up.' raise RuntimeError(msg.format(reason)) return f(*args,**kargs) - return check + return _ready def graph_built(f): assert callable(f) @wraps(f) - def check(*args,**kargs): + def _graph_built(*args,**kargs): self = args[0] msg = 'Cannot call {}.{}() on node \'{}\' because {}'\ .format(self.__class__.__name__,f.__name__,self.name,'{}') @@ -85,12 +85,12 @@ if __debug__: reason = 'the graph has not been built yet.' raise RuntimeError(msg.format(reason)) return f(*args,**kargs) - return check + return _graph_built def generated(f): assert callable(f) @wraps(f) - def check(*args,**kargs): + def _generated(*args,**kargs): self = args[0] msg = 'Cannot call {}.{}() on node \'{}\' because {}'\ .format(self.__class__.__name__,f.__name__,self.name,'{}') @@ -98,7 +98,7 @@ if __debug__: reason='this node has not been generated yet.' raise RuntimeError(msg.format(reason)) return f(*args,**kargs) - return check + return _generated else: # not __debug__ # python optimized, no checks diff --git a/hysop/core/memory/memory_request.py b/hysop/core/memory/memory_request.py index 9112c12e9..3efc91348 100644 --- a/hysop/core/memory/memory_request.py +++ b/hysop/core/memory/memory_request.py @@ -327,7 +327,11 @@ class MultipleOperatorMemoryRequests(object): raise ValueError(msg) buffers = op_buffers[request_identifier] if handle: - buffers = tuple([b.handle for b in buffers]) + from hysop.backend.host.host_buffer import HostBuffer + if isinstance(buffers[0].handle, HostBuffer): + buffers = tuple([b.handle.view(np.ndarray) for b in buffers]) + else: + buffers = tuple([b.handle for b in buffers]) return buffers def __str__(self): diff --git a/hysop/fields/cartesian_discrete_field.py b/hysop/fields/cartesian_discrete_field.py index e5296eb48..0bd1ac7d8 100644 --- a/hysop/fields/cartesian_discrete_field.py +++ b/hysop/fields/cartesian_discrete_field.py @@ -170,12 +170,15 @@ class CartesianDiscreteFieldView(DiscreteFieldView): By default return all components. """ data = self.__get_data_views(*components) - return tuple(d.handle for d in data) + if (self.backend.kind == Backend.HOST): + return tuple(d.handle.view(type=np.ndarray) for d in data) + else: + return tuple(d.handle for d in data) def __getitem__(self, key): """ - Access to the content of the field. - Returns ArrayBackend.Array component key of the field if key in an integer. + Access to the content of the field as with self.data. + Returns ArrayBackend.Array component(s) key of the field if key in an integer. Else if key is a slice, returns a tuple of slice Arrays. """ if isinstance(key, slice): @@ -183,10 +186,30 @@ class CartesianDiscreteFieldView(DiscreteFieldView): return self._get_data(*range(*indices)) elif isinstance(key, (int,long)): return self._get_data(key)[0] + elif isinstance(key, Ellipsis): + return self._get_data() else: msg='Unknown key type {} for CartesianDiscreteField.__getitem__().' msg=msg.format(key.__class__) raise TypeError(msg) + + def __call__(self, key): + """ + Access to the content of the field as with self.buffer. + Returns ArrayBackend.Array handle component(s) key of the field if key in an integer. + Else if key is a slice, returns a tuple of slice Arrays. + """ + if isinstance(key, slice): + indices = key.indices(self.nb_components) + return self._get_buffers(*range(*indices)) + elif isinstance(key, (int,long)): + return self._get_buffers(key)[0] + elif isinstance(key, Ellipsis): + return self._get_buffers() + else: + msg='Unknown key type {} for CartesianDiscreteField.__call__().' + msg=msg.format(key.__class__) + raise TypeError(msg) def _get_axes(self): """Return the permutation scheme in numpy notations.""" diff --git a/hysop/mesh/cartesian_mesh.py b/hysop/mesh/cartesian_mesh.py index dbda4d668..dd78b0ea1 100644 --- a/hysop/mesh/cartesian_mesh.py +++ b/hysop/mesh/cartesian_mesh.py @@ -352,20 +352,41 @@ class CartesianMeshView(MeshView): for idx in np.ndindex(*iter_shape): yield (idx, I) - def iter_compute_axes(self, axes=0): + + def iter_compute_mesh(self, axes=0): """ Return an iterator over a compute mesh array that returns indices. Iterates 'axes' axes at a time (0=whole array view, 1=one axe at a time, max value is self.dim). """ + return self.build_compute_mesh_iterator(axes=axes).iter_compute_mesh() + + def build_compute_mesh_iterator(self, axes): + """ + Return an object that can iterate over the local + computational mesh multiple times. + Usefull for optimizing operator apply methods. + See self.iter_compute_mesh(). + """ assert 0 <= axes <= self.dim, 'bad value for axes' - ghosts = self.ghosts - iter_shape = tuple(self.compute_resolution[:axes]) - gI = np.ix_(*self.local_compute_indices[axes:]) - for idx in np.ndindex(*iter_shape): - gidx = tuple(idx[i]+ghosts[i] for i in xrange(axes)) - I = tuple(gI[i]-ghosts[j] for i,j in enumerate(xrange(axes, self.dim))) - yield (idx, gidx, I, gI) + + class __CartesianMeshComputeAxeIterator(object): + def __init__(self, dim, ghosts, compute_resolution, local_compute_indices): + iter_shape = tuple(compute_resolution[:axes]) + gI = np.ix_(*local_compute_indices[axes:]) + self._data = (dim, ghosts, iter_shape, gI) + + def iter_compute_mesh(self): + (dim, ghosts, iter_shape, gI) = self._data + for idx in np.ndindex(*iter_shape): + gidx = tuple(idx[i]+ghosts[i] for i in xrange(axes)) + I = tuple(gI[i]-ghosts[j] for i,j in enumerate(xrange(axes, dim))) + yield (idx, gidx, I, gI) + + return __CartesianMeshComputeAxeIterator(dim=self.dim, + ghosts=self.ghosts, + compute_resolution=self.compute_resolution, + local_compute_indices=self.local_compute_indices) def is_inside_local_domain(self, point): """ diff --git a/hysop/operator/adapt_timestep.py b/hysop/operator/adapt_timestep.py index 17b7c2bf2..c2e436c06 100755 --- a/hysop/operator/adapt_timestep.py +++ b/hysop/operator/adapt_timestep.py @@ -100,32 +100,39 @@ class CflTimestepCriteria(TimestepCriteria): super(CflTimestepCriteria,self).__init__(name=name, variables={velocity:None} ) self.cfl = cfl self.velocity = velocity + self._compute_criteria_impl = None def discretize(self): super(CflTimestepCriteria, self).discretize() - self.dvelocity = self.input_discrete_fields[self.velocity] - - def compute_criteria(self, **kwds): + dv = self.input_discrete_fields[self.velocity] + self.dvelocity = dv + from hysop.fields.cartesian_discrete_field import CartesianDiscreteFieldView - - dv = self.dvelocity - cfl = self.cfl - dt = +npw.inf - if isinstance(dv, CartesianDiscreteFieldView): - dx = dv.dfield.mesh.space_step - for i in xrange(dv.nb_components): - Vi_min, Vi_max = dv[i].min(), dv[i].max() - Vi_inf = max(abs(Vi_min), abs(Vi_max)) - if Vi_inf == 0: - dti = +npw.inf - else: - dti = (dx[i]*cfl) / Vi_inf - dt = min(dt, dti) + self._compute_criteria_impl = self._compute_criteria_cartesian + self._dx = dv.dfield.mesh.space_step else: msg='CFL criteria has not been implemented for {} discrete fields.' msg=msg.format(type(self.dvelocity)) raise RuntimeError(msg) + + def compute_criteria(self, **kwds): + return self._compute_criteria_impl(**kwds) + + def _compute_criteria_cartesian(self, **kwds): + dv = self.dvelocity + cfl = self.cfl + dt = +npw.inf + dx = self._dx + + for i in xrange(dv.nb_components): + Vi_min, Vi_max = dv(i).min(), dv(i).max() + Vi_inf = max(abs(Vi_min), abs(Vi_max)) + if Vi_inf == 0: + dti = +npw.inf + else: + dti = (dx[i]*cfl) / Vi_inf + dt = min(dt, dti) assert dt>0.0 return dt diff --git a/hysop/operator/hdf_io.py b/hysop/operator/hdf_io.py index f40541064..eb3f41159 100755 --- a/hysop/operator/hdf_io.py +++ b/hysop/operator/hdf_io.py @@ -15,6 +15,7 @@ from hysop.tools.decorators import debug from hysop.tools.types import check_instance from hysop.tools.numpywrappers import npw from hysop.tools.io_utils import IO, IOParams, XMF +from hysop.core.graph.graph import op_apply from hysop.core.graph.computational_graph import ComputationalGraphOperator from hysop.fields.continuous_field import Field from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors @@ -127,6 +128,7 @@ class HDF_IO(ComputationalGraphOperator): refmesh = self.subset.mesh[self.topology] else: refmesh = self.topology.mesh + self.refmesh = refmesh # Global resolution for hdf5 output (warning : this must # be the whole domain resolution, not the subset resolution) @@ -211,10 +213,8 @@ class HDF_Writer(HDF_IO): if xmfalways: self.step = self._step_HDF5_XMF - self.finalize = lambda: 1 else: self.step = self._step_HDF5 - self.finalize = self.createXMFFile self._xdmf_data_files = [] # filename = prefix_N, N = counter value self._get_filename = self._input_fname @@ -223,6 +223,17 @@ class HDF_Writer(HDF_IO): # time where xdmf has been written, to raise an exception # if that happens. self._last_written_time = None + self._xmf_file = None + + def setup(self, **kwds): + super(HDF_Writer,self).setup(**kwds) + self._setup_grid_template() + self.createXMFFile() + + def finalize(self): + self.updateXMFFile() + if self._xmf_file: + self._xmf_file.close() def _input_fname(self, i): """Set output file name for current iteration""" @@ -230,7 +241,7 @@ class HDF_Writer(HDF_IO): assert i >= 0, msg return self.io_params.filename + "_{0:05d}".format(i) + '.h5' - @debug + @op_apply def apply(self, simulation=None, **kwds): if simulation is None: raise ValueError("Missing simulation value for monitoring.") @@ -241,11 +252,41 @@ class HDF_Writer(HDF_IO): if self.kill: sys.exit(1) - def createXMFFile(self): - """Create and fill the xdmf file - """ + def _setup_grid_template(self): + topo = self.topology + dim = topo.domain.dim + dx = list(topo.mesh.space_step) + mesh = self.refmesh + subset = self.subset + if subset is not None: + res = list(mesh[topo].grid_resolution) + orig = list(subset.real_orig[topo]) + else: + res = list(mesh.grid_resolution) + orig = list(topo.domain.origin) + resolution = [1,]*3 + origin = [0.0,]*3 + step = [0.0,]*3 + + idim = 3-dim + resolution[idim:] = res + origin[idim:] = orig + step[idim:] = dx + + write_resolution = tuple(resolution) + write_origin = tuple(origin) + write_step = tuple(step) + + ds_names = self.dataset.keys() + grid_attributes = XMF.prepare_grid_attributes( + ds_names, + resolution, origin, step) + self.grid_attributes_template = grid_attributes + - if self.mpi_params.rank == self.io_params.io_leader: + def createXMFFile(self): + """Create and fill the header of the xdmf file.""" + if (self.mpi_params.rank == self.io_params.io_leader): f = open(self.io_params.filename + '.xmf', 'w') f.write("<?xml version=\"1.0\" ?>\n") f.write("<!DOCTYPE Xdmf SYSTEM \"Xdmf.dtd\">\n") @@ -253,15 +294,29 @@ class HDF_Writer(HDF_IO): f.write(" <Domain>\n") f.write(" <Grid Name=\"CellTime\" GridType=\"Collection\" ") f.write("CollectionType=\"Temporal\">\n") - ds_names = self.dataset.keys() - for i, t in self._xdmf_data_files: - f.write(XMF.write_grid_attributes( - self.topology, ds_names, i, t, self._get_filename(i), - self.subset)) - f.write(" </Grid>\n") - f.write(" </Domain>\n") - f.write("</Xdmf>\n") - f.close() + self._last_xmf_pos = f.tell() + f.flush() + self._xmf_file = f + + def updateXMFFile(self): + """Update xdmf file.""" + if (self.mpi_params.rank == self.io_params.io_leader): + f = self._xmf_file + lastp = self._last_xmf_pos + assert (f is not None) + assert (lastp is not None) + for (i, t) in self._xdmf_data_files: + filename = self._get_filename(i).split('/')[-1] + grid_attrs = self.grid_attributes_template.format( + niteration=i, time=t, filename=filename) + f.seek(lastp) + f.write(grid_attrs) + self._last_xmf_pos = f.tell() + f.write(" </Grid>\n") + f.write(" </Domain>\n") + f.write("</Xdmf>\n") + f.flush() + self._xdmf_data_files = [] def _step_HDF5(self, simu): """Write an h5 file with data on each mpi process. @@ -299,8 +354,7 @@ class HDF_Writer(HDF_IO): def _step_HDF5_XMF(self, simu): self._step_HDF5(simu) - self.createXMFFile() - + self.updateXMFFile() class HDF_Reader(HDF_IO): """ @@ -335,8 +389,8 @@ class HDF_Reader(HDF_IO): self.io_params.filename + "_{0:05d}".format(i) + '.h5' else: self._get_filename = lambda i=None: self.io_params.filename - - @debug + + @op_apply def apply(self, simulation=None, **kwds): # Read HDF file self.open_hdf(count=self.restart, mode='r') diff --git a/hysop/parameters/numpy_parameter.py b/hysop/parameters/numpy_parameter.py index e1dc61c1a..b0d73aaf0 100644 --- a/hysop/parameters/numpy_parameter.py +++ b/hysop/parameters/numpy_parameter.py @@ -153,11 +153,11 @@ class NumpyParameter(Parameter): assert (value is not None) and isinstance(value, np.ndarray) if (value.shape != self.shape): msg='Parameter shape mismatch, expected {} but got {}.' - msg=msg.format(self.shape, other.shape) + msg=msg.format(self.shape, value.shape) raise ValueError(msg) if (value.dtype != self.dtype): msg='Parameter dtype mismatch, expected {} but got {}.' - msg=msg.format(self.dtype, other.dtype) + msg=msg.format(self.dtype, value.dtype) raise ValueError(msg) if (self.min_value is not None) or \ (self.max_value is not None) or \ diff --git a/hysop/parameters/scalar_parameter.py b/hysop/parameters/scalar_parameter.py index a82f95ac7..bb3c3146f 100644 --- a/hysop/parameters/scalar_parameter.py +++ b/hysop/parameters/scalar_parameter.py @@ -26,7 +26,8 @@ class ScalarParameter(NumpyParameter): """ if not isinstance(value, np.ndarray): assert np.isscalar(value), 'value is neither a np.ndarray, nor a scalar.' - value = np.asarray([value], dtype=self.dtype).reshape((1,)) + value = np.asarray([value], dtype=self.dtype) + value = value.reshape((1,)) super(ScalarParameter,self)._set_value_impl(value) def __call__(self): diff --git a/hysop/problem.py b/hysop/problem.py index 2daf17286..43a8dcad0 100644 --- a/hysop/problem.py +++ b/hysop/problem.py @@ -26,7 +26,7 @@ class Problem(ComputationalGraph): self.setup(work) @debug - def solve(self, simu, max_iter=None, report_freq=10, **kargs): + def solve(self, simu, max_iter=None, report_freq=20, **kargs): vprint('\nSolving problem...') simu.initialize() while not simu.is_over: diff --git a/hysop/tools/decorators.py b/hysop/tools/decorators.py index 255a621c1..1ae124dcf 100644 --- a/hysop/tools/decorators.py +++ b/hysop/tools/decorators.py @@ -124,13 +124,13 @@ def profile(f): if __PROFILE__: from hysop.core.mpi import Wtime @wraps(f) - def decorator(*args, **kwargs): + def _profile(*args, **kwargs): """args[0] contains the object""" t0 = Wtime() res = f(*args, **kwargs) args[0]._profiler[f.func_name] += Wtime() - t0 return res - return decorator + return _profile else: return f diff --git a/hysop/tools/io_utils.py b/hysop/tools/io_utils.py index dcdc35af4..3211a174b 100755 --- a/hysop/tools/io_utils.py +++ b/hysop/tools/io_utils.py @@ -419,23 +419,18 @@ class XMF(object): return buff.replace(']', '').replace('(', '').replace(')', '') @staticmethod - def write_grid_attributes(topo, dataset_names, ite, - time, filename, subset=None): + def prepare_grid_attributes(dataset_names, + resolution, origin, step): """ - Write XDMF header into a file + Prepare XDMF header as a string. Parameters ----------- - topo : :class:`hysop.topology.topology.CartesianTopology` - used as reference to define local and global meshes in xdmf file. dataset_names : list all datasets names - ite : int - iteration number - time : double - current time - filename : string - name of the hdf file which contains datas for the current process. + resolution: 3d tuple + origin: 3d tuple + step: 3d tuple subset : :class:`hysop.domain.subsets.Subset`, optional to define a grid only on this subset. If None, grid on the whole domain (from topo) @@ -443,38 +438,22 @@ class XMF(object): Returns: -------- string - the xml-like header. + the xml-like header formattable with the following keywords: + niteration : iteration number + time: time in seconds + filename: target file name """ # The header (xml-like), saved in a string. # always use a 3D mesh because paraview does not like 2D meshes (uses axe (Y,Z) instead of (X,Y)). xml_grid = "" - dim = topo.domain.dim topo_type = "3DCORECTMesh" geo_type = "ORIGIN_DXDYDZ" - xml_grid += " <Grid Name=\"Iteration {0:03d}\"".format(ite) + xml_grid += " <Grid Name=\"Iteration {}\"".format('{niteration:03d}') xml_grid += " GridType=\"Uniform\">\n" - xml_grid += " <Time Value=\"{0}\" />\n".format(time) + xml_grid += " <Time Value=\"{}\" />\n".format('{time}') xml_grid += " <Topology TopologyType=\"" + str(topo_type) + "\"" xml_grid += " NumberOfElements=\"" - - # Check substet to find the required grid resolution - dx = list(topo.mesh.space_step) - if subset is not None: - res = list(subset.mesh[topo].grid_resolution) - orig = list(subset.real_orig[topo]) - else: - res = list(topo.mesh.grid_resolution) - orig = list(topo.domain.origin) - resolution = [1,]*3 - origin = [0.0,]*3 - step = [0.0,]*3 - - idim = 3-dim - resolution[idim:] = res - origin[idim:] = orig - step[idim:] = dx - xml_grid += XMF._list_format(resolution) + " \"/>\n" xml_grid += " <Geometry GeometryType=\"" + geo_type + "\">\n" xml_grid += " <DataItem Dimensions=\"" + str(3) + " \"" @@ -495,7 +474,7 @@ class XMF(object): xml_grid += XMF._list_format(resolution) + " \"" xml_grid += " NumberType=\"Float\" Precision=\"8\" Format=\"HDF\"" xml_grid += " Compression=\"Raw\">\n" # - xml_grid += " " + filename.split('/')[-1] + xml_grid += " {filename}" xml_grid += ":/" + name xml_grid += "\n </DataItem>\n" xml_grid += " </Attribute>\n" -- GitLab