New publication in JACS

| categories: publication, news | tags:

Electrocatalytic Oxygen Evolution with an Immobilized TAML Activator

Ethan L. Demeter, Shayna L. Hilburg, Newell R. Washburn, Terrence J. Collins, and John R. Kitchin

Iron complexes of tetra-amido macrocyclic ligands are important members of the suite of oxidation catalysts known as TAML activators. TAML activators are known to be fast homogeneous water oxidation (WO) catalysts, producing oxygen in the presence of chemical oxidants, e.g., ceric ammonium nitrate. These homogeneous systems exhibited low turnover numbers (TONs). Here we demonstrate immobilization on glassy carbon and carbon paper in an ink composed of the prototype TAML activator, carbon black, and Nafion and the subsequent use of this composition in heterogeneous electrocatalytic WO. The immobilized TAML system is shown to readily produce O2 with much higher TONs than the homogeneous predecessors.

http://pubs.acs.org/doi/full/10.1021/ja5015986

Congratulations Ethan!

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.5h

Discuss on Twitter

Scheduling tasks on a rotating semester basis

| categories: python | tags:

Let us say we have a list of tasks labeled task a through k. We want to schedule these tasks on a rotating basis, so that some tasks are done in even years and some tasks are done in odd years. Within those years, some tasks are done in the Fall, and some are done in the spring. This post explores how to code those tasks so we can figure out which tasks should be done in some part of some year.

We break the problem down like this. A year is an even year if mod(year,2)=0, and it is odd if mod(year,2)=1. So for a year, we have a bit of information. Now, since there are two times of the year we will do the tasks, we can assign this as another bit, e.g. FALL=0, and SPRING=1. Now, we have the following possibilities:

year time period binary code decimal number
2013 Fall 10 2
2014 Spring 01 1
2014 Fall 00 0
2015 Spring 11 3

And then the cycle will repeat. So, if we code each task with an integer of 0, 1, 2 or 3, we can say in a given year and time period whether a task should be completed. If 2 * mod(year, 2) + period_code is equal to the code on the task, then it should be executed.

Now, we need to start the task sequence. Let us say we start in the Fall of 2013. That is an odd year, so year % 2 = 1, and we use a tag of 0 to represent the Fall semester, giving an overall binary code of 10 which is equal to 2, so all tasks labeled 2 should be executed.

We will assign the codes to each task by enumerating a string of letters, and giving the task a code of mod(letter index, 4). That will loop through the tasks assigning codes of 0, 1, 2 or 3 to each task.

So to schedule these we will loop through a list of years, calculate the code for each year and time perid, and then filter the list of tasks with that code.

tasks = [(letter, i % 4) for i,letter in enumerate('abcdefghijk')]

print 'tasks = ',tasks

SEMESTERS = (('FALL',0), ('SPRING',1))

for year in [2013, 2014, 2015, 2016, 2017, 2018]:
    for semester,i in SEMESTERS:
        N = 2 * (year % 2) + i
        print '{0} {1:8s}: {2}'.format(year, semester,
                                    [x[0] for x in 
                                     filter(lambda x: x[1]==N,
                                            tasks)])
tasks =  [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 0), ('f', 1), ('g', 2), ('h', 3), ('i', 0), ('j', 1), ('k', 2)]
2013 FALL    : ['c', 'g', 'k']
2013 SPRING  : ['d', 'h']
2014 FALL    : ['a', 'e', 'i']
2014 SPRING  : ['b', 'f', 'j']
2015 FALL    : ['c', 'g', 'k']
2015 SPRING  : ['d', 'h']
2016 FALL    : ['a', 'e', 'i']
2016 SPRING  : ['b', 'f', 'j']
2017 FALL    : ['c', 'g', 'k']
2017 SPRING  : ['d', 'h']
2018 FALL    : ['a', 'e', 'i']
2018 SPRING  : ['b', 'f', 'j']

This leads to each task being completed every other year. We could also write a function and filter by list comprehension.

tasks = [(letter, i % 4) for i,letter in enumerate('abcdefghijk')]

FALL = 0
SPRING = 1

def execute_p(year, semester, task):
    'year is an integer, semester is 0 for fall, 1 for spring, task is a tuple of (label,code)'
    N = 2 * (year % 2) + semester
    return task[1] == N

YEAR, SEMESTER = 2018, FALL
print '{0} {1:8s}: {2}'.format(YEAR, 
                               'FALL' if SEMESTER==0 else 'SPRING',
                               [task[0]  for task in tasks
                                if execute_p(2018, FALL, task)])
2018 FALL    : ['a', 'e', 'i']

Now, at any point in the future you can tell what tasks should be done!

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.5h

Discuss on Twitter

Using pyparsing for search queries with tags

| categories: python | tags:

A few times I have wanted to use a more natural search string like "A and pw and 350 and not kpt". The trouble is figuring out how to parse that string and turn it into search code. There may be nested logic, e.g. "(A xor B) and (pw and (200 or 300))". This means we have to recursively parse the sstring. Rather than invent this from scratch, we use pyparsing which is designed for that. There is some code in "Getting started with pyparsing" that provides an example on parsing search strings. I want to see how I can turn the parsed output into search code. Here, we parse the search string and generate something that looks like lisp code.

1 Parsing simple string and generating lisp

We define a hiearchy of classes that codifythe operators, and which print representations of the logic. The grammar we implement is basically words or strings separatedd by logic operators.

from pyparsing import *

class UnaryOperation(object):
    'takes one operand,e.g. not'
    def __init__(self, tokens):
        self.op, self.operands = tokens[0]

class BinaryOperation(object):
    'takes two or more operands, e.g. and, or'
    def __init__(self, tokens):
        self.op = tokens[0][1]
        self.operands = tokens[0][0::2]

class SearchAnd(BinaryOperation):
    def __repr__(self):
        return '(AND {0})'.format(' '.join(str(oper) for oper in self.operands))
        
class SearchOr(BinaryOperation):
    def __repr__(self):
        return '(OR {0})'.format(' '.join(str(oper) for oper in self.operands))

class SearchNot(UnaryOperation):
    def __repr__(self):
        return '(NOT {0})'.format(self.operands)

class SearchTerm(object):
    'represents a termthat is being searched. here just a word'                         
    def __init__(self, tokens):
        self.term = tokens[0]

    def __repr__(self):
        return self.term

# the grammar
and_ = CaselessLiteral("and")
or_ = CaselessLiteral("or")
not_ = CaselessLiteral("not")

searchTerm = Word(alphanums) | quotedString.setParseAction(removeQuotes)
searchTerm.setParseAction(SearchTerm)

searchExpr = operatorPrecedence( searchTerm,
                                 [(not_, 1, opAssoc.RIGHT, SearchNot),
                                  (and_, 2, opAssoc.LEFT, SearchAnd),
                                  (or_, 2, opAssoc.LEFT, SearchOr)])


print searchExpr.parseString('not kpt')[0]
print searchExpr.parseString('not (kpt and eos)')[0]
print searchExpr.parseString('wood and blue or red')[0]
print searchExpr.parseString('wood and blue and heavy or red')[0]
(NOT kpt)
(NOT (AND kpt eos))
(OR (AND wood blue) red)
(OR (AND wood blue heavy) red)

That works pretty well, and does not seem overly complicated to me. There is a lot of class definition, but that would presumably get buried in a module as a one time investment, and some function interface would look like this: search('wood and blue or red').

Now, let us try python notation.

2 Parsing a search string to generate python set notations

I will use a similar idea as I used before with TAGS. We will use set operations with the binary logical operators to do the actual searching. Finally, we wrap the code in a little function to search a dictionary we previously made.

from pyparsing import *

class UnaryOperation(object):
    def __init__(self, tokens):
        self.op, self.operands = tokens[0]

class BinaryOperation(object):
    def __init__(self, tokens):
        self.op = tokens[0][1]
        self.operands = tokens[0][0::2]

class SearchAnd(BinaryOperation):
    def __repr__(self):
        return '(' + ' & '.join(['{}'.format(oper) for oper in self.operands]) + ')'
        
class SearchOr(BinaryOperation):
    def __repr__(self):
        return '(' + ' | '.join(['{}'.format(oper) for oper in self.operands]) +')'

class SearchXor(BinaryOperation):
    def __repr__(self):
        return '(' + ' ^ '.join(['{}'.format(oper) for oper in self.operands]) + ')'

class SearchNot(UnaryOperation):
    def __repr__(self):
        return 'TAGS[\'all\'] - {}'.format(self.operands)

class SearchTerm(object):
    def __init__(self, tokens):
        self.term = tokens[0]

    def __repr__(self):
        'instead of just the  term, we represent it as TAGS[term]'
        return 'TAGS[\'{0}\']'.format(self.term)

# the grammar
and_ = CaselessLiteral("and")
or_ = CaselessLiteral("or")
xor_ = CaselessLiteral("xor")
not_ = CaselessLiteral("not")

searchTerm = Word(alphanums) | quotedString.setParseAction(removeQuotes)
searchTerm.setParseAction(SearchTerm)

searchExpr = operatorPrecedence( searchTerm,
                                 [(not_, 1, opAssoc.RIGHT, SearchNot),
                                  (and_, 2, opAssoc.LEFT, SearchAnd),
                                  (xor_, 2, opAssoc.LEFT, SearchXor),
                                  (or_, 2, opAssoc.LEFT, SearchOr)])

print searchExpr.parseString('not kpt')[0]
print searchExpr.parseString('not (kpt and eos)')[0]
print searchExpr.parseString('kpt or not eos)')[0]
print searchExpr.parseString('wood and blue or red')[0]
print searchExpr.parseString('wood and blue xor red')[0]

# check it out on tags.
def search_tags(srch):
    'function to  search the TAGS  file'
    import pickle

    with open('TAGS.pkl', 'r') as f:
        TAGS = pickle.loads(f.read())
    
    s = searchExpr.parseString(srch)[0]
    return eval(str(s))
print search_tags('pw and A and not 300')
TAGS['all'] - TAGS['kpt']
TAGS['all'] - (TAGS['kpt'] & TAGS['eos'])
(TAGS['kpt'] | TAGS['all'] - TAGS['eos'])
((TAGS['wood'] & TAGS['blue']) | TAGS['red'])
((TAGS['wood'] & TAGS['blue']) ^ TAGS['red'])
set(['tags\\A\\pw\\350', 'tags\\A\\pw', 'tags\\A\\pw\\200', 'tags\\A\\pw\\400', 'tags\\A\\pw\\250'])

That is pretty nice. It looks like a nice syntax for queries. One day I will try incorporating this into a database application.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.5h

Discuss on Twitter

Searching for directories by tags

| categories: python | tags:

Today I explore searching for directories by using tags. We will create a TAGS table by first walking through the directories and tagging each directory with all of the relative path components. Then, we will use set algebra to identify specific directories.

First, let us make a directory setup to use. We will nest some calculations that might be typical. These will be nested directories that might contain planewave (pw) and k-points (kpts) convergence and equation of state (eos) directories, with the sets of calculations inside those. The idea then is that the directory components will form at least some of the tags.

import os

os.mkdir('tags')

for cmpd in ['A', 'B', 'C', 'D']:
    for c in ['pw', 'kpts', 'eos']:
        os.makedirs(os.path.join('tags', cmpd, c))

for cmpd in ['A', 'B', 'C', 'D']:
    for run in [1, 2, 3, 4, 5]:
        os.makedirs(os.path.join('tags', cmpd, 'eos', str(run)))

for cmpd in ['A', 'B', 'C', 'D']:
    for run in [200, 250, 300, 350, 400]:
        os.makedirs(os.path.join('tags', cmpd, 'pw', str(run)))

for cmpd in ['A', 'B', 'C', 'D']:
    for run in ['2x2x2', '4x4x4', '8x8x8']:
        os.makedirs(os.path.join('tags', cmpd, 'kpts', str(run)))

Let us just double check what this directory tree looks like for one compound A.

ls tags/A/*
tags/A/eos:
1
2
3
4
5

tags/A/kpts:
2x2x2
4x4x4
8x8x8

tags/A/pw:
200
250
300
350
400

Now, we will walk through the directories, and split the path components to create a TAGS structure. I will store these as sets within a dictionary. We will save the structure in a pickle file to reuse it later.

import os
import pickle

TAGS = {}
TAGS['all'] = set()

for root, dirs, files in os.walk('tags'):
    base, tail = os.path.split(root)
    TAGS['all'].add(root)
    while base:
        if tail in TAGS:
            TAGS[tail].add(root)
        else:
            TAGS[tail] = set([root])
        base, tail = os.path.split(base)

with open('TAGS.pkl', 'w') as f:
    f.write(pickle.dumps(TAGS))

print TAGS.keys()
['A', '1', 'all', 'B', '250', 'pw', '2x2x2', '300', 'C', '400', 'kpts', '8x8x8', 'eos', '3', '2', '5', '4', '350', '200', '4x4x4', 'D']

Now we have a lot of keys that tag each directory. Each tag is a set of directories, and we can do set algebra to get specific results. For example, we can find a result by appropriate differences, intersections and unions of the sets. An advantage of this approach is that order of the tags is not relevant (unlike the path, where each component must be in the right order).

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

print TAGS['A'].intersection(TAGS['pw']).intersection(TAGS['300'])
print TAGS['300'].intersection(TAGS['pw']).intersection(TAGS['A'])
set(['tags\\A\\pw\\300'])
set(['tags\\A\\pw\\300'])

The syntax here is a tad heavy because of the chained dot notation operations. You can also use the logical operators like this:

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

# 300 and pw and A
print TAGS['300'] & TAGS['pw'] & TAGS['A']
set(['tags\\A\\pw\\300'])

We can get a set of calculations, for example an equation of state like this:

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

# 300 and pw and A
print TAGS['A'] & TAGS['eos']
set(['tags\\A\\eos', 'tags\\A\\eos\\5', 'tags\\A\\eos\\4', 'tags\\A\\eos\\1', 'tags\\A\\eos\\3', 'tags\\A\\eos\\2'])

Now, let us construct some more complex queries. With sets we use intersections for and and we construct unions of queries that are like an or. We examine different notations to see which one is better.

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

# find calculation 3 in eos for B and C
print (TAGS['3']
       .intersection(TAGS['eos'])
       .intersection(TAGS['B'])
       .union
       (TAGS['3']
        .intersection(TAGS['eos'])
        .intersection(TAGS['C'])))

# this notation makes more sense to me. ^ = or
print TAGS['3'] & TAGS['eos'] & (TAGS['B'] ^ TAGS['C'])
set(['tags\\C\\eos\\3', 'tags\\B\\eos\\3'])
set(['tags\\C\\eos\\3', 'tags\\B\\eos\\3'])

You can see the two approaches give the same results. The logical operator syntax is more concise and (I think) more readable. Let us consider a query with "not". We can use a difference operator for that. We subtract all the paths with tag "B" from the set containing "4x4x4", which will give us paths tagged with "4x4x4" but not "B".

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

# find calculation 4x4x4 but not B
print (TAGS['4x4x4'].difference(TAGS['B']))

# this makes more sense 4x4x4 but not B, 
# i.e. subtract paths tagged B from those tagged 4x4x4
print TAGS['4x4x4'] -  TAGS['B']
set(['tags\\D\\kpts\\4x4x4', 'tags\\A\\kpts\\4x4x4', 'tags\\C\\kpts\\4x4x4'])
set(['tags\\D\\kpts\\4x4x4', 'tags\\A\\kpts\\4x4x4', 'tags\\C\\kpts\\4x4x4'])

Note it is not so obvious how to get results not tagged with "A". We need to subtract the tagged calculations from some set.

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

print TAGS['all'] - TAGS['A']  # not A
set(['tags\\D\\kpts', 'tags\\D\\pw\\200', 'tags\\D\\pw\\350', 'tags\\D\\pw\\250', 'tags\\D\\kpts\\8x8x8', 'tags\\C\\eos', 'tags\\D\\eos\\2', 'tags\\D\\eos\\3', 'tags\\D\\eos\\4', 'tags\\D\\eos\\5', 'tags\\B\\kpts\\2x2x2', 'tags\\C\\kpts\\4x4x4', 'tags\\C\\eos\\3', 'tags\\C\\eos\\2', 'tags\\C\\eos\\1', 'tags\\C\\kpts\\8x8x8', 'tags\\C\\eos\\5', 'tags\\C\\eos\\4', 'tags\\B\\kpts', 'tags\\C\\pw\\200', 'tags\\B\\eos\\2', 'tags\\B\\pw\\350', 'tags\\B\\eos\\1', 'tags\\B\\kpts\\8x8x8', 'tags\\C\\pw\\300', 'tags\\B\\eos\\4', 'tags\\B\\eos\\5', 'tags\\C\\kpts', 'tags\\D\\pw\\300', 'tags\\B\\kpts\\4x4x4', 'tags\\C\\kpts\\2x2x2', 'tags\\D\\kpts\\4x4x4', 'tags\\B\\pw\\250', 'tags', 'tags\\D\\pw\\400', 'tags\\D\\eos', 'tags\\C\\pw\\400', 'tags\\D\\kpts\\2x2x2', 'tags\\D\\pw', 'tags\\C\\pw\\250', 'tags\\C\\pw\\350', 'tags\\C\\pw', 'tags\\D\\eos\\1', 'tags\\B\\pw\\400', 'tags\\B\\pw', 'tags\\B\\eos', 'tags\\B\\pw\\300', 'tags\\B\\eos\\3', 'tags\\C', 'tags\\B', 'tags\\D', 'tags\\B\\pw\\200'])

We can also look at "or". This is done with the union function, which is the set of elements in either set. The logical operator is |.

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

print TAGS['1'].union(TAGS['2'])
print TAGS['1'] | TAGS['2']        # 1 or 2
set(['tags\\B\\eos\\2', 'tags\\B\\eos\\1', 'tags\\A\\eos\\1', 'tags\\A\\eos\\2', 'tags\\D\\eos\\1', 'tags\\D\\eos\\2', 'tags\\C\\eos\\2', 'tags\\C\\eos\\1'])
set(['tags\\B\\eos\\2', 'tags\\B\\eos\\1', 'tags\\A\\eos\\1', 'tags\\A\\eos\\2', 'tags\\D\\eos\\1', 'tags\\D\\eos\\2', 'tags\\C\\eos\\2', 'tags\\C\\eos\\1'])

There is an xor operator too. xor is not the same as or, it means A xor B means "A or B but not both". The symmetric_difference function gives this behavior. The logical operator is ^.

import pickle

with open('TAGS.pkl', 'r') as f:
    TAGS = pickle.loads(f.read())

# I think this is like the xor, A or 2 but not both
print TAGS['A'].symmetric_difference(TAGS['2']), '\n'
print TAGS['A'] ^ TAGS['2']
set(['tags\\A\\pw\\300', 'tags\\A\\pw\\250', 'tags\\A\\eos', 'tags\\B\\eos\\2', 'tags\\A\\eos\\4', 'tags\\A\\kpts\\2x2x2', 'tags\\A\\pw\\400', 'tags\\A\\kpts\\4x4x4', 'tags\\A\\eos\\3', 'tags\\A\\kpts\\8x8x8', 'tags\\A\\pw\\350', 'tags\\A\\eos\\5', 'tags\\D\\eos\\2', 'tags\\A\\kpts', 'tags\\A', 'tags\\C\\eos\\2', 'tags\\A\\eos\\1', 'tags\\A\\pw', 'tags\\A\\pw\\200']) 

set(['tags\\A\\pw\\300', 'tags\\A\\pw\\250', 'tags\\A\\eos', 'tags\\B\\eos\\2', 'tags\\A\\eos\\4', 'tags\\A\\kpts\\2x2x2', 'tags\\A\\pw\\400', 'tags\\A\\kpts\\4x4x4', 'tags\\A\\eos\\3', 'tags\\A\\kpts\\8x8x8', 'tags\\A\\pw\\350', 'tags\\A\\eos\\5', 'tags\\D\\eos\\2', 'tags\\A\\kpts', 'tags\\A', 'tags\\C\\eos\\2', 'tags\\A\\eos\\1', 'tags\\A\\pw', 'tags\\A\\pw\\200'])

To summarize, this is one approach to using tags with the technical infrastructure of the set. The advantage is flexibility, that you do not need to know the full path to a result, provided you know the set of tags that refers to it. You can use the tags in any order.

There are many alternative approaches to implementing this idea. One could create a sqlite table and do SQL queries. You could also store lists in the dictionary, and use python code to find the matches. The syntax here is varied. Using the functional approach, the syntax gets heavy with all the dot notation. With the logical operators, the syntax is a little lighter.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.5h

Discuss on Twitter

Literate programming in python with org-mode and noweb

| categories: org-mode, python | tags:

This post examines a different approach to literate programming with org-mode that uses noweb . I have adapted an example from http://home.fnal.gov/~neilsen/notebook/orgExamples/org-examples.html which has some pretty cool ideas in it.

The gist of using noweb is that in your source blocks you have labels like <<imports>>, that refer to other named code blocks that get substituted in place of the label. In the example below, we put labels for a code block of imports, for a function definition, a class definition, and a main function. This code block will get tangled to main.py . The noweb expansion happens at export, so here is the literal code block:

#+BEGIN_SRC python :noweb yes :tangle main.py
<<imports>>

<<some-func>>

<<class-dfn>>

<<main-func>>

if __name__ == '__main__':
    status = main()
    sys.exit(status)
#+END_SRC

You may want to just check out the org-mode source link at the bottom of the post to see all the details.

import sys
import numpy as np
import matplotlib.pyplot as plt

from argparse import ArgumentParser

def utility_func(arg=None):
    return 'you called a utility function with this arg: {0}'.format(arg)

class HelloWorld(object):
    def __init__(self, who):
        self.who = who

    def __call__(self):
        return 'Hello {0}'.format(self.who)

    def test(self):
        return True

def main():
    parser = ArgumentParser(description="Say hi")
    parser.add_argument("-w", "--who", 
                        type=str,
                        default="world",
                        help="Who to say hello to")
    args = parser.parse_args()
  
    who = args.who
  
    greeter = HelloWorld(who)
    greeter()

    print 'test func = ', greeter.test()
  
    print utility_func()
    print utility_func(5)

    return 0

if __name__ == '__main__':
    status = main()
    sys.exit(status)

1 imports

Now, we define a block that gives us the imports. We do not have to use any tangle headers here because noweb will put it in where it belongs.

import sys
import numpy as np
import matplotlib.pyplot as plt

from argparse import ArgumentParser

2 utility function

Now we define a function we will want imported from the main file.

def utility_func(arg=None):
    return 'you called a utility function with this arg: {0}'.format(arg)

3 class definition

Finally, let us define a class. Note we use noweb here too, and we get the indentation correct!

class HelloWorld(object):
    def __init__(self, who):
        self.who = who

    def __call__(self):
        return 'Hello {0}'.format(self.who)

    def test(self):
        return True

3.1 some class function

Now, let us make the some-other-func. This block is not indented, but with the noweb syntax above, it seems to get correctly indented. Amazing.

def test(self):
    return True

4 The main function

This is a typical function that could be used to make your module into a script, and is only run when the module is used as a script..

def main():
    parser = ArgumentParser(description="Say hi")
    parser.add_argument("-w", "--who", 
                        type=str,
                        default="world",
                        help="Who to say hello to")
    args = parser.parse_args()
  
    who = args.who
  
    greeter = HelloWorld(who)
    greeter()

    print 'test func = ', greeter.test()
  
    print utility_func()
    print utility_func(5)

    return 0

5 Tangle and run the code

This link will extract the code to main.py:

elisp:org-babel-tangle

We can run the code like this (linux):

python main.py --w John 2>&1
true
test func =  True
you called a utility function with this arg: None
you called a utility function with this arg: 5

or this (windows, which as no sh)

from main import *

main()
test func =  True
you called a utility function with this arg: None
you called a utility function with this arg: 5

6 Summary thoughts

The use of noweb syntax is pretty cool. I have not done anything serious with it, but it looks like you could pretty easily create a sophisticated python module this way that is documented in org-mode.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.5h

Discuss on Twitter
« Previous Page -- Next Page »