ffc team mailing list archive
-
ffc team
-
Mailing list archive
-
Message #00687
Re: Consolidating dolfin-swig and dolfin
Anders Logg wrote:
> I'll take a look.
>
> There will be a fix to this problem in the not so distant future. I'm
> working with Ola Skavhaug and Kent Andre Mardal on a new common
> interface specification for the code generation. This will be named
> UFC - Unified Form-assembly Code and be the default (and perhaps only)
> output format from FFC. The idea is to have a common output format for
> FFC and SyFi which is a similar tool developed by Kent.
>
> There will also be a common input language: UFL - Unified Form
> Language for both compilers.
>
> The UFC will be chosen to work well with SWIG out of the box, so we
> would not need any special solutions for SWIG.
>
> I hope we'll have a first draft ready for the UFC within a month or
> two.
>
In the light of this, is it worthwhile fixing the blas output option for
boundary integrals or should I wait?
Garth
> /Anders
>
>
> On Thu, Sep 07, 2006 at 03:23:07AM +0200, Johan Jansson wrote:
>> FFC has a special output format for PyDOLFIN: "dolfin-swig", which is
>> very similar to the standard "dolfin" format, but generates a bit
>> simpler code since SWIG does not support some concepts in C++ (nested
>> classes primarily, and namespaces somewhat). This is bad for
>> maintainability, since a change in one format has to be made in the
>> other, possibly with some minor modification.
>>
>> I've done some work in consolidating the two formats and I think I've
>> achieved an acceptable result. The new "dolfin" format is semantically
>> equivalent to the old format, so there is no visible change.
>>
>> The two main changes are: moving nested class declarations out of the
>> enclosing class and generating a map (which is output as a SWIG file)
>> between DOLFIN class names and PyDOLFIN class names (the scope
>> resolution operator "::" doesn't exist in Python, so it has to be
>> removed or replaced).
>>
>> The consequence is that SWIG can at least parse the nested classes,
>> though it (probably) ignores the fact that they are nested. This is a
>> hack (and an additional workaround is necessary to compensate for the
>> scoping), but it will have to do until there is better support in
>> SWIG. The SWIG developers have stated that nested classes won't be
>> implemented in the foreseeable future (i.e. until someone volunteers
>> responsibility of the problem), so we can't just wait.
>>
>> I've attached the diff of the changes (against changeset
>> 3a54d08c5669). Tell me if you have any objections, otherwise I'll go
>> ahead and make the corresponding changes to PyDOLFIN as well.
>>
>> Johan
>
>> diff -r 3a54d08c5669 src/ffc/format/dolfin.py
>> --- a/src/ffc/format/dolfin.py Wed Jun 14 18:04:10 2006 +0200
>> +++ b/src/ffc/format/dolfin.py Thu Sep 07 02:52:38 2006 +0200
>> @@ -37,6 +37,15 @@ format = { "sum": lambda l: " + ".join(l
>> "tmp declaration": lambda j, k: "const real tmp%d_%d" % (j, k),
>> "tmp access": lambda j, k: "tmp%d_%d" % (j, k) }
>>
>> +def swig(swigmap):
>> + output = ""
>> +
>> + for p in swigmap.items():
>> + output += "%%rename(%s) %s;\n" % (p[1], p[0])
>> +
>> + return output
>> +
>> +
>> def init(options):
>> "Initialize code generation for DOLFIN format."
>>
>> @@ -54,6 +63,9 @@ def write(forms, options):
>>
>> # Get name of form
>> name = forms[0].name
>> +
>> + # Initialize SWIG map
>> + swigmap = {}
>>
>> # Write file header
>> output = ""
>> @@ -80,8 +92,11 @@ be able to use it with DOLFIN.""", -1)
>> else:
>> xmlfile = "%s.xml" % forms[j].name
>>
>> - # Write form
>> - output += __form(form, type, options, xmlfile)
>> + # Write form prototype
>> + output += __form(form, type, options, xmlfile, swigmap, True)
>> +
>> + # Write form implementation
>> + output += __form(form, type, options, xmlfile, swigmap, False)
>>
>> # Write file footer
>> output += __file_footer()
>> @@ -92,6 +107,15 @@ be able to use it with DOLFIN.""", -1)
>> file.write(output)
>> file.close()
>> debug("Output written to " + filename)
>> +
>> + print swigmap
>> +
>> + # Write SWIG file
>> + swigfilename = name + ".i"
>> + swigfile = open(swigfilename, "w")
>> + swigfile.write(swig(swigmap))
>> + swigfile.close()
>> + debug("Swig output written to " + filename)
>>
>> # Write XML files if compiling for BLAS
>> if options["blas"]:
>> @@ -196,26 +220,36 @@ def __file_footer_element():
>>
>> #endif\n"""
>>
>> -def __elements(form):
>> +def __elements(form, subclass, swigmap, prototype = False):
>> "Generate finite elements for DOLFIN."
>>
>> output = ""
>>
>> # Write test element (if any)
>> if form.test:
>> - output += __element(form.test, "TestElement")
>> + output += __element(form.test, subclass,
>> + "TestElement", prototype)
>> + swigmap["dolfin::" + form.name + "::" + subclass + "::" + "TestElement"] = \
>> + form.name + subclass + "TestElement"
>>
>> # Write trial element (if any)
>> if form.trial:
>> - output += __element(form.trial, "TrialElement")
>> + output += __element(form.trial, subclass,
>> + "TrialElement", prototype)
>> + swigmap["dolfin::" + form.name + "::" + subclass + "::" + "TrialElement"] = \
>> + form.name + subclass + "TrialElement"
>>
>> # Write function elements (if any)
>> for j in range(len(form.elements)):
>> - output += __element(form.elements[j], "FunctionElement_%d" % j)
>> + output += __element(form.elements[j], subclass,
>> + "FunctionElement_%d" % j, prototype)
>> + swigmap["dolfin::" + form.name + "::" + subclass + "::" + \
>> + "FunctionElement_%d" % j] = \
>> + form.name + subclass + "FunctionElement_%d" % j
>>
>> return output
>>
>> -def __element(element, name):
>> +def __element(element, subclass, name, prototype = False):
>> "Generate finite element for DOLFIN."
>>
>> # Generate code for initialization of tensor dimensions
>> @@ -267,7 +301,8 @@ def __element(element, name):
>> subelements = ""
>> if isinstance(element, MixedElement):
>> for i in range(len(element.elements)):
>> - subelements += __element(element.elements[i], "SubElement_%d" % i)
>> + subelements += __element(element.elements[i], "",
>> + "SubElement_%d" % i)
>>
>> # Generate code for FiniteElementSpec
>> if element.type_str == "mixed":
>> @@ -280,9 +315,23 @@ def __element(element, name):
>> (element.type_str, element.shape_str, element.degree())
>>
>> # Generate output
>> - output = """\
>> -
>> -class %s : public dolfin::FiniteElement
>> + if subclass != "":
>> + subclass += "::"
>> +
>> + if prototype == True:
>> + # Write element prototype
>> +
>> + output = """\
>> + class %s;
>> +
>> +""" % (name)
>> + return output
>> + else:
>> + # Write element implementation
>> +
>> + output = """\
>> +
>> +class %s%s : public dolfin::FiniteElement
>> {
>> public:
>>
>> @@ -360,7 +409,7 @@ private:
>> FiniteElement** subelements;
>>
>> };
>> -""" % (name, name,
>> +""" % (subclass, name, name,
>> diminit,
>> elementinit,
>> name,
>> @@ -377,9 +426,9 @@ private:
>> spec,
>> subelements)
>>
>> - return indent(output, 2)
>> -
>> -def __form(form, type, options, xmlfile):
>> + return output
>> +
>> +def __form(form, type, options, xmlfile, swigmap, prototype = False):
>> "Generate form for DOLFIN."
>>
>> #ptr = "".join(['*' for i in range(form.rank)])
>> @@ -395,8 +444,10 @@ def __form(form, type, options, xmlfile)
>> if constinit:
>> constinit = ", " + constinit
>>
>> - # Write class header
>> - output = """\
>> + if prototype == True:
>> + # Write form prototype
>> +
>> + output = """\
>> /// This class contains the form to be evaluated, including
>> /// contributions from the interior and boundary of the domain.
>>
>> @@ -405,98 +456,125 @@ public:
>> public:
>> """ % (subclass, baseclass)
>>
>> - # Write elements
>> - output += __elements(form)
>> -
>> - # Write constructor
>> - output += """\
>> -
>> - %s(%s) : dolfin::%s(%d)%s
>> - {
>> -""" % (subclass, arguments, baseclass, form.nfunctions, constinit)
>> -
>> - # Initialize test and trial elements
>> - if form.test:
>> - output += """\
>> - // Create finite element for test space
>> - _test = new TestElement();
>> -"""
>> - if form.trial:
>> - output += """\
>> -
>> - // Create finite element for trial space
>> - _trial = new TrialElement();
>> -"""
>> + # Write element prototypes
>> + output += __elements(form, subclass, swigmap, True)
>> +
>> + # Write constructor
>> + output += """\
>> +
>> + %s(%s);
>> +""" % (subclass, arguments)
>> +
>> + # Interior contribution
>> + output += """\
>> +
>> + void eval(real block[], const AffineMap& map) const;
>> +"""
>> +
>> + # Boundary contribution (if any)
>> + output += """\
>> +
>> + void eval(real block[], const AffineMap& map, unsigned int facet) const;
>> +"""
>> +
>> + # Declare class members (if any)
>> + if form.nconstants > 0:
>> + output += """\
>> +
>> +private:
>> +
>> +"""
>> +
>> + # Create declaration list for for constants (if any)
>> + if form.nconstants > 0:
>> + for j in range(form.nconstants):
>> + output += """\
>> + const real& c%d;""" % j
>> + output += "\n"
>> +
>> + # Class footer
>> + output += """
>> +};
>> +
>> +"""
>> +
>> + # Write elements
>> + output += __elements(form, subclass, swigmap, False)
>> +
>> + else:
>> + # Write form implementation
>>
>> - # Add functions (if any)
>> - if form.nfunctions > 0:
>> - output += """\
>> -
>> - // Add functions\n"""
>> - for j in range(form.nfunctions):
>> - output += " add(w%d, new FunctionElement_%d());\n" % (j, j)
>> -
>> - # Initialize BLAS array (if any)
>> - if options["blas"]:
>> - output += """\
>> -
>> - // Initialize form data for BLAS
>> - blas.init(\"%s\");\n""" % xmlfile
>> -
>> - output += " }\n"
>> -
>> - # Interior contribution (if any)
>> - if form.AKi.terms:
>> - eval = __eval_interior(form, options)
>> - output += """\
>> -
>> - void eval(real block[], const AffineMap& map) const
>> - {
>> -%s }
>> -""" % eval
>> - else:
>> - output += """\
>> -
>> - // No contribution from the interior
>> - void eval(real block[], const AffineMap& map) const {}
>> -"""
>> -
>> - # Boundary contribution (if any)
>> - if form.AKb[0].terms:
>> - eval = __eval_boundary(form, options)
>> - output += """\
>> -
>> - void eval(real block[], const AffineMap& map, unsigned int facet) const
>> - {
>> -%s }
>> -""" % eval
>> - else:
>> - output += """\
>> -
>> - // No contribution from the boundary
>> - void eval(real block[], const AffineMap& map, unsigned int facet) const {}
>> -"""
>> -
>> - # Declare class members (if any)
>> - if form.nconstants > 0:
>> - output += """\
>> -
>> -private:
>> -
>> -"""
>> -
>> - # Create declaration list for for constants (if any)
>> - if form.nconstants > 0:
>> - for j in range(form.nconstants):
>> - output += """\
>> - const real& c%d;""" % j
>> - output += "\n"
>> -
>> - # Class footer
>> - output += """
>> -};
>> -
>> -"""
>> + # Write constructor
>> + output = """\
>> +
>> +%s::%s(%s) : dolfin::%s(%d)%s
>> +{
>> +""" % (subclass, subclass, arguments, baseclass, form.nfunctions, constinit)
>> +
>> + # Initialize test and trial elements
>> + if form.test:
>> + output += """\
>> + // Create finite element for test space
>> + _test = new TestElement();
>> +"""
>> + if form.trial:
>> + output += """\
>> +
>> + // Create finite element for trial space
>> + _trial = new TrialElement();
>> +"""
>> +
>> + # Add functions (if any)
>> + if form.nfunctions > 0:
>> + output += """\
>> +
>> + // Add functions\n"""
>> + for j in range(form.nfunctions):
>> + output += " add(w%d, new FunctionElement_%d());\n" % (j, j)
>> +
>> + # Initialize BLAS array (if any)
>> + if options["blas"]:
>> + output += """\
>> +
>> + // Initialize form data for BLAS
>> + blas.init(\"%s\");\n""" % xmlfile
>> +
>> + output += "}\n"
>> +
>> + # Interior contribution (if any)
>> + if form.AKi.terms:
>> + eval = __eval_interior(form, options)
>> + output += """\
>> +
>> +void %s::eval(real block[], const AffineMap& map) const
>> +{
>> +%s}
>> +""" % (subclass, eval)
>> + else:
>> + output += """\
>> +
>> +// No contribution from the interior
>> +void %s::eval(real block[], const AffineMap& map) const {}
>> +""" % subclass
>> +
>> + # Boundary contribution (if any)
>> + if form.AKb[0].terms:
>> + eval = __eval_boundary(form, options)
>> + output += """\
>> +
>> +void %s::eval(real block[], const AffineMap& map, unsigned int facet) const
>> +{
>> +%s}
>> +""" % (subclass, eval)
>> + else:
>> + output += """\
>> +
>> +// No contribution from the boundary
>> +void %s::eval(real block[], const AffineMap& map, unsigned int facet) const {}
>> +""" % subclass
>> +
>> + swigmap["dolfin::" + form.name + "::" + subclass] = \
>> + form.name + subclass
>>
>> return output
>>
>> @@ -514,22 +592,22 @@ def __eval_interior_default(form, option
>> if not options["debug-no-geometry-tensor"]:
>> if len(form.cKi) > 0:
>> output += """\
>> - // Compute coefficients
>> -%s
>> -""" % "".join([" const real %s = %s;\n" % (cKi.name, cKi.value) for cKi in form.cKi if cKi.used])
>> - output += """\
>> - // Compute geometry tensors
>> -%s""" % "".join([" const real %s = %s;\n" % (gK.name, gK.value) for gK in form.AKi.gK if gK.used])
>> - else:
>> - output += """\
>> - // Compute geometry tensors
>> -%s""" % "".join([" const real %s = 0.0;\n" % gK.name for gK in form.AKi.gK if gK.used])
>> + // Compute coefficients
>> +%s
>> +""" % "".join([" const real %s = %s;\n" % (cKi.name, cKi.value) for cKi in form.cKi if cKi.used])
>> + output += """\
>> + // Compute geometry tensors
>> +%s""" % "".join([" const real %s = %s;\n" % (gK.name, gK.value) for gK in form.AKi.gK if gK.used])
>> + else:
>> + output += """\
>> + // Compute geometry tensors
>> +%s""" % "".join([" const real %s = 0.0;\n" % gK.name for gK in form.AKi.gK if gK.used])
>>
>> if not options["debug-no-element-tensor"]:
>> output += """\
>>
>> - // Compute element tensor
>> -%s""" % "".join([" %s = %s;\n" % (aK.name, aK.value) for aK in form.AKi.aK])
>> + // Compute element tensor
>> +%s""" % "".join([" %s = %s;\n" % (aK.name, aK.value) for aK in form.AKi.aK])
>>
>> return output
>>
>> @@ -541,24 +619,24 @@ def __eval_interior_blas(form, options):
>> if not options["debug-no-geometry-tensor"]:
>> if len(form.cKi) > 0:
>> output += """\
>> - // Compute coefficients
>> -%s
>> -""" % "".join([" const real %s = %s;\n" % (cKi.name, cKi.value) for cKi in form.cKi if cKi.used])
>> - output += """\
>> - // Reset geometry tensors
>> - for (unsigned int i = 0; i < blas.ni; i++)
>> - blas.Gi[i] = 0.0;
>> -
>> - // Compute entries of G multiplied by nonzero entries of A
>> -%s
>> -""" % "".join([" blas.Gi[%d] = %s;\n" % (j, form.AKi.gK[j].value)
>> + // Compute coefficients
>> +%s
>> +""" % "".join([" const real %s = %s;\n" % (cKi.name, cKi.value) for cKi in form.cKi if cKi.used])
>> + output += """\
>> + // Reset geometry tensors
>> + for (unsigned int i = 0; i < blas.ni; i++)
>> + blas.Gi[i] = 0.0;
>> +
>> + // Compute entries of G multiplied by nonzero entries of A
>> +%s
>> +""" % "".join([" blas.Gi[%d] = %s;\n" % (j, form.AKi.gK[j].value)
>> for j in range(len(form.AKi.gK)) if form.AKi.gK[j].used])
>>
>> # Compute element tensor
>> if not options["debug-no-element-tensor"]:
>> output += """\
>> - // Compute element tensor using level 2 BLAS
>> - cblas_dgemv(CblasRowMajor, CblasNoTrans, blas.mi, blas.ni, 1.0, blas.Ai, blas.ni, blas.Gi, 1, 0.0, block, 1);
>> + // Compute element tensor using level 2 BLAS
>> + cblas_dgemv(CblasRowMajor, CblasNoTrans, blas.mi, blas.ni, 1.0, blas.Ai, blas.ni, blas.Gi, 1, 0.0, block, 1);
>> """
>>
>> return output
>> @@ -577,31 +655,31 @@ def __eval_boundary_default(form, option
>> if not options["debug-no-geometry-tensor"]:
>> if len(form.cKb) > 0:
>> output += """\
>> - // Compute coefficients
>> -%s
>> -""" % "".join([" const real %s = %s;\n" % (cKb.name, cKb.value) for cKb in form.cKb if cKb.used])
>> - output += """\
>> - // Compute geometry tensors
>> -%s""" % "".join([" const real %s = %s;\n" % (gK.name, gK.value) for gK in form.AKb[-1].gK if gK.used])
>> - else:
>> - output += """\
>> - // Compute geometry tensors
>> -%s""" % "".join([" const real %s = 0.0;\n" % gK.name for gK in form.AKb[-1].gK if gK.used])
>> + // Compute coefficients
>> +%s
>> +""" % "".join([" const real %s = %s;\n" % (cKb.name, cKb.value) for cKb in form.cKb if cKb.used])
>> + output += """\
>> + // Compute geometry tensors
>> +%s""" % "".join([" const real %s = %s;\n" % (gK.name, gK.value) for gK in form.AKb[-1].gK if gK.used])
>> + else:
>> + output += """\
>> + // Compute geometry tensors
>> +%s""" % "".join([" const real %s = 0.0;\n" % gK.name for gK in form.AKb[-1].gK if gK.used])
>>
>> if not options["debug-no-element-tensor"]:
>> output += """\
>>
>> - // Compute element tensor
>> - switch ( facet )
>> - { """
>> + // Compute element tensor
>> + switch ( facet )
>> + { """
>> for akb in form.AKb:
>> output += """
>> - case %s:""" % akb.facet
>> + case %s:""" % akb.facet
>> output += """
>> -%s break; \n""" % "".join([" %s = %s;\n" % (aK.name, aK.value) for aK in akb.aK])
>> -
>> - output += """\
>> - } \n"""
>> +%s break; \n""" % "".join([" %s = %s;\n" % (aK.name, aK.value) for aK in akb.aK])
>> +
>> + output += """\
>> + } \n"""
>> return output
>>
>> def __eval_boundary_blas(form, options):
>> @@ -612,24 +690,24 @@ def __eval_boundary_blas(form, options):
>> if not options["debug-no-geometry-tensor"]:
>> if len(form.cKb) > 0:
>> output += """\
>> - // Compute coefficients
>> -%s
>> -""" % "".join([" const real %s = %s;\n" % (cKb.name, cKb.value) for cKb in form.cKb if cKb.used])
>> - output += """\
>> - // Reset geometry tensors
>> - for (unsigned int i = 0; i < blas.nb; i++)
>> - blas.Gb[i] = 0.0;
>> -
>> - // Compute entries of G multiplied by nonzero entries of A
>> -%s
>> -""" % "".join([" blas.Gb[%d] = %s;\n" % (j, form.AKb.gK[j].value)
>> + // Compute coefficients
>> +%s
>> +""" % "".join([" const real %s = %s;\n" % (cKb.name, cKb.value) for cKb in form.cKb if cKb.used])
>> + output += """\
>> + // Reset geometry tensors
>> + for (unsigned int i = 0; i < blas.nb; i++)
>> + blas.Gb[i] = 0.0;
>> +
>> + // Compute entries of G multiplied by nonzero entries of A
>> +%s
>> +""" % "".join([" blas.Gb[%d] = %s;\n" % (j, form.AKb.gK[j].value)
>> for j in range(len(form.AKb.gK)) if form.AKb.gK[j].used])
>>
>> # Compute element tensor
>> if not options["debug-no-element-tensor"]:
>> output += """\
>> - // Compute element tensor using level 2 BLAS
>> - cblas_dgemv(CblasRowMajor, CblasNoTrans, blas.mb, blas.nb, 1.0, blas.Ab, blas.nb, blas.Gb, 1, 0.0, block, 1);
>> + // Compute element tensor using level 2 BLAS
>> + cblas_dgemv(CblasRowMajor, CblasNoTrans, blas.mb, blas.nb, 1.0, blas.Ab, blas.nb, blas.Gb, 1, 0.0, block, 1);
>> """
>>
>> return output
>
>> _______________________________________________
>> FFC-dev mailing list
>> FFC-dev@xxxxxxxxxx
>> http://www.fenics.org/mailman/listinfo/ffc-dev
>
> _______________________________________________
> FFC-dev mailing list
> FFC-dev@xxxxxxxxxx
> http://www.fenics.org/mailman/listinfo/ffc-dev
>
Follow ups
References