|
@@ -1,5 +1,20 @@
|
|
|
"""
|
|
"""
|
|
|
A module that allows the creation of a C file, in which the block's computations are inlined.
|
|
A module that allows the creation of a C file, in which the block's computations are inlined.
|
|
|
|
|
+Additional library files will be created to allow for linking:
|
|
|
|
|
+ - lsolve.c
|
|
|
|
|
+ - lsolve.h
|
|
|
|
|
+
|
|
|
|
|
+The :code:`template.c` file is the jinja template file.
|
|
|
|
|
+
|
|
|
|
|
+Note:
|
|
|
|
|
+ To compile the generated code, execute
|
|
|
|
|
+
|
|
|
|
|
+ ```
|
|
|
|
|
+ gcc <filename>.c lsolve.c -lm
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+Requires:
|
|
|
|
|
+ - `Jinja2 <https://jinja.palletsprojects.com/en/3.0.x/>`_
|
|
|
"""
|
|
"""
|
|
|
from CBD.scheduling import TopologicalScheduler
|
|
from CBD.scheduling import TopologicalScheduler
|
|
|
from CBD.solver import GaussianJordanLinearSolver
|
|
from CBD.solver import GaussianJordanLinearSolver
|
|
@@ -8,11 +23,34 @@ from CBD.converters.latexify import CBD2Latex, _BLOCK_MAP
|
|
|
import CBD.naivelog as naivelog
|
|
import CBD.naivelog as naivelog
|
|
|
|
|
|
|
|
from jinja2 import Template
|
|
from jinja2 import Template
|
|
|
|
|
+from shutil import copyfile
|
|
|
|
|
+import os
|
|
|
|
|
|
|
|
class CBD2C:
|
|
class CBD2C:
|
|
|
|
|
+ """
|
|
|
|
|
+ Main conversion class. Generates the new C file.
|
|
|
|
|
+ The C model will be executed in fixed-time steps of size
|
|
|
|
|
+ :code:`delta`, for a total of :code:`itcnt` iterations.
|
|
|
|
|
+ In total, the simulation will thus execute until timestep
|
|
|
|
|
+ :code:`itcnt * delta`.
|
|
|
|
|
+
|
|
|
|
|
+ Note:
|
|
|
|
|
+ This class does *not* change the original model.
|
|
|
|
|
+
|
|
|
|
|
+ Warning:
|
|
|
|
|
+ This class only works if the dependency graph is fixed
|
|
|
|
|
+ after the first iteration. I.e., iteration 0 can be
|
|
|
|
|
+ different, but all others must be the same.
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ model (CBD): CBD model that will be converted to C.
|
|
|
|
|
+ itcnt (int): The amount of iterations to execute the
|
|
|
|
|
+ c model for.
|
|
|
|
|
+ delta (float): The stepsize of the C code.
|
|
|
|
|
+ """
|
|
|
def __init__(self, model, itcnt, delta=1.0):
|
|
def __init__(self, model, itcnt, delta=1.0):
|
|
|
self.itcnt = itcnt
|
|
self.itcnt = itcnt
|
|
|
- self.delta = 1.0
|
|
|
|
|
|
|
+ self.delta = delta
|
|
|
self.model = model.clone()
|
|
self.model = model.clone()
|
|
|
self.model.flatten(psep='_')
|
|
self.model.flatten(psep='_')
|
|
|
for block in self.model.getBlocks():
|
|
for block in self.model.getBlocks():
|
|
@@ -21,17 +59,48 @@ class CBD2C:
|
|
|
self.solver = GaussianJordanLinearSolver(naivelog.getLogger("CBD"))
|
|
self.solver = GaussianJordanLinearSolver(naivelog.getLogger("CBD"))
|
|
|
|
|
|
|
|
def get_blocks(self):
|
|
def get_blocks(self):
|
|
|
|
|
+ """
|
|
|
|
|
+ Gets all the blocks from the flattened model.
|
|
|
|
|
+ """
|
|
|
return self.model.getBlocks()
|
|
return self.model.getBlocks()
|
|
|
|
|
|
|
|
def get_order(self, curIt):
|
|
def get_order(self, curIt):
|
|
|
|
|
+ """
|
|
|
|
|
+ Gets the topological order of the blocks at a specific iteration.
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ curIt (int):
|
|
|
|
|
+ """
|
|
|
depGraph = createDepGraph(self.model, curIt)
|
|
depGraph = createDepGraph(self.model, curIt)
|
|
|
return self.scheduler.schedule(depGraph, curIt, 0.0)
|
|
return self.scheduler.schedule(depGraph, curIt, 0.0)
|
|
|
|
|
|
|
|
@staticmethod
|
|
@staticmethod
|
|
|
def flatten(t):
|
|
def flatten(t):
|
|
|
|
|
+ """
|
|
|
|
|
+ Flattens a list of sublists.
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ t (iter): The list to flatten.
|
|
|
|
|
+ """
|
|
|
return [item for sublist in t for item in sublist]
|
|
return [item for sublist in t for item in sublist]
|
|
|
|
|
|
|
|
def obtain_data_from_order(self, order, curIt):
|
|
def obtain_data_from_order(self, order, curIt):
|
|
|
|
|
+ """
|
|
|
|
|
+ Given the order in which the blocks must be executed,
|
|
|
|
|
+ this function will use the :class:`CBD2Latex` class to
|
|
|
|
|
+ construct the C-representation for the functions.
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ order: List of components, topologically ordered.
|
|
|
|
|
+ curIt (int): The current iteration.
|
|
|
|
|
+
|
|
|
|
|
+ Returns:
|
|
|
|
|
+ A list of tuples like :code:`[([LHS, ...], RHS), ...]`;
|
|
|
|
|
+ where the :code:`LHS` identifies an ordered list of
|
|
|
|
|
+ left-hand sides (when there are multiple, it means an
|
|
|
|
|
+ algebraic loop); and :code:`RHS` the string-representation
|
|
|
|
|
+ of the results.
|
|
|
|
|
+ """
|
|
|
i = "0" if curIt == 0 else "i"
|
|
i = "0" if curIt == 0 else "i"
|
|
|
conf = {"path_sep": '_', "ignore_path": False, "escape_nonlatex": False, "time_variable": "time"}
|
|
conf = {"path_sep": '_', "ignore_path": False, "escape_nonlatex": False, "time_variable": "time"}
|
|
|
norder = []
|
|
norder = []
|
|
@@ -48,6 +117,8 @@ class CBD2C:
|
|
|
kv = {x: n.getBlockConnectedToInput(x) for x in n.getInputPortNames()}
|
|
kv = {x: n.getBlockConnectedToInput(x) for x in n.getInputPortNames()}
|
|
|
for k, v in kv.items():
|
|
for k, v in kv.items():
|
|
|
bd[1].apply(n.getPath('_') + '_' + k, v.block.getPath('_') + '_' + v.output_port)
|
|
bd[1].apply(n.getPath('_') + '_' + k, v.block.getPath('_') + '_' + v.output_port)
|
|
|
|
|
+ # if not isinstance(bd[1], str):
|
|
|
|
|
+ # bd[1].args = ["(double)%s" % str(x) for x in bd[1].args]
|
|
|
if n.getBlockType() == "DelayBlock":
|
|
if n.getBlockType() == "DelayBlock":
|
|
|
if curIt == 0:
|
|
if curIt == 0:
|
|
|
bd[1].apply_time(time=-1, t=i, fmt="[{time}]")
|
|
bd[1].apply_time(time=-1, t=i, fmt="[{time}]")
|
|
@@ -90,12 +161,29 @@ class CBD2C:
|
|
|
|
|
|
|
|
nc = (dlist, str(mat).replace("[", "{").replace("]", "}"))
|
|
nc = (dlist, str(mat).replace("[", "{").replace("]", "}"))
|
|
|
norder.append(nc)
|
|
norder.append(nc)
|
|
|
- print(norder)
|
|
|
|
|
return norder
|
|
return norder
|
|
|
|
|
|
|
|
def generate(self, fname):
|
|
def generate(self, fname):
|
|
|
- with open("template.c", 'r') as file:
|
|
|
|
|
|
|
+ """
|
|
|
|
|
+ Generates the C file and copies the sources for the
|
|
|
|
|
+ lsolve library in the same folder.
|
|
|
|
|
+
|
|
|
|
|
+ To compile the generated code, execute
|
|
|
|
|
+
|
|
|
|
|
+ ```
|
|
|
|
|
+ gcc <filename>.c lsolve.c -lm
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ fname (str): The filename of the C-code.
|
|
|
|
|
+ """
|
|
|
|
|
+ path = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
+ tpath = os.path.dirname(os.path.realpath(fname))
|
|
|
|
|
+ filename = os.path.join(path, "template.c")
|
|
|
|
|
+ with open(filename, 'r') as file:
|
|
|
template = Template(file.read(), trim_blocks=True, lstrip_blocks=True)
|
|
template = Template(file.read(), trim_blocks=True, lstrip_blocks=True)
|
|
|
|
|
+ copyfile(os.path.join(path, "lsolve.c"), os.path.join(tpath, "lsolve.c"))
|
|
|
|
|
+ copyfile(os.path.join(path, "lsolve.h"), os.path.join(tpath, "lsolve.h"))
|
|
|
variables = []
|
|
variables = []
|
|
|
for block in self.get_blocks():
|
|
for block in self.get_blocks():
|
|
|
if block.getBlockType() == "WireBlock":
|
|
if block.getBlockType() == "WireBlock":
|
|
@@ -111,8 +199,6 @@ class CBD2C:
|
|
|
comp0 = self.obtain_data_from_order(comp0, 0)
|
|
comp0 = self.obtain_data_from_order(comp0, 0)
|
|
|
comp = self.obtain_data_from_order(comp, 1)
|
|
comp = self.obtain_data_from_order(comp, 1)
|
|
|
|
|
|
|
|
- # print(comp0)
|
|
|
|
|
-
|
|
|
|
|
contents = template.render(itcnt = self.itcnt,
|
|
contents = template.render(itcnt = self.itcnt,
|
|
|
delta = self.delta,
|
|
delta = self.delta,
|
|
|
variables = variables,
|
|
variables = variables,
|