diff -Naur orig/taxonomy_nco_analysis.module patched/taxonomy_nco_analysis.module
--- orig/taxonomy_nco_analysis.module   2012-03-15 13:16:20.000000000 +0100
+++ patched/taxonomy_nco_analysis.module        2012-06-21 14:34:09.000000000 +0200
@@ -185,7 +185,7 @@
 function taxonomy_nco_analysis_admin_form($form_state) {
   $seed_term_ocurrence_threshold = variable_get('taxonomy_nco_analysis_seed_term_occurrence_threshold', SEED_TERM_OCCURENCE_THRESHOLD);
   $mininum_analysis_intersection = variable_get('taxonomy_nco_analysis_minimum_analysis_intersection', MINIMUM_ANALYSIS_INTERSECTION);
-  $vocabulary = variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY);
+  $selectedvocabularies = explode(',',variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY));
   $ongoing_analysis = variable_get('taxonomy_nco_analysis_ongoing_running', FALSE);
   $verbose_reporting = variable_get('taxonomy_nco_analysis_verbose_reporting', FALSE);
   $vocabularies = taxonomy_nco_analysis_vocabularies_all();
@@ -208,10 +208,11 @@
   );
   $form['taxonomy_vocabulary'] = array(
     '#type' => 'select',
+    '#multiple' => TRUE,
     '#title' => t('Vocabulary for NCO analysis'),
     '#options' => $vocabularies,
     '#description' => t('NCO analysis will only be performed on this vocabulary'),
-    '#default_value' => $vocabulary,
+    '#default_value' => $selectedvocabularies,
   );
   $form['ongoing_analysis_enabled'] = array(
     '#title' => t('Ongoing analysis enabled'),
@@ -264,12 +265,12 @@

   $seed_term_ocurrence_threshold = $form_state['values']['seed_term_occurrence_threshold'];
   $mininum_analysis_intersection = $form_state['values']['mininum_analysis_intersection'];
-  $vocabulary = $form_state['values']['taxonomy_vocabulary'];
+  $selectedvocabularies = $form_state['values']['taxonomy_vocabulary'];
   $ongoing_analysis = $form_state['values']['ongoing_analysis_enabled'];
   $verbose_reporting = $form_state['values']['verbose_reporting_enabled'];
   variable_set('taxonomy_nco_analysis_seed_term_occurrence_threshold', $seed_term_ocurrence_threshold);
   variable_set('taxonomy_nco_analysis_minimum_analysis_intersection', $mininum_analysis_intersection);
-  variable_set('taxonomy_nco_analysis_vocabulary', $vocabulary);
+  variable_set('taxonomy_nco_analysis_vocabulary', implode(',',$selectedvocabularies));
   if ($verbose_reporting) {
     variable_set('taxonomy_nco_analysis_verbose_reporting', TRUE);
   }
@@ -505,14 +506,14 @@
  */
 function taxonomy_nco_analysis_tids_on_nodes_updated_between($time, $now) {
   // Get tids that may have been updated
-  $vocabulary = variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY);
+  $vocabularies = explode(',',variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY));
   $sql = "select ti.tid from {node} n
           join {taxonomy_index} ti on n.nid = ti.nid
           join {taxonomy_term_data} td on ti.tid = td.tid
           where n.changed > :time
           and n.changed < :now
-          and td.vid = :vocabulary";
-  $placeholders = array(':time' => $time, ':now' => $now, ':vocabulary' => $vocabulary);
+          and td.vid in (:vocabularies)";
+  $placeholders = array(':time' => $time, ':now' => $now, ':vocabularies' => $vocabularies);
   $result = db_query($sql, $placeholders);
   $tids = NULL;
   foreach ($result as $record) {
@@ -535,19 +536,19 @@
   // before term_node, now taxonomy_index
   $sql_common_tids = "select ti.tid from {taxonomy_index} ti
                       join {taxonomy_term_data} td on ti.tid = td.tid
-                      where td.vid = :vocabulary
+                      where td.vid in (:vocabularies)
                       group by ti.tid having count(ti.tid) > :seed_term_ocurrence_threshold";

-  $vocabulary = variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY);
+  $vocabularies = explode(',',variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY));
   $seed_term_ocurrence_threshold = variable_get('taxonomy_nco_analysis_seed_term_occurrence_threshold', SEED_TERM_OCCURENCE_THRESHOLD);
   // limit for testing
   if ($limit > 0) {
-    $placeholders = array(':vocabulary' => $vocabulary,
+    $placeholders = array(':vocabularies' => $vocabularies,
                   ':seed_term_ocurrence_threshold' => $seed_term_ocurrence_threshold);
     $result_common_tids = db_query_range($sql_common_tids, 0, $limit, $placeholders);
   }
   else {
-    $placeholders = array(':vocabulary' => $vocabulary,
+    $placeholders = array(':vocabularies' => $vocabularies,
                   ':seed_term_ocurrence_threshold' => $seed_term_ocurrence_threshold);
     $result_common_tids = db_query($sql_common_tids, $placeholders);
   }
@@ -583,10 +584,10 @@
   $sql_related_tids = "select distinct ti1.tid from {taxonomy_index} ti1
                        join {taxonomy_index} ti2 on ti1.nid = ti2.nid
                        join {taxonomy_term_data} td on ti1.tid = td.tid
-                       where ti2.tid = :tid_a and td.vid = :vocabulary";
-  $vocabulary = variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY);
+                       where ti2.tid = :tid_a and td.vid in (:vocabularies)";
+  $vocabularies = explode(',',variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY));

-  $placeholders = array(':tid_a' => $tid_a, ':vocabulary' => $vocabulary);
+  $placeholders = array(':tid_a' => $tid_a, ':vocabularies' => $vocabularies);
   $result_related_tids = db_query($sql_related_tids, $placeholders);
   // cycle through related terms analysing the relationships with this seed
   foreach ($result_related_tids as $record) {
diff -Naur orig/taxonomy_nco.module patched/taxonomy_nco.module
--- orig/taxonomy_nco.module    2012-06-21 14:34:16.000000000 +0200
+++ patched/taxonomy_nco.module 2012-06-21 14:34:23.000000000 +0200
@@ -131,34 +131,37 @@
  * @param $minimum_match_nco
  * @return array of tid + nco
  */
-function taxonomy_nco_related_terms($tids, $minimum_match_nco = -1) {
+function taxonomy_nco_related_terms($tids, $minimum_match_nco = -1, $vids = array(), $multi_id) {
   // TODO: check this string is a float
   //watchdog('taxonomy nco', 'Similar terms request NCO: ' . $minimum_match_nco . ' tids: ' . print_r($tids, 1));
   if ($minimum_match_nco == '' or $minimum_match_nco == -1) {
-    $minimum_match_nco = variable_get('taxonomy_nco_default_minimum_match_nco', DEFAULT_MINIMUM_MATCH_NCO);
+    $minimum_match_nco = variable_get('taxonomy_nco'.$multi_id.'_default_minimum_match_nco', DEFAULT_MINIMUM_MATCH_NCO);
   }
   // cached related tids query
   // todo: check this caching is working in D7
   // todo?: cache all results rather than just the latest result?
   static $similars = array();
   sort($tids, SORT_NUMERIC); //ensures caching will work even if same tids are presented in different order
+  sort($vids, SORT_NUMERIC);
   $output = NULL;
   // check to see if we can just serve up the cache ..
-  if (array_key_exists('input_tids', $similars) && array_key_exists('minimum_nco', $similars)) {
-    if (($similars['input_tids'] == $tids) && ($similars['minimum_nco'] == $minimum_match_nco)) {
-      if (array_key_exists('output_tids')) {
+  if (array_key_exists('input_tids', $similars) && array_key_exists('minimum_nco', $similars) && array_key_exists('input_vids', $similars)
+    && ($similars['input_tids'] == $tids) && ($similars['input_vids'] == $vids) && ($similars['minimum_nco'] == $minimum_match_nco)
+      && array_key_exists('output_tids', $similars)) {
       $output = $similars['output_tids'];
-      }
-    }
   }
   else {  //otherswise we'd better to the sums ..
     //watchdog('taxonomy nco', 'tid_list: ' . implode($tids, ',') . ' minimum nco: ' . $minimum_match_nco);
     // get automatic nco related terms
-    $minimum_match_intersection = variable_get('taxonomy_nco_minimum_match_intersection', MINIMUM_MATCH_INTERSECTION);
+    $minimum_match_intersection = variable_get('taxonomy_nco'.$multi_id.'_minimum_match_intersection', MINIMUM_MATCH_INTERSECTION);

     $sel = db_select('taxonomy_nco', 't')
-      ->fields('t', array('tid_b'))
-      ->condition('tid_a', $tids, 'IN')
+      ->fields('t', array('tid_b'));
+    if (count($vids)) {
+      $sel->leftJoin('taxonomy_term_data', 'td', 't.tid_b = td.tid');
+      $sel->condition('td.vid', $vids, 'IN');
+    }
+    $sel->condition('tid_a', $tids, 'IN')
       ->condition('a_and_b', $minimum_match_intersection, '>=')
       ->condition('nco', $minimum_match_nco, '>=')
       ->groupBy('tid_b');
@@ -166,17 +169,20 @@
     //where there are multiple tids passed, there may be multiple nco results for each related tid
     //therefore we select the instance of each related tid that has the greatest nco
     $ncoAlias = $sel->addExpression('max(nco)');
-
     $rows = $sel->execute();
     foreach ($rows as $row) {
       $output[$row->tid_b] = $row->$ncoAlias;
     }

     // now get manual overrides
-    $rows = db_select('taxonomy_nco_forced', 't')
-      ->fields('t', array('tid_b', 'nco'))
-      ->condition('tid_a', $tids, 'IN')
-      ->execute();
+    $qsel = db_select('taxonomy_nco_forced', 't')
+      ->fields('t', array('tid_b', 'nco'));
+    if (count($vids)) {
+      $qsel->leftJoin('taxonomy_term_data', 'td', 't.tid_b = td.tid');
+      $qsel->condition('td.vid', $vids, 'IN');
+    }
+    $rows = $qsel->condition('tid_a', $tids, 'IN')
+      ->execute();
     foreach ($rows as $row) {
       $output[$row->tid_b] = $row->nco;
     }
@@ -185,6 +191,7 @@
     }
     $similars['input_tids'] = $tids;
     $similars['minimum_nco'] = $minimum_match_nco;
+    $similars['input_vids'] = $vids;
     $similars['output_tids'] = $output;
   }
   //watchdog('taxonomy nco', 'debug - output: ' . print_r($output, 1));
@@ -199,8 +206,8 @@
  * @param $minimum_match_nco
  * @return array of tids
  */
-function taxonomy_nco_similar_terms($tids, $minimum_match_nco = -1) {
-  $data = taxonomy_nco_related_terms($tids);
+function taxonomy_nco_similar_terms($tids, $minimum_match_nco = -1, $vids = array(), $multi_id = '') {
+  $data = taxonomy_nco_related_terms($tids, $minimum_match_nco, $vids, $multi_id);
   $output = NULL;
   if ($data) {
     $output = array_keys($data);
@@ -222,24 +229,84 @@
 function taxonomy_nco_block_info() {
   $blocks['taxonomy-nco'] = array(
     'info' => t('Taxonomy NCO similarity'),
+    'cache' => DRUPAL_NO_CACHE,
+    'mb_enabled' => TRUE
   );
   return $blocks;
 }

 /**
- * Implements hook_block_view().
+ * Implements hook_block_view() with support for Multiblock
  */
-function taxonomy_nco_block_view($delta) {
+function taxonomy_nco_block_view($delta, $edit = array()) {
   switch ($delta) {
     case 'taxonomy-nco':
+      $multi_id = isset($edit['multiblock_delta']) ? '_'. $edit['multiblock_delta']['#value'] : '';
+      $chosenvocs = explode(',',variable_get('taxonomy_nco_block'.$multi_id.'_vocabularies', TAXONOMY_NCO_VOCABULARY));
       $block['subject'] = t('Taxonomy NCO similarity');
-      $block['content'] = taxonomy_nco_relationship_block_contents();
+      $block['content'] = taxonomy_nco_relationship_block_contents($chosenvocs);
       break;
   }
   return $block;
 }

 /**
+ * Implements hook_block_configure() with support for Multiblock
+ */
+function taxonomy_nco_block_configure($delta, $edit = array()) {
+  $form = array();
+  if ($delta == 'taxonomy-nco') {
+    $multi_id = isset($edit['multiblock_delta']) ? '_'. $edit['multiblock_delta']['#value'] : '';
+    $availablevocabularies = explode(',',variable_get('taxonomy_nco_analysis_vocabulary', TAXONOMY_NCO_VOCABULARY));
+    $chosenvocs = explode(',',variable_get('taxonomy_nco_block'.$multi_id.'_vocabularies', TAXONOMY_NCO_VOCABULARY));
+    $form['taxonomy_vocabularies'] =  array(
+      '#type' => 'select',
+      '#multiple' => TRUE,
+      '#title' => t('Vocabularies for NCO match'),
+      '#options' => taxonomy_nco_vocabularies_list($availablevocabularies),
+      '#description' => t('NCO matches will only be retrieved for this vocabularies'),
+      '#default_value' => $chosenvocs,
+    );
+    $include_tags_in_related = variable_get('taxonomy_nco'.$multi_id.'_include_tags_in_related', 'auto');
+    $form['include_tags_in_related'] = array(
+      '#type' => 'select',
+      '#title' => t('Include Node Tags in Related Terms'),
+      '#description' => t('When the Related Terms block is used on a node page, how should the node\'s own tags be presented?'),
+      '#default_value' => $include_tags_in_related,
+      '#options' => array(
+        'auto' => t('Automatic: node tags will appear according to normal NCO rules'),
+        'include' => t('Include: node tags will always appear in Related Content'),
+        'exclude' => t('Exclude: node tags will never appear in Related Content'),
+      )
+    );
+    $minimum_match = variable_get('taxonomy_nco'.$multi_id.'_default_minimum_match_nco', DEFAULT_MINIMUM_MATCH_NCO);
+    $form['minimum_match_nco'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Minimum NCO for matching'),
+      '#default_value' => $minimum_match,
+    );
+    $minimum_match_int = variable_get('taxonomy_nco'.$multi_id.'_minimum_match_intersection', MINIMUM_MATCH_INTERSECTION);
+    $form['minimum_match_intersection'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Minimum match intersection'),
+      '#default_value' => $minimum_match_int,
+    );
+  }
+  return $form;
+}
+
+/**
+ * Implements hook_block_save().
+ */
+function taxonomy_nco_block_save($delta, $edit = array()) {
+  $multi_id = isset($edit['multiblock_delta']) ? '_'. $edit['multiblock_delta']['#value'] : '';
+  variable_set('taxonomy_nco_block'.$multi_id.'_vocabularies',implode(',', $edit['taxonomy_vocabularies']));
+  variable_set('taxonomy_nco'.$multi_id.'_include_tags_in_related', $edit['include_tags_in_related']);
+  variable_set('taxonomy_nco'.$multi_id.'_default_minimum_match_nco', $edit['minimum_match_nco']);
+  variable_set('taxonomy_nco'.$multi_id.'_minimum_match_intersection', $edit['minimum_match_intersection']);
+}
+
+/**
  * Fish out all the tids used in the vocabulary used by nco
  */
 function taxonomy_nco_all_tids() {
@@ -293,7 +360,7 @@
  * Or on any node page
  * Displays related terms based on given NCO similarity cutoff
  */
-function taxonomy_nco_relationship_block_contents() {
+function taxonomy_nco_relationship_block_contents($vocabularies = array(), $multi_id = '') {
   $output = '';
   $tids = array();
   switch (arg(0)) {
@@ -314,10 +381,12 @@
     // since, strictly speaking, we do not know in THIS module (only the analysis module)
     // which vocabulary we are using, we'll read back the tids we use for nco, and only use the ones
     // found in our nco table
+
+    // TODO taxonomy_nco_all_tids is a killer!
     $tids = array_intersect($tids, taxonomy_nco_all_tids());

     if ($tids) {
-      $variables = array('tids' => $tids, 'minimum_nco' => NULL,  'display_counts' => FALSE, 'context' => arg(0));
+      $variables = array('tids' => $tids, 'minimum_nco' => NULL,  'display_counts' => FALSE, 'context' => arg(0), 'vids' => $vocabularies, 'multi_id' => $multi_id);
       $related_terms_output = theme('taxonomy_nco_related_terms', $variables);
       if ($related_terms_output){
         $output .= t('Related subjects') . ': ' . $related_terms_output;
@@ -369,13 +438,15 @@
   $minimum_nco = $variables['minimum_nco'];
   $display_counts = $variables['display_counts'];
   //watchdog('taxonomy nco', print_r($tids, 1));
-  $related_terms = taxonomy_nco_similar_terms($tids, $minimum_nco);
+  $vids = array_key_exists('vids', $variables) ? $variables['vids'] : array();
+  $related_terms = taxonomy_nco_similar_terms($tids, $minimum_nco, $vids);
   //watchdog('taxonomy nco', 'related terms: ' . print_r($related_terms, 1));
+  $multi_id = array_key_exists('multi_id', $variables) ? $variables['multi_id'] : '';

   // include/exclude node tags based on user-specified preference
   if ($variables['context']=='node') {
     if ($related_terms) {
-      $include_tags_in_related = variable_get('taxonomy_nco_include_tags_in_related', 'auto');
+      $include_tags_in_related = variable_get('taxonomy_nco'.$multi_id.'_include_tags_in_related', 'auto');
       switch ($include_tags_in_related) {
         case 'auto': break; //no further action to take
         case 'include':
@@ -686,4 +757,21 @@
       return $title;
   }
 }
-
+
+/**
+ * Fetch all available vocabularies formatted ready for select options
+ *
+ */
+function taxonomy_nco_vocabularies_list($vids = array()) {
+  $rows = db_select('taxonomy_vocabulary', 'tv')
+    ->fields('tv', array('vid', 'name'));
+  if (count($vids)) {
+    $rows = $rows->condition('vid', $vids, 'IN');
+  }
+  $rows = $rows->orderBy('name')
+    ->execute();
+  $result=array();
+  while ($row = $rows->fetchAssoc()) $result[$row['vid']] = $row['name'];
+  return $result;
+}
+
