diff --git a/reacnetgenerator/_draw.py b/reacnetgenerator/_draw.py index 5054e2e94..e13e50e07 100644 --- a/reacnetgenerator/_draw.py +++ b/reacnetgenerator/_draw.py @@ -18,6 +18,7 @@ import logging import math +import traceback from io import StringIO import matplotlib.pyplot as plt @@ -45,10 +46,18 @@ def __init__(self, rng): self.pos = rng.pos self.nolabel = rng.nolabel self.showid = rng.showid + self._split = rng.split def draw(self): """Draw the network.""" - table, name = self._readtable() + self._draw() + if self._split > 1: + for st in range(self._split): + self._draw(timeaxis=st) + + def _draw(self, timeaxis=None): + table, name = self._readtable( + self.tablefilename if timeaxis is None else f"{self.tablefilename}.{timeaxis}") species, showname = self._handlespecies(name) G = nx.DiGraph() @@ -67,35 +76,35 @@ def draw(self): tableij)]) weights = np.array([math.log(G[u][v]['weight']+1) for u, v in G.edges()]) - widths = [ - weight / max(weights) * self.widthcoefficient * 2 - if weight > max(weights) * 0.7 else weight / max(weights) * self.widthcoefficient * - 0.5 for weight in weights] - colors = [ - self.start_color + weight / max(weights) * - (self.end_color - self.start_color) for weight in weights] + widths = weights / np.max(weights) * self.widthcoefficient * np.array( + [0.5, 2])[(weights > np.max(weights) * 0.7)+0] if weights.size else np.zeros(0) + colors = self.start_color + weights[:, np.newaxis] / np.max(weights) * ( + self.end_color - self.start_color) if weights.size else np.zeros(0) try: - self.pos = (nx.spring_layout(G) if not self.pos else nx.spring_layout(G, pos=self.pos, fixed=[p for p in self.pos])) if not self.k else ( - nx.spring_layout(G, k=self.k) if not self.pos else nx.spring_layout(G, pos=self.pos, fixed=[p for p in self.pos], k=self.k)) - if self.pos: + pos = nx.spring_layout(G, + pos=self.pos if self.pos else None, + fixed=list(self.pos) if self.pos else None, + k=self.k) + if pos: logging.info("The position of the species in the network is:") - logging.info(self.pos) + logging.info(pos) for with_labels in ([True] if not self.nolabel else [True, False]): nx.draw( - G, pos=self.pos, width=widths, node_size=self.node_size, + G, pos=pos, width=widths, node_size=self.node_size, font_size=self.font_size, with_labels=with_labels, - edge_color=colors, node_color=[self.node_color]*len(self.pos)) + edge_color=colors, node_color=[self.node_color]*len(pos)) imagefilename = "".join( (("" if with_labels else "nolabel_"), self.imagefilename)) - with StringIO() as stringio, open(imagefilename, 'w') as f: + with StringIO() as stringio, open(imagefilename if timeaxis is None else f"{imagefilename}.{timeaxis}", 'w') as f: plt.savefig(stringio, format='svg') f.write(scour.scour.scourString(stringio.getvalue())) plt.close() except Exception as e: logging.error(f"Error: cannot draw images. Details: {e}") + traceback.print_tb(e.__traceback__) - def _readtable(self): - df = pd.read_csv(self.tablefilename, sep=' ', index_col=0, header=0) + def _readtable(self, filename): + df = pd.read_csv(filename, sep=' ', index_col=0, header=0) return df.values, df.index def _handlespecies(self, name): diff --git a/reacnetgenerator/_matrix.py b/reacnetgenerator/_matrix.py index 26bb7ab46..5ef788730 100644 --- a/reacnetgenerator/_matrix.py +++ b/reacnetgenerator/_matrix.py @@ -35,13 +35,14 @@ def generate(self): R=[a_ij ], i=1,2,…,100;j=1,2,…,100 where aij is the number of reactions from species si to sj. """ - allroute = self._getallroute(self.allmoleculeroute) - self._printtable(allroute) + self._printtable(self._getallroute(self.allmoleculeroute)) + if self.rng.splitmoleculeroute is not None: + for i, smr in enumerate(self.rng.splitmoleculeroute): + self._printtable(self._getallroute(smr), timeaxis=i) if self.needprintspecies: self._printspecies() def _getallroute(self, allmoleculeroute): - allroute = Counter() names = self._mname[allmoleculeroute-1] names = names[names[:, 0] != names[:, 1]] if names.size > 0: @@ -49,7 +50,7 @@ def _getallroute(self, allmoleculeroute): return zip(equations[0].tolist(), equations[1].tolist()) return [] - def _printtable(self, allroute, maxsize=100): + def _printtable(self, allroute, maxsize=100, timeaxis=None): species = [] sortedreactions = sorted( allroute, key=lambda d: d[1], reverse=True) @@ -76,7 +77,7 @@ def _printtable(self, allroute, maxsize=100): table = np.zeros((maxsize, maxsize), dtype=np.int) reactionnumber = np.zeros((2), dtype=np.int) - with open(self.reactionfilename, 'w') as f: + with open(self.reactionfilename if timeaxis is None else f"{self.reactionfilename}.{timeaxis}", 'w') as f: for reaction, n_reaction in sortedreactions: f.write(f"{n_reaction} {'->'.join(reaction)}\n") for i, spec in enumerate(reaction): @@ -93,7 +94,7 @@ def _printtable(self, allroute, maxsize=100): df = pd.DataFrame(table[:len(species), :len( species)], index=species, columns=species) - df.to_csv(self.tablefilename, sep=' ') + df.to_csv(self.tablefilename if timeaxis is None else f"{self.tablefilename}.{timeaxis}", sep=' ') def _searchspecies(self, originspec, sortedreactions, species): searchedspecies = [] diff --git a/reacnetgenerator/_path.py b/reacnetgenerator/_path.py index efbb735ca..fc42220f2 100644 --- a/reacnetgenerator/_path.py +++ b/reacnetgenerator/_path.py @@ -46,6 +46,7 @@ def __init__(self, rng): self._decompress = rng.decompress self._bytestolist = rng.bytestolist self._produce = rng.produce + self._split = rng.split self._mname = None self.atomnames = None @@ -67,6 +68,9 @@ def collect(self): self._printmoleculename() atomeach = self._getatomeach() self.rng.allmoleculeroute = self._printatomroute(atomeach) + if self._split > 1: + splittime = np.array_split(np.arange(self._step), self._split) + self.rng.splitmoleculeroute = list([self._printatomroute(atomeach[:, st], timeaxis=i) for i, st in enumerate(splittime)]) self.rng.mname = self._mname @abstractmethod @@ -96,14 +100,14 @@ def _getatomroute(self, item): [f"Atom {i} {self.atomname[atomtypei]}: ", " -> ".join(names)]) return moleculeroute, routestr - def _printatomroute(self, atomeach): - with open(self.atomroutefilename, 'w') as f, Pool(self.nproc, maxtasksperchild=1000) as pool: + def _printatomroute(self, atomeach, timeaxis=None): + with open(self.atomroutefilename if timeaxis is None else f"{self.atomroutefilename}.{timeaxis}", 'w') as f, Pool(self.nproc, maxtasksperchild=1000) as pool: allmoleculeroute = [] semaphore = Semaphore(self.nproc*150) results = pool.imap(self._getatomroute, self._produce( semaphore, enumerate(zip(atomeach, self.atomtype), start=1), ()), 100) for moleculeroute, routestr in tqdm( - results, total=self._N, desc="Collect reaction paths", + results, total=self._N, desc="Collect reaction paths" if timeaxis is None else f"Collect reaction paths {timeaxis}", unit="atom"): f.write("".join([routestr, '\n'])) if moleculeroute.size > 0: diff --git a/reacnetgenerator/_reachtml.py b/reacnetgenerator/_reachtml.py index d9c8d0703..633577c69 100644 --- a/reacnetgenerator/_reachtml.py +++ b/reacnetgenerator/_reachtml.py @@ -27,6 +27,7 @@ def __init__(self, rng): self._resultfile = rng.resultfilename self._imagefile = rng.imagefilename self._nproc = rng.nproc + self._split = rng.split self._templatedict = { "speciesshownum": 30, "reactionsshownum": 20, @@ -51,15 +52,15 @@ def _re(cls, smi): "C", "[C]").replace( "[HH]", "[H]") - def _readreaction(self): + def _readreaction(self, timeaxis=None): reaction = [] - with open(self._reactionfile) as f: + with open(self._reactionfile if timeaxis is None else f"{self._reactionfile}.{timeaxis}") as f: for line in f: sx = line.split() s = sx[1].split("->") left, right, num = self._re(s[0]), self._re(s[1]), int(sx[0]) reaction.append((left, right, num)) - if len(self._linkreac[left]) < 5: + if timeaxis is None and len(self._linkreac[left]) < 5: self._linkreac[left].append(right) return reaction @@ -80,29 +81,38 @@ def _convertsvg(cls, smiles): svgdata = re.sub(r"""