diff --git a/examples/bubble/periodic_bubble.py b/examples/bubble/periodic_bubble.py index 5c088b7376f0118c83bc588ddb62e64ec6ece1b4..cdc9868ceb867afa3a40a56ce11d899b56944dbf 100644 --- a/examples/bubble/periodic_bubble.py +++ b/examples/bubble/periodic_bubble.py @@ -110,6 +110,7 @@ def compute(args): if (dim==3): stretch_diffuse = DirectionalStretchingDiffusion(implementation=impl, name='stretch_diffuse', + pretty_name='sdiff', formulation = args.stretching_formulation, viscosity = mu, velocity = velo, @@ -118,7 +119,8 @@ def compute(args): dt=dt, **extra_op_kwds) elif (dim==2): stretch_diffuse = DirectionalDiffusion(implementation=impl, - name='stretch_diffuse', + name='diffuse', + pretty_name='diff', coeffs = mu, fields = vorti, variables = {vorti: npts, mu: npts}, @@ -140,7 +142,7 @@ def compute(args): #> Operators to dump rho and mu io_params = IOParams(filename='fields', frequency=args.dump_freq) - dump_fields = HDF_Writer(name='hdf_writer', + dump_fields = HDF_Writer(name='dump', io_params=io_params, variables={velo: npts, vorti: npts, @@ -148,19 +150,19 @@ def compute(args): mu: npts}) #> Operator to compute the infinite norm of the velocity - min_max_U = MinMaxFieldStatistics(name='min_max_U', field=velo, + min_max_U = MinMaxFieldStatistics(field=velo, Finf=True, implementation=impl, variables={velo:npts}, **extra_op_kwds) #> Operator to compute the infinite norm of the vorticity - min_max_W = MinMaxFieldStatistics(name='min_max_W', field=vorti, + min_max_W = MinMaxFieldStatistics(field=vorti, Finf=True, implementation=impl, variables={vorti:npts}, **extra_op_kwds) #> Operator to track min and max density and viscosity - min_max_rho = MinMaxFieldStatistics(name='min_max_rho', field=rho, + min_max_rho = MinMaxFieldStatistics(field=rho, Fmin=True, Fmax=True, implementation=impl, variables={rho:npts}, **extra_op_kwds) - min_max_mu = MinMaxFieldStatistics(name='min_max_mu', field=mu, + min_max_mu = MinMaxFieldStatistics(field=mu, Fmin=True, Fmax=True, implementation=impl, variables={mu:npts}, **extra_op_kwds) diff --git a/hysop/core/graph/computational_graph.py b/hysop/core/graph/computational_graph.py index 596c52735311348ad21501ad2a3d8248a4d43584..8c9d2ab0d6c5c65e6920040b156e1c4b9ca2c7ec 100644 --- a/hysop/core/graph/computational_graph.py +++ b/hysop/core/graph/computational_graph.py @@ -1,7 +1,7 @@ from hysop import __DEBUG__, __VERBOSE__, vprint, dprint from hysop.tools.decorators import debug from hysop.tools.types import to_list, to_set -from hysop.tools.string_utils import framed_str +from hysop.tools.string_utils import framed_str, strlen from hysop.tools.numpywrappers import npw from hysop.core.graph.graph import not_implemented, initialized, discretized, \ ready, graph_built, not_initialized @@ -70,7 +70,8 @@ class ComputationalGraph(ComputationalGraphNode): sinputs, soutputs = {}, {} def sorted_reqs(reqs): return sorted(reqs, key=lambda x: \ - '{}::{}'.format(x.operator.name, x.field.name)) + u'{}::{}'.format(x.operator.pretty_name.decode('utf-8'), + x.field.pretty_name.decode('utf-8'))) for field, mreqs in requirements.input_field_requirements.iteritems(): for td, reqs in mreqs.requirements.iteritems(): for req in reqs: @@ -79,15 +80,15 @@ class ComputationalGraph(ComputationalGraphNode): sin = sinputs.setdefault(td, []) for field, reqs in td_reqs.iteritems(): for req in sorted_reqs(reqs): - opname = getattr(req.operator, 'name', 'UnknownOperator') - fname = getattr(req.field, 'name', 'UnknownField') + opname = getattr(req.operator, 'pretty_name', 'UnknownOperator').decode('utf-8') + fname = getattr(req.field, 'pretty_name', 'UnknownField').decode('utf-8') min_ghosts=req.ghost_str(req.min_ghosts) max_ghosts=req.ghost_str(req.max_ghosts+1) - ghosts = '{}<=ghosts<{}'.format(min_ghosts, max_ghosts) + ghosts = u'{}<=ghosts<{}'.format(min_ghosts, max_ghosts) can_split=req.can_split.view(npw.int8) - basis='{}'.format(','.join(str(basis)[:3] for basis in req.basis)) \ + basis=u'{}'.format(u','.join(str(basis)[:3] for basis in req.basis)) \ if req.basis else 'ANY' - tstates='{}'.format(','.join(str(ts) for ts in req.tstates)) \ + tstates=u'{}'.format(u','.join(str(ts) for ts in req.tstates)) \ if req.tstates else 'ANY' sin.append( (opname, fname, ghosts, basis, tstates) ) for field, mreqs in requirements.output_field_requirements.iteritems(): @@ -98,35 +99,34 @@ class ComputationalGraph(ComputationalGraphNode): sin = soutputs.setdefault(td, []) for field, reqs in td_reqs.iteritems(): for req in sorted_reqs(reqs): - opname = getattr(req.operator, 'name', 'UnknownOperator') - fname = getattr(req.field, 'name', 'UnknownField') + opname = getattr(req.operator, 'pretty_name', 'UnknownOperator').decode('utf-8') + fname = getattr(req.field, 'pretty_name', 'UnknownField').decode('utf-8') min_ghosts=req.ghost_str(req.min_ghosts) max_ghosts=req.ghost_str(req.max_ghosts+1) - ghosts = '{}<=ghosts<{}'.format(min_ghosts, max_ghosts) + ghosts = u'{}<=ghosts<{}'.format(min_ghosts, max_ghosts) can_split=req.can_split.view(npw.int8) - basis='{}'.format(','.join(str(basis)[:3] for basis in req.basis)) \ - if req.basis else 'ANY' - tstates='{}'.format(','.join(str(ts) for ts in req.tstates)) \ - if req.tstates else 'ANY' + basis=u'{}'.format(u','.join(str(basis)[:3] for basis in req.basis)) \ + if req.basis else u'ANY' + tstates=u'{}'.format(u','.join(str(ts) for ts in req.tstates)) \ + if req.tstates else u'ANY' sin.append( (opname, fname, ghosts, basis, tstates) ) - titles = [[('OPERATOR', 'FIELD', 'GHOSTS', 'BASIS', 'TSTATES')]] + titles = [[(u'OPERATOR', u'FIELD', u'GHOSTS', u'BASIS', u'TSTATES')]] name_size = max(len(s[0]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) field_size = max(len(s[1]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) - ghosts_size = max(len(s[2].decode('utf-8').replace(u'\u221e', u' ')) \ - for ss in sinputs.values()+soutputs.values()+titles for s in ss) + ghosts_size = max(len(s[2]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) basis_size = max(len(s[3]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) tstates_size = max(len(s[4]) for ss in sinputs.values()+soutputs.values()+titles for s in ss) - template = '\n {:<{name_size}} {:^{field_size}} {:^{ghosts_size}} {:^{basis_size}} {:^{tstates_size}}' + template = u'\n {:<{name_size}} {:^{field_size}} {:^{ghosts_size}} {:^{basis_size}} {:^{tstates_size}}' - ss= '>INPUTS:' + ss= u'>INPUTS:' if sinputs: for (td, sreqs) in sinputs.iteritems(): if isinstance(td, Topology): - ss+='\n {}'.format(td.short_description()) + ss+=u'\n {}'.format(td.short_description()) else: - ss+='\n {}'.format(td) + ss+=u'\n {}'.format(td) ss+= template.format(*titles[0][0], name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, basis_size=basis_size, tstates_size=tstates_size) @@ -136,14 +136,14 @@ class ComputationalGraph(ComputationalGraphNode): name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, basis_size=basis_size, tstates_size=tstates_size) else: - ss+=' None' - ss+= '\n>OUTPUTS:' + ss+=u' None' + ss+= u'\n>OUTPUTS:' if soutputs: for (td, sreqs) in soutputs.iteritems(): if isinstance(td, Topology): - ss+='\n {}'.format(td.short_description()) + ss+=u'\n {}'.format(td.short_description()) else: - ss+='\n {}'.format(td) + ss+=u'\n {}'.format(td) ss+= template.format(*titles[0][0], name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, basis_size=basis_size, tstates_size=tstates_size) @@ -153,10 +153,10 @@ class ComputationalGraph(ComputationalGraphNode): name_size=name_size, field_size=field_size, ghosts_size=ghosts_size, basis_size=basis_size, tstates_size=tstates_size) else: - ss+=' None' + ss+=u' None' - title = ' ComputationalGraph {} field requirements report '.format(self.name) - return '\n{}\n'.format(framed_str(title=title, msg=ss)) + title = u' ComputationalGraph {} field requirements report '.format(self.pretty_name.decode('utf-8')) + return u'\n{}\n'.format(framed_str(title=title, msg=ss)).encode('utf-8') def domain_report(self): domains = self.get_domains() @@ -164,84 +164,126 @@ class ComputationalGraph(ComputationalGraphNode): for (domain,operators) in domains.iteritems(): if (domain is None): continue - for op in sorted(operators, key=lambda x: x.name): - finputs = ','.join( sorted([f.name for f in op.input_fields if f.domain is domain])) - foutputs =','.join( sorted([f.name for f in op.output_fields if f.domain is domain])) - pinputs = ','.join( sorted([p for p in op.input_params])) - poutputs =','.join( sorted([p for p in op.output_params])) - infields = '[{}]'.format(finputs) if finputs else '' - outfields = '[{}]'.format(foutputs) if foutputs else '' - inparams = '[{}]'.format(pinputs) if pinputs else '' - outparams = '[{}]'.format(poutputs) if poutputs else '' + for op in sorted(operators, key=lambda x: x.pretty_name): + finputs = u','.join( sorted([f.pretty_name.decode('utf-8') for f in op.input_fields if f.domain is domain])) + foutputs =u','.join( sorted([f.pretty_name.decode('utf-8') for f in op.output_fields if f.domain is domain])) + pinputs = u','.join( sorted([p.pretty_name.decode('utf-8') for p in op.input_params.values()])) + poutputs =u','.join( sorted([p.pretty_name.decode('utf-8') for p in op.output_params.values()])) + infields = u'[{}]'.format(finputs) if finputs else u'' + outfields = u'[{}]'.format(foutputs) if foutputs else u'' + inparams = u'[{}]'.format(pinputs) if pinputs else u'' + outparams = u'[{}]'.format(poutputs) if poutputs else u'' - inputs = '{}{}{}'.format(infields, 'x' if infields and inparams else '', inparams) - outputs = '{}{}{}'.format(outfields, 'x' if outfields and outparams else '', outparams) + inputs = u'{}{}{}'.format(infields, u'x' if infields and inparams else u'', inparams) + outputs = u'{}{}{}'.format(outfields, u'x' if outfields and outparams else u'', outparams) - if inputs == '': - inputs='no inputs' - if outputs == '': - outputs='no outputs' - ops.setdefault(domain, []).append( (op.name, inputs, outputs, type(op).__name__) ) + if inputs == u'': + inputs=u'no inputs' + if outputs == u'': + outputs=u'no outputs' + ops.setdefault(domain, []).append( (op.pretty_name.decode('utf-8'), inputs, outputs, type(op).__name__) ) if (None in domains): operators = domains[None] - for op in sorted(operators, key=lambda x: x.name): - pinputs = ','.join( sorted([p for p in op.input_params])) - poutputs =','.join( sorted([p for p in op.output_params])) - inparams = '[{}]'.format(pinputs) if pinputs else '' - outparams = '[{}]'.format(poutputs) if poutputs else '' - - inputs='{}'.format(inparams) - outputs='{}'.format(outparams) + for op in sorted(operators, key=lambda x: x.pretty_name): + pinputs = u','.join( sorted([p.pretty_name.decode('utf-8') for p in op.input_params.values()])) + poutputs =u','.join( sorted([p.pretty_name.decode('utf-8') for p in op.output_params.values()])) + inparams = u'[{}]'.format(pinputs) if pinputs else '' + outparams = u'[{}]'.format(poutputs) if poutputs else '' + + inputs=u'{}'.format(inparams) + outputs=u'{}'.format(outparams) if inputs == '': - inputs='no inputs' + inputs=u'no inputs' if outputs == '': - outputs='no outputs' - ops.setdefault(None, []).append( (op.name, inputs, outputs, type(op).__name__) ) + outputs=u'no outputs' + ops.setdefault(None, []).append( (op.pretty_name.decode('utf-8'), inputs, outputs, type(op).__name__) ) - name_size = max(len(s[0]) for ss in ops.values() for s in ss) - in_size = max(len(s[1]) for ss in ops.values() for s in ss) - out_size = max(len(s[2]) for ss in ops.values() for s in ss) - type_size = max(len(s[3]) for ss in ops.values() for s in ss) + name_size = max(strlen(s[0]) for ss in ops.values() for s in ss) + in_size = max(strlen(s[1]) for ss in ops.values() for s in ss) + out_size = max(strlen(s[2]) for ss in ops.values() for s in ss) + type_size = max(strlen(s[3]) for ss in ops.values() for s in ss) - ss = '' + ss = u'' for (domain,dops) in ops.iteritems(): if (domain is None): continue - ss += '\n>{}'.format(domain.short_description()) - ss += '\n {:<{name_size}} {:<{in_size}} {:<{out_size}} {:<{type_size}}'.format( + ss += u'\n>{}'.format(domain.short_description()) + ss += u'\n {:<{name_size}} {:<{in_size}} {:<{out_size}} {:<{type_size}}'.format( 'OPERATOR', 'INPUTS', 'OUTPUTS', 'OPERATOR TYPE', name_size=name_size, in_size=in_size, out_size=out_size, type_size=type_size) for (opname, inputs, outputs, optype) in dops: - ss += '\n {:<{name_size}} {:<{in_size}} -> {:<{out_size}} {:<{type_size}}'.format( + ss += u'\n {:<{name_size}} {:<{in_size}} -> {:<{out_size}} {:<{type_size}}'.format( opname, inputs, outputs, optype, name_size=name_size, in_size=in_size, out_size=out_size, type_size=type_size) if (None in domains): - ss += '\n>Domainless operators:' - ss += '\n {:<{name_size}} {:<{in_size}} {:<{out_size}} {:<{type_size}}'.format( + ss += u'\n>Domainless operators:' + ss += u'\n {:<{name_size}} {:<{in_size}} {:<{out_size}} {:<{type_size}}'.format( 'OPERATOR', 'INPUTS', 'OUTPUTS', 'OPERATOR TYPE', name_size=name_size, in_size=in_size, out_size=out_size, type_size=type_size) for (opname, inputs, outputs, optype) in ops[None]: - ss += '\n {:<{name_size}} {:<{in_size}} -> {:<{out_size}} {:<{type_size}}'.format( + ss += u'\n {:<{name_size}} {:<{in_size}} -> {:<{out_size}} {:<{type_size}}'.format( opname, inputs, outputs, optype, name_size=name_size, in_size=in_size, out_size=out_size, type_size=type_size) - title=' ComputationalGraph {} domain and operator report '.format(self.name) - return '\n{}\n'.format(framed_str(title=title, msg=ss[1:])) + title=u' ComputationalGraph {} domain and operator report '.format(self.pretty_name.decode('utf-8')) + return u'\n{}\n'.format(framed_str(title=title, msg=ss[1:])).encode('utf-8') def topology_report(self): ss='' for (backend,topologies) in self.get_topologies().iteritems(): - ss += '\n {}:'.format(backend.short_description()) - ss += '\n *'+'\n *'.join(t.short_description() + ss += u'\n {}:'.format(backend.short_description()) + ss += u'\n *'+'\n *'.join(t.short_description() for t in sorted(topologies, key=lambda x: x.id)) - title = ' ComputationalGraph {} topology report '.format(self.name) - return '\n{}\n'.format(framed_str(title=title, msg=ss[1:])) + title = u' ComputationalGraph {} topology report '.format(self.pretty_name.decode('utf-8')) + return u'\n{}\n'.format(framed_str(title=title, msg=ss[1:])) + + def variable_report(self): + reduced_graph = self.reduced_graph + operators = reduced_graph.vertex_properties['operators'] + fields = self.fields + + topologies = {} + for field in self.fields: + field_topologies = {} + for (i,vid) in enumerate(self.sorted_nodes): + vertex = reduced_graph.vertex(vid) + op = operators[vertex] + if field in op.input_fields: + topo = op.input_fields[field] + field_topologies.setdefault(topo, []).append(op) + if field in op.output_fields: + topo = op.output_fields[field] + field_topologies.setdefault(topo, []).append(op) + for topo in field_topologies.keys(): + pnames = set(op.pretty_name.decode('utf-8') for op in field_topologies[topo]) + sops=u', '.join(sorted(pnames)) + entries = (str(topo.backend.kind).lower(), topo.tag, sops) + topologies.setdefault(field, []).append(entries) + + titles = [[(u'BACKEND', u'TOPOLOGY', u'OPERATORS')]] + backend_size = max(len(s[0]) for ss in topologies.values()+titles for s in ss) + topo_size = max(len(s[1]) for ss in topologies.values()+titles for s in ss) + template = u'\n {:<{backend_size}} {:<{topo_size}} {}' + sizes = {'backend_size': backend_size, + 'topo_size': topo_size} + + ss = u'' + for field in sorted(self.fields, key=lambda x: x.tag): + ss += u'\n>FIELD {}::{}'.format(field.name, field.pretty_name.decode('utf-8')) + ss += template.format(*titles[0][0], **sizes) + field_topologies = topologies[field] + for entries in sorted(field_topologies, key=lambda x: x[0]): + ss += template.format(*entries, **sizes) + + title = u' ComputationalGraph {} fields report '.format(self.pretty_name.decode('utf-8')) + ss = u'\n{}\n'.format(framed_str(title=title, msg=ss[1:])) + return ss.encode('utf-8') def operator_report(self): reduced_graph = self.reduced_graph @@ -251,24 +293,30 @@ class ComputationalGraph(ComputationalGraphNode): vertex = reduced_graph.vertex(vid) op = operators[vertex] - finputs = ','.join( sorted(['{}.{}'.format(f.name,t.tag) for (f,t) in op.input_fields.iteritems()])) - foutputs = ','.join( sorted(['{}.{}'.format(f.name,t.tag) for (f,t) in op.output_fields.iteritems()])) - pinputs = ','.join( sorted([p for p in op.input_params])) - poutputs = ','.join( sorted([p for p in op.output_params])) - - infields = '[{}]'.format(finputs) if finputs else '' - outfields = '[{}]'.format(foutputs) if foutputs else '' - inparams = '[{}]'.format(pinputs) if pinputs else '' - outparams = '[{}]'.format(poutputs) if poutputs else '' + finputs = u','.join( sorted([u'{}.{}'.format(f.pretty_name.decode('utf-8'), + t.pretty_tag.decode('utf-8')) + for (f,t) in op.input_fields.iteritems()])) + foutputs = u','.join( sorted([u'{}.{}'.format(f.pretty_name.decode('utf-8'), + t.pretty_tag.decode('utf-8')) + for (f,t) in op.output_fields.iteritems()])) + pinputs = u','.join( sorted([p.pretty_name.decode('utf-8') + for p in op.input_params.values()])) + poutputs = u','.join( sorted([p.pretty_name.decode('utf-8') + for p in op.output_params.values()])) + + infields = u'[{}]'.format(finputs) if finputs else u'' + outfields = u'[{}]'.format(foutputs) if foutputs else u'' + inparams = u'[{}]'.format(pinputs) if pinputs else u'' + outparams = u'[{}]'.format(poutputs) if poutputs else u'' - inputs = '{}{}{}'.format(infields, 'x' if infields and inparams else '', inparams) - outputs = '{}{}{}'.format(outfields, 'x' if outfields and outparams else '', outparams) + inputs = u'{}{}{}'.format(infields, u'x' if infields and inparams else u'', inparams) + outputs = u'{}{}{}'.format(outfields, u'x' if outfields and outparams else u'', outparams) if inputs == '': - inputs='no inputs' + inputs=u'no inputs' if outputs == '': - outputs='no outputs' + outputs=u'no outputs' - ops.append( (op.name, inputs, outputs, type(op).__name__) ) + ops.append( (op.pretty_name.decode('utf-8'), inputs, outputs, type(op).__name__) ) isize = len(self.sorted_nodes) isize = int(npw.ceil(npw.log10(isize))) @@ -277,20 +325,20 @@ class ComputationalGraph(ComputationalGraphNode): out_size = max(len(s[2]) for s in ops) type_size = max(len(s[3]) for s in ops) - ss = ' {:<{isize}} {:<{name_size}} {:<{in_size}} {:<{out_size}} {:<{type_size}}'.format( + ss = u' {:<{isize}} {:<{name_size}} {:<{in_size}} {:<{out_size}} {:<{type_size}}'.format( 'ID', 'OPERATOR', 'INPUTS', 'OUTPUTS', 'OPERATOR TYPE', isize=isize, name_size=name_size, in_size=in_size, out_size=out_size, type_size=type_size) for i, (opname, inputs, outputs, optype) in enumerate(ops): - ss += '\n {:>{isize}} {:<{name_size}} {:<{in_size}} -> {:<{out_size}} {:<{type_size}}'.format( + ss += u'\n {:>{isize}} {:<{name_size}} {:<{in_size}} -> {:<{out_size}} {:<{type_size}}'.format( i, opname, inputs, outputs, optype, isize=isize, name_size=name_size, in_size=in_size, out_size=out_size, type_size=type_size) - title = ' ComputationalGraph {} discrete operator report '.format(self.name) - return '\n{}\n'.format(framed_str(title=title, msg=ss)) + title = u' ComputationalGraph {} discrete operator report '.format(self.pretty_name.decode('utf-8')) + return u'\n{}\n'.format(framed_str(title=title, msg=ss)).encode('utf-8') def get_domains(self): domains = {} @@ -330,8 +378,8 @@ class ComputationalGraph(ComputationalGraphNode): def available_methods(self): avail_methods = {} if not self.nodes: - msg='No nodes present in ComputationalGraph {}.'.format(self.name) - raise RuntimeError(msg) + msg=u'No nodes present in ComputationalGraph {}.'.format(self.pretty_name.decode('utf-8')) + raise RuntimeError(msg.encode('utf-8')) for node in self.nodes: for (k,v) in node.available_methods().iteritems(): v = to_set(v) @@ -358,8 +406,8 @@ class ComputationalGraph(ComputationalGraphNode): if is_root: self.pre_initialize(**kwds) - msg='ComputationalGraph {} is empty.' - assert len(self.nodes) > 0, msg.format(self.name) + msg=u'ComputationalGraph {} is empty.' + assert len(self.nodes) > 0, msg.format(self.pretty_name.decode('utf-8')).encode('utf-8') for node in self.nodes: node.pre_initialize(**kwds) @@ -470,6 +518,7 @@ class ComputationalGraph(ComputationalGraphNode): if (self.is_root and __VERBOSE__) or __DEBUG__ or self.__FORCE_REPORTS__: print self.topology_report() + print self.variable_report() print self.operator_report() @debug @@ -542,7 +591,7 @@ class ComputationalGraph(ComputationalGraphNode): if self.is_root: input_discrete_fields = {} - for field,topo in self.input_fields.iteritems(): + for (field,topo) in self.input_fields.iteritems(): istate = self.initial_input_topology_states[field][1] istate = istate.copy(is_read_only=False) # problem inputs are writeable for initialization dfield = field.discretize(topo, istate) @@ -577,10 +626,10 @@ class ComputationalGraph(ComputationalGraphNode): wp = op.get_work_properties() requests += op.get_work_properties() if __DEBUG__ or (__VERBOSE__ and self.level==0) or self.__FORCE_REPORTS__: - srequests = str(requests) - ss = (srequests if (srequests != '') else ' *no extra work requested*') - title= ' ComputationalGraph {} work properties report '.format(self.name) - vprint('\n{}\n'.format(framed_str(title=title, msg=ss))) + srequests = requests.sreport() + ss = (srequests if (srequests != u'') else u' *no extra work requested*') + title= u' ComputationalGraph {} work properties report '.format(self.pretty_name.decode('utf-8')) + vprint(u'\n{}\n'.format(framed_str(title=title, msg=ss)).encode('utf-8')) return requests @debug diff --git a/hysop/core/graph/graph_builder.py b/hysop/core/graph/graph_builder.py index 75d48a01157f339dc3196668196ca497e06ad837..cf069e9cdec77e769b7daf4fde1d71ae7e27981f 100644 --- a/hysop/core/graph/graph_builder.py +++ b/hysop/core/graph/graph_builder.py @@ -164,7 +164,7 @@ class GraphBuilder(object): target_node = self.target_node current_level = self.current_level - outputs_are_inputs = self.outputs_are_inputs + outputs_are_inputs = self.outputs_are_inputs graph = self.graph vertex_properties = self.vertex_properties @@ -172,8 +172,8 @@ class GraphBuilder(object): parameter_handler = self.__ParameterHandler(graph, edge_properties, vertex_properties) - input_fields = self.input_fields - output_fields = self.output_fields + input_fields = self.input_fields + output_fields = self.output_fields input_params = self.input_params output_params = self.output_params input_topology_states = self.input_topology_states @@ -199,7 +199,7 @@ class GraphBuilder(object): raise RuntimeError(msg) # iterate over ComputationalNodes - for node_id,node in enumerate(target_node.nodes): + for (node_id, node) in enumerate(target_node.nodes): dprint(' >Handling node {}: {}'.format(node_id, node.name) ) # Recursively build graph. @@ -213,12 +213,12 @@ class GraphBuilder(object): # iterate over subgraph operators for (opvertex, op) in zip(node_vertices,node_ops): dprint(' *{} ({})'.format(op.name, type(op))) - opname = op.name - oppname = op.pretty_name - iparams = op.input_params - oparams = op.output_params - ifields = op.input_fields - ofields = op.output_fields + opname = op.name + oppname = op.pretty_name + iparams = op.input_params + oparams = op.output_params + ifields = op.input_fields + ofields = op.output_fields backends = op.supported_backends() field_requirements = op._field_requirements @@ -231,7 +231,7 @@ class GraphBuilder(object): for (ifield, itopo) in ifields.iteritems(): if (itopo is not None): continue - # look for ifield usage till now + # look for ifield usage untill now if (ifield in ofields) and (ofields[ifield] is not None) and (ofields[ifield].backend.kind in backends): ifields[ifield] = ofields[ifield] elif (ifield not in self.topology_states): @@ -564,6 +564,9 @@ class GraphBuilder(object): operators = reduced_graph.vertex_properties['operators'] nodes = [ operators[reduced_graph.vertex(vid)] for vid in self.sorted_nodes ] return nodes + import sys + sys.exit(1) + def build_subgraph(self, node, current_level, **kwds): node_ops = [] diff --git a/hysop/core/graph/node_generator.py b/hysop/core/graph/node_generator.py index 79b7a06c0542b735c1e7dfec56b6b67b2c61ecae..4063482fdfb7e9d944aa786acb5ffabb0228fdc6 100644 --- a/hysop/core/graph/node_generator.py +++ b/hysop/core/graph/node_generator.py @@ -1,6 +1,7 @@ from abc import ABCMeta, abstractmethod from hysop import dprint from hysop.tools.decorators import debug +from hysop.tools.types import first_not_None from hysop.core.graph.computational_node import ComputationalGraphNode class ComputationalGraphNodeGenerator(object): @@ -11,9 +12,10 @@ class ComputationalGraphNodeGenerator(object): __metaclass__ = ABCMeta @debug - def __init__(self, name=None, **kwds): + def __init__(self, name=None, pretty_name=None, **kwds): super(ComputationalGraphNodeGenerator, self).__init__(**kwds) self.name = name or self.__class__.__name__ + self.pretty_name = first_not_None(pretty_name, self.name) self.nodes = [] self.generated = False diff --git a/hysop/core/memory/memory_request.py b/hysop/core/memory/memory_request.py index e4d8def1c1616307240623882a603ee4a1514497..b006ba955b09216dac8143af28c4ef75d3b7ffbd 100644 --- a/hysop/core/memory/memory_request.py +++ b/hysop/core/memory/memory_request.py @@ -415,7 +415,7 @@ class MultipleOperatorMemoryRequests(object): buffers = tuple([b.handle for b in buffers]) return buffers - def __str__(self): + def sreport(self): all_requests = {} for (backend, backend_requests) in self._all_requests_per_backend.iteritems(): total=0 @@ -423,7 +423,7 @@ class MultipleOperatorMemoryRequests(object): op_requests = backend_requests[op] sop_request = all_requests.setdefault(backend, {}).setdefault(op, []) local_total=0 - opname='{}'.format(op.name) + opname=u'{}'.format(op.pretty_name.decode('utf-8')) for req in op_requests: sop_request.append((opname,)+req.stuple()) local_total+=req.max_bytes() @@ -432,35 +432,35 @@ class MultipleOperatorMemoryRequests(object): if len(all_requests): sizes = {} - template = '\n' - titles=('OPERATOR', 'REQUEST_ID', 'SIZE', 'COMPONENTS', 'SHAPE', 'DTYPE', 'ALIGNMENT') + template = u'\n' + titles=(u'OPERATOR', u'REQUEST_ID', u'SIZE', u'COMPONENTS', u'SHAPE', u'DTYPE', u'ALIGNMENT') for (i,k) in enumerate(titles): k=k.lower() - template += ' ' + template += u' ' size = max(len(req[i]) for breqs in all_requests.values() for reqs in breqs.values() for req in reqs) size = max(size, len(k)) - name=k+'_len' + name=k+u'_len' sizes[name] = size - template += '{:'+('<' if i==0 else '^')+'{'+name+'}}' + template += u'{:'+(u'<' if i==0 else u'^')+u'{'+name+u'}}' ss='' for (backend, backend_srequests) in all_requests.iteritems(): kind = backend.kind if (kind == Backend.OPENCL): - precision = ' on device {}'.format(backend.device.name.strip()) + precision = u' on device {}'.format(backend.device.name.strip()) else: - precision = '' - ss+= '\n {}{}:'.format(backend.full_tag, precision) + precision = u'' + ss+= u'\n {}{}:'.format(backend.full_tag, precision) ss+= template.format(*titles, **sizes) for op in sorted(backend_srequests.keys(), key=lambda op: op.name): sop_reqs = backend_srequests[op] for sreq in sop_reqs: ss+= template.format(*sreq, **sizes) - ss +='\n Total extra work buffers requested: {} ({})'.format( + ss +=u'\n Total extra work buffers requested: {} ({})'.format( bytes2str(total,decimal=False), bytes2str(total,decimal=True)) - ss += '\n' + ss += u'\n' return ss[1:-1] else: - return ' No extra buffers have been requested.' + return u' No extra buffers have been requested.' diff --git a/hysop/fields/default_fields.py b/hysop/fields/default_fields.py index 0eea67820ef3c44028dfc524d87c2941791e82b0..a7c6c2559234596e36891533acd34cd6a9f762a7 100644 --- a/hysop/fields/default_fields.py +++ b/hysop/fields/default_fields.py @@ -5,7 +5,7 @@ from hysop.fields.continuous_field import Field def VelocityField(domain, name=None, pretty_name=None, is_vector=True, **kwds): name = first_not_None(name, 'U') - pretty_name = first_not_None(pretty_name, 'U') + pretty_name = first_not_None(pretty_name, greak[20]) is_vector = first_not_None(is_vector, True) return Field(domain=domain, name=name, pretty_name=pretty_name, is_vector=is_vector, **kwds) diff --git a/hysop/fields/field_requirements.py b/hysop/fields/field_requirements.py index 48c9d26417de411c1db39131f0a4a21b5c098b06..0c588ed16dcb0f49e3735c25c94e4bb85d18c9a7 100644 --- a/hysop/fields/field_requirements.py +++ b/hysop/fields/field_requirements.py @@ -111,7 +111,7 @@ class DiscreteFieldRequirements(object): def ghost_str(self, array): inf = u'+\u221e' vals = [u''+str(x) if np.isfinite(x) else inf for x in array] - return u'[{}]'.format(u','.join(vals)).encode('utf-8').strip() + return u'[{}]'.format(u','.join(vals)).strip() def __str__(self): diff --git a/hysop/operator/adapt_timestep.py b/hysop/operator/adapt_timestep.py index 4cfbb51210800df770a752291b1b5c38ea745392..0c8ff73ac5a41819c8070a7e56911e0946e67474 100755 --- a/hysop/operator/adapt_timestep.py +++ b/hysop/operator/adapt_timestep.py @@ -126,7 +126,10 @@ class CflTimestepCriteria(TimestepCriteria): dx = self.dx Finf = tuple(self.Finf().tolist()) assert len(dx) == len(Finf) - dt = cfl / npw.max(npw.divide(Finf, dx)) + if npw.any(npw.divide(Finf, dx)==0): + dt = cfl*npw.inf + else: + dt = cfl / npw.max(npw.divide(Finf, dx)) return dt def compute_cfl(self, dt): @@ -195,7 +198,10 @@ class AdvectionTimestepCriteria(TimestepCriteria): lcfl = self.lcfl if (criteria is AdvectionCriteria.W_INF): Finf = self.Finf() - return lcfl / npw.max(Finf) + if npw.max(Finf)==0: + return lcfl*npw.inf + else: + return lcfl / npw.max(Finf) elif (criteria is AdvectionCriteria.GRAD_U): gradFinf = self.gradFinf() if (gradFinf.ndim == 2): diff --git a/hysop/operator/base/min_max.py b/hysop/operator/base/min_max.py index daabf3cfa22f39744bcf4ce44b0472e18347cffc..edd7131670c6fd2cb1bf248e3e6340bab1c5b808 100644 --- a/hysop/operator/base/min_max.py +++ b/hysop/operator/base/min_max.py @@ -38,7 +38,7 @@ class MinMaxFieldStatisticsBase(object): raise ValueError(msg) pbasename = first_not_None(pbasename, field.name) - ppbasename = first_not_None(ppbasename, pbasename) + ppbasename = first_not_None(ppbasename, field.pretty_name.decode('utf-8'), pbasename) def make_param(k, quiet): return TensorParameter(name=names[k], pretty_name=pretty_names[k], @@ -80,7 +80,8 @@ class MinMaxFieldStatisticsBase(object): @debug def __init__(self, field, components=None, coeffs=None, Fmin=None, Fmax=None, Finf=None, all_quiet=None, - name=None, pbasename=None, ppbasename=None, + name=None, pretty_name=None, + pbasename=None, ppbasename=None, variables=None, **kwds): """ Initialize a MinMaxField Statistics operator frontend. @@ -118,6 +119,8 @@ class MinMaxFieldStatisticsBase(object): Set all autogenerated TensorParameter to be quiet. name: str, optional Name of this operator. + pretty_name: str, optional + Pretty name of this operator. pbasename: str, optional Parameters basename for created parameters. Defaults to field.name. @@ -160,10 +163,11 @@ class MinMaxFieldStatisticsBase(object): check_instance(ppbasename, str, allow_none=True) check_instance(all_quiet, bool, allow_none=True) - coeffs = first_not_None(coeffs, {}) - name = first_not_None(name, 'MinMax[{}]'.format(field.name)) - variables = first_not_None(variables, {field: None}) - all_quiet = first_not_None(all_quiet, False) + coeffs = first_not_None(coeffs, {}) + name = first_not_None(name, 'MinMax[{}]'.format(field.name)) + pretty_name = first_not_None(pretty_name, 'MinMax({})'.format(field.pretty_name)) + variables = first_not_None(variables, {field: None}) + all_quiet = first_not_None(all_quiet, False) parameters = self.build_parameters(field=field, components=components, all_quiet=all_quiet, Fmin=Fmin, Fmax=Fmax, Finf=Finf, @@ -172,12 +176,14 @@ class MinMaxFieldStatisticsBase(object): output_params = { p.name: p for p in parameters.values() if (p is not None) } if MinMaxDerivativeStatisticsBase in self.__class__.__mro__: - super(MinMaxFieldStatisticsBase, self).__init__(name=name, + super(MinMaxFieldStatisticsBase, self).__init__( + name=name, pretty_name=pretty_name, variables=variables, output_params=output_params, **kwds) self.has_derivative = True else: input_fields = {field: variables[field] } - super(MinMaxFieldStatisticsBase, self).__init__(name=name, + super(MinMaxFieldStatisticsBase, self).__init__( + name=name, pretty_name=pretty_name, input_fields=input_fields, output_params=output_params, **kwds) self.has_derivative = False @@ -237,7 +243,8 @@ class MinMaxDerivativeStatisticsBase(MinMaxFieldStatisticsBase): derivative=None, component=None, direction=None, out_component=None, scaling_view=None, Fmin=None, Fmax=None, Finf=None, coeffs=None, all_quiet=False, - name=None, pbasename=None, ppbasename=None, + name=None, pretty_name=None, + pbasename=None, ppbasename=None, variables=None, **kwds): """ Initialize an MinMaxDerivativeStatisticsBase. @@ -333,6 +340,8 @@ class MinMaxDerivativeStatisticsBase(MinMaxFieldStatisticsBase): each components. If not given, defaults to 1 for all statistics. name: str, optional Name of this operator. + pretty_name: str, optional + Pretty name of this operator. pbasename: str, optional Parameters basename for created parameters. Defaults to field.name. @@ -376,7 +385,8 @@ class MinMaxDerivativeStatisticsBase(MinMaxFieldStatisticsBase): super(MinMaxDerivativeStatisticsBase, self).__init__(field=dF, components=(out_component,), coeffs=coeffs, Fmin=Fmin, Fmax=Fmax, Finf=Finf, - name=name, pbasename=pbasename, variables=variables, + name=name, pretty_name=pretty_name, + pbasename=pbasename, variables=variables, F=F, dF=dF, A=A, derivative=derivative, component=component, direction=direction, out_component=out_component, scaling_view=scaling_view, **kwds) diff --git a/hysop/operator/base/redistribute_operator.py b/hysop/operator/base/redistribute_operator.py index fdeb3908a93f72fd42845abb1ffc0c706e82df19..a26ec479d02338a6136a4f40781a59ee2d8a4a0a 100644 --- a/hysop/operator/base/redistribute_operator.py +++ b/hysop/operator/base/redistribute_operator.py @@ -1,7 +1,8 @@ from abc import ABCMeta, abstractmethod from hysop.tools.decorators import debug, not_implemented -from hysop.tools.types import check_instance, to_set +from hysop.tools.types import check_instance, to_set, first_not_None +from hysop.tools.sympy_utils import subscript from hysop.constants import Backend from hysop.core.graph.computational_operator import ComputationalGraphOperator from hysop.topology.topology import Topology @@ -30,7 +31,8 @@ class RedistributeOperatorBase(ComputationalGraphOperator): """ return Backend.all - def __init__(self, variable, source_topo, target_topo, components=None, **kwds): + def __init__(self, variable, source_topo, target_topo, components=None, + name=None, pretty_name=None, **kwds): """ Parameters ---------- @@ -40,8 +42,12 @@ class RedistributeOperatorBase(ComputationalGraphOperator): source mesh topology target_topo: :class:`~hysop.topology.topology.Topology` target mesh topology - components: int or list of ints + components: int or list of ints, optional which component of the field must be distributed (default = all) + name: str, optional + name of this operator + pretty_name: str, optional + pretty name of this operator """ check_instance(variable, Field) check_instance(source_topo, Topology) @@ -53,7 +59,21 @@ class RedistributeOperatorBase(ComputationalGraphOperator): input_fields = {variable: source_topo} output_fields = {variable: target_topo} - super(RedistributeOperatorBase, self).__init__(input_fields=input_fields, + + default_name = 'R_{}_{},{}'.format(variable.name, source_topo.id, target_topo.id) + + default_pname = u'R{}{}{}{}'.format(variable.pretty_name.decode('utf-8'), + subscript(source_topo.id), + u'\u2192', + subscript(target_topo.id)) + default_pname = default_pname.encode('utf-8') + + pretty_name = first_not_None(pretty_name, name, default_pname) + name = first_not_None(name, default_name) + + super(RedistributeOperatorBase, self).__init__( + name=name, pretty_name=pretty_name, + input_fields=input_fields, output_fields=output_fields, **kwds) self.variable = variable diff --git a/hysop/operator/base/transpose_operator.py b/hysop/operator/base/transpose_operator.py index c6f0ea1dd066b6b9ce7eb487f7936218c861d1d2..71ceedf4d025f611536e1c1e81d6e0ccf8093dd0 100644 --- a/hysop/operator/base/transpose_operator.py +++ b/hysop/operator/base/transpose_operator.py @@ -54,7 +54,8 @@ class TransposeOperatorBase(object): input_fields = { input_field: variables[input_field] } output_fields = { output_field: variables[output_field] } - super(TransposeOperatorBase, self).__init__(input_fields=input_fields, + super(TransposeOperatorBase, self).__init__( + input_fields=input_fields, output_fields=output_fields, **kwds) self.input_field = input_field diff --git a/hysop/operator/directional/directional.py b/hysop/operator/directional/directional.py index ab3e57b8e02377403f599a19813765f7f46a8c5a..1616d1279849ebc711f6f21fa8479cc84ba3b709 100644 --- a/hysop/operator/directional/directional.py +++ b/hysop/operator/directional/directional.py @@ -1,7 +1,8 @@ from abc import ABCMeta, abstractmethod -from hysop.tools.types import check_instance, to_list +from hysop.tools.types import check_instance, to_list, first_not_None from hysop.tools.decorators import debug, static_vars +from hysop.tools.sympy_utils import subscript from hysop.constants import Implementation, DirectionLabels, TranspositionState from hysop.core.graph.graph import generated, not_implemented from hysop.core.graph.node_generator import ComputationalGraphNodeGenerator @@ -84,7 +85,8 @@ class DirectionalOperatorGenerator(object): __metaclass__ = ABCMeta @debug - def __init__(self, operator, base_kwds, name=None, **op_kwds): + def __init__(self, operator, base_kwds, + name=None, pretty_name=None, **op_kwds): """ Initialize a DirectionalOperatorGenerator. @@ -101,14 +103,11 @@ class DirectionalOperatorGenerator(object): Keywords arguments that will be passed towards operator.__init__ during a call to _generate. In addition to those arguments, direction and splitting_dim will also be passed. - If name is present in op_kwds, generated names will be - op_kwds['name'] + '_' + DirectionLabels[dir] - else: - self.__class__.__name__ + '_' + DirectionLabels[dir] """ super(DirectionalOperatorGenerator,self).__init__(**base_kwds) - self.name = name or type(self).__name__ + self.name = first_not_None(name, type(self).__name__) + self.pretty_name = first_not_None(pretty_name, self.name) self._operator = operator self._op_kwds = op_kwds @@ -174,20 +173,22 @@ class DirectionalOperatorGenerator(object): @generated def generate_direction(self, i, dt_coeff): kwds = self._op_kwds - if 'name' in kwds: - basename = kwds.pop('name') - else: - basename = self.name + basename = kwds.pop('name', self.name) + basepname = kwds.pop('pretty_name', self.pretty_name) kargs = {} kargs.update(kwds) kargs.update(self.custom_directional_kwds(i)) name = '{}_{}_{}'.format(basename, DirectionLabels[i], self._direction_counter[i]) + pname = u'{}_{}{}'.format(basepname, DirectionLabels[i], + subscript(self._direction_counter[i])) + pname = pname.encode('utf-8') self._direction_counter[i]+=1 try: - op = self._operator(name=name, splitting_direction=i, dt_coeff=dt_coeff, **kargs) + op = self._operator(name=name, pretty_name=pname, + splitting_direction=i, dt_coeff=dt_coeff, **kargs) except: sargs = ['*{} = {}'.format(k,v.__class__) for (k,v) in kargs.iteritems()] diff --git a/hysop/operator/redistribute.py b/hysop/operator/redistribute.py index c5846b75ed5035a78fb4dfe699947c6319048d8a..115abd3204128be14d62d76eee6dab3c8df8ceb1 100644 --- a/hysop/operator/redistribute.py +++ b/hysop/operator/redistribute.py @@ -40,7 +40,8 @@ class Redistribute(ComputationalGraphNodeGenerator): '{} is not a RedistributeOperatorBase.'.format(cls) def __init__(self, variables, source_topos, target_topo, components=None, - name=None, base_kwds=None, **kwds): + name=None, pretty_name=None, + base_kwds=None, **kwds): """ Initialize a Redistribute operator generator. Parameters @@ -55,6 +56,8 @@ class Redistribute(ComputationalGraphNodeGenerator): which component of the fields must be distributed (default = all components) name: string prefix for generated operator names + pretty_name: string + pretty prefix for generated operator names base_kwds: dict, optional, defaults to None Base class keywords arguments. If None, an empty dict will be passed. @@ -66,7 +69,7 @@ class Redistribute(ComputationalGraphNodeGenerator): assert 'source_topo' not in kwds base_kwds = base_kwds or dict() - super(Redistribute,self).__init__(name=name, **base_kwds) + super(Redistribute,self).__init__(name=name, pretty_name=pretty_name, **base_kwds) # format variables to a set of variables variables = to_set(variables) @@ -104,7 +107,6 @@ class Redistribute(ComputationalGraphNodeGenerator): self._source_topos = source_topos self._target_topo = target_topo self._components = components - self._name = name self._kwds = kwds @debug @@ -116,17 +118,12 @@ class Redistribute(ComputationalGraphNodeGenerator): components = self._components[var] kwds = self._kwds.copy() - if (self._name is not None): - name = self._name + '_{}'.format(var.name) - name += ''.join([DirectionLabels[i] for i in sorted(components)]).lower() - kwds['name'] = name - # if source topology is destination topology there is nothing to be done if target_topo in source_topos: continue # else we find the most suitable source topology - node = Redistribute._select_redistribute(variable=var, source_topos=source_topos, + node = self._select_redistribute(variable=var, source_topos=source_topos, target_topo=target_topo, components=components, **kwds) nodes.append(node) return nodes diff --git a/hysop/operator/transpose.py b/hysop/operator/transpose.py index b0a97b117a365af546208fca6f488f37278d342a..791239532e370be9758e5e0637c29b5a580e7928 100644 --- a/hysop/operator/transpose.py +++ b/hysop/operator/transpose.py @@ -43,7 +43,7 @@ class Transpose(ComputationalGraphNodeGenerator): from hysop.backend.host.python.operator.transpose import PythonTranspose from hysop.backend.device.opencl.operator.transpose import OpenClTranspose _implementations = { - Implementation.PYTHON: PythonTranspose, + Implementation.PYTHON: PythonTranspose, Implementation.OPENCL: OpenClTranspose } return _implementations @@ -303,10 +303,15 @@ class Transpose(ComputationalGraphNodeGenerator): TransposeOp = self._get_op_and_check_implementation(src_topo, dst_topo) axes = TransposeOp.get_preferred_axes(src_topo, dst_topo, self.candidate_axes) - if (self.name is not None): - name = self.name + '_{}_'.format(ifield.name) - name += ''.join([DirectionLabels[i] for i in axes]).lower() - kwds['name'] = name + name = self.name.replace('Transpose', 'T') + name = self.name + '_{}_'.format(ifield.name) + name += ''.join([DirectionLabels[i] for i in axes]).lower() + kwds['name'] = name + + pretty_name = self.pretty_name.replace('Transpose', 'T') + pname = pretty_name + u'{}_'.format(ifield.pretty_name.decode('utf-8')) + pname += u''.join([DirectionLabels[i] for i in axes]).lower() + kwds['pretty_name'] = pname.encode('utf-8') variables = { ifield: src_topo } variables[ofield] = dst_topo diff --git a/hysop/tools/handle.py b/hysop/tools/handle.py index 8927ec30662737e816c3054271cd296e26db9a2e..4806c367e11ce19bed4d3146c804427558044ccd 100644 --- a/hysop/tools/handle.py +++ b/hysop/tools/handle.py @@ -3,6 +3,7 @@ from abc import ABCMeta, abstractmethod from hysop.deps import np from hysop.tools.decorators import not_implemented, debug from hysop.tools.types import to_tuple, first_not_None +from hysop.tools.sympy_utils import subscript from hysop.core.mpi import MPI class TaggedObjectView(object): @@ -39,12 +40,20 @@ class TaggedObjectView(object): else: return getattr(self.__obj_view, '_TaggedObject__get_object_tag')() + def __get_object_pretty_tag(self): + """Unique pretty tag of the underlying object view.""" + if (not hasattr(self, '_TaggedObjectView__obj_view')) or (self.__obj_view is None): + assert isinstance(self, TaggedObject) + return getattr(self, '_TaggedObject__get_object_pretty_tag')() + else: + return getattr(self.__obj_view, '_TaggedObject__get_object_pretty_tag')() def __get_object_full_tag(self): """Unique tag of the underlying object view with cls information.""" return '{}::{}'.format(self.__class__.__name__, self.__get_object_tag()) id = property(__get_object_id) tag = property(__get_object_tag) + pretty_tag = property(__get_object_pretty_tag) full_tag = property(__get_object_full_tag) @abstractmethod @@ -129,6 +138,16 @@ class TaggedObject(object): self.__tag_formatter(self.__tag_id), self.__tag_postfix) + def __get_object_pretty_tag(self): + """ + Get the formatted pretty tag of this object as a string. + This is an instance identifier + """ + return u'{}{}{}'.format( + self.__tag_prefix, + self.__tag_formatter(subscript(self.__tag_id)), + self.__tag_postfix).encode('utf-8') + def __get_object_full_tag(self): """ Get the formatted tag of this object as a string. @@ -139,6 +158,7 @@ class TaggedObject(object): id = property(__get_object_id) tag = property(__get_object_tag) + pretty_tag = property(__get_object_pretty_tag) full_tag = property(__get_object_full_tag) @abstractmethod diff --git a/hysop/tools/string_utils.py b/hysop/tools/string_utils.py index 4e6bb1e3fca89794cb328ca651677c73faafa367..9e1a87957f71616a5e98052413e0917a2872496b 100644 --- a/hysop/tools/string_utils.py +++ b/hysop/tools/string_utils.py @@ -47,4 +47,9 @@ def framed_str(title, msg, c='=', at_border=2): title = c*at_border + title + c*at_border header = title + c*max(0, length-len(title)) footer = c*len(header) - return '{}\n{}\n{}'.format(header, msg, footer) + return u'{}\n{}\n{}'.format(header, msg, footer) + +def strlen(s): + """Like length but replace unicode characters by space before applying len()""" + #res = len(s.decode('utf-8')) + return len(s)