stoichiometry problem

User 870ab5b546

19-07-2007 21:15:14

I want to design a reaction for Grignard addition to esters. I want to be able to take one Grignard starting material and one ester starting material and get the tertiary alcohol. Reactor doesn't seem to properly calculate the result of a reaction in which two equivalents of one of the reactants is incorporated into the product. I understand that in the reaction definition, I can have two instances of the one reactant, but I don't want to allow the possibility that the two instances could be different. Nor do I want to have to clone one of the reactants before I send them to Reactor. Is there a way around the stoichiometry problem?





Desired reaction scheme:





Code:
[#6-:3].[#6]O[C:1]([#1,#6])=[O:2]>>[#6:3][C:1]([#6:3])([O:2])[#1,#6]






Example:





Code:
[CH3-].CCOC(=O)C1=CC=CC=C1>>CC(C)(O)C1=CC=CC=C1 |c:7,9,17,19,t:5,15|

ChemAxon e08c317633

20-07-2007 08:50:53

Reactor currently doesn't support it. Identical atom maps can not be used on one reaction side, it is not a valid changing mapping.





Zsolt

User 870ab5b546

20-07-2007 23:57:13

I suggest that Reactor should support it in the future.





Meanwhile I will write a workaround.

User 870ab5b546

21-07-2007 23:59:49

The following code accounts for the case where two equivalents of a reactant are required, but the reactant is drawn only once, and you don't know which reactant needs to be duplicated or in what order the reactants need to be presented.





What it does not do is prevent the user from submitting two different instances of the reactant that should be duplicated.





Code:
   public static Molecule[] doCalc(String reactants, String reactionDef) {


      try {


         Molecule substrate = MolImporter.importMol(reactants);


         return doCalc(substrate, reactionDef);


      } catch (MolFormatException e) {


         System.out.println("SynthSolver: caught MolFormatException on:\n"


            + reactants + "\n" + e.getMessage());


      }


      return null;


   } // doCalc(AppConfig, String, String)





   private static Molecule[] doCalc(Molecule substrate, String reactionDef) {


      try {


         /* println("SynthSolver.doCalc: reactionDef:\n" + reactionDef); 


         println("SynthSolver.doCalc: substrate:\n"


               + substrate.toFormat("mrv")); 


         */


         Molecule reactionmol =


               new MolHandler(reactionDef).getMolecule();


         RxnMolecule reaction =


               RxnMolecule.getReaction(reactionmol);


         if (reaction == null) {


            System.out.println("SynthSolver.doCalc: reaction "


                  + "definition molecule is null.");


            return null;


         } // if reaction definition can't be parsed


         


         Reactor reactor = new Reactor();


         reactor.setReaction(reaction);


         int desiredNumSubstrates = reactor.getReactantCount();


         Molecule[] splitSubstrates =


               substrate.cloneMolecule().convertToFrags();


         // make sure that number of submitted substrates is number needed;


         // if not, duplicate or remove substrates


         ArrayList<Molecule[]> resizedSubstrates =


               new ArrayList<Molecule[]>();


         resizedSubstrates.add(splitSubstrates);


         int diff = desiredNumSubstrates - splitSubstrates.length;


         if (diff > 0) {


            println("SynthSolver.doCalc: submitted substrates are "


                  + diff + " too few; calling addMultiSubstrates.");


            resizedSubstrates =


                  addMultiSubstrates(resizedSubstrates,


                        desiredNumSubstrates);


         } else if (diff < 0) {


            println("SynthSolver.doCalc: submitted substrates are "


                  + (-diff)


                  + " too many; calling removeMultiSubstrates.");


            resizedSubstrates =


                  removeMultiSubstrates(resizedSubstrates,


                        desiredNumSubstrates);


         } // if diff > or < 0


         int numResizedArrays = resizedSubstrates.size();


         int arrayLength = resizedSubstrates.get(0).length;


         println("SynthSolver.doCalc: resized substrates array has "


               + numResizedArrays + " members; first member contains "


                  + (arrayLength == 1 ?  " one substrate."


                     : arrayLength + " substrates."));


         


         // test each array of substrates for products


         Molecule[] products = null;


         for (int eachSubstrateArray = 0;


               eachSubstrateArray < numResizedArrays;


               eachSubstrateArray++) {


            Molecule[] thisArray =


                  resizedSubstrates.get(eachSubstrateArray);


            println("Substrate array " + (eachSubstrateArray + 1)


                  + ":\n" + arrayPrintOut(thisArray));


            // get permutations of the resized substrate array


            ArrayList<Molecule[]> allPerms =


                  getPermutations(thisArray);


            int numPerms = allPerms.size();


            println("Found " + numPerms + " permutations of the "


                  + (thisArray.length == 1 ?  " one substrate."


                     : thisArray.length + " substrates."));


            // submit each permutation to Reactor


            for (int i = 0; i < numPerms; i++) {


               println("Trying permutation " + (i + 1) + "...");


               reactor.setReactants(allPerms.get(i));


               products = reactor.react();


               if (products != null) break;


            } // for each permutation of the substrates


            if (products != null) break;


         } // for each member of resizedSubstrates


      


         if (products != null) {


            println("SynthSolver.doCalc: calculation gave "


                  + (products.length == 1 ? 


                     "one product."


                     : products.length + " products."));


            for (int i = 0; i < products.length; i++)


               println("Product " + (i + 1) + ":\n"


                     + products[i].toFormat("mrv"));


         } else println("SynthSolver.doCalc: calculation gave no products.");


         return products;


      } catch (MolFormatException e) {


         System.out.println("SynthSolver.doCalc: caught MolFormatException "


            + "on reaction definition:\n"


            + reactionDef + "\n" + e.getMessage());


      } catch (ReactionException e) {


         System.out.println("SynthSolver.doCalc: caught ReactionException:\n"


            + e.getMessage());


      }


      return null;


   } // doCalc(Molecule, String)





   // The following three methods are adapted from


   // http://www.cs.princeton.edu/introcs/23recursion/Permutations.java.html


   // get ArrayList of N! permutations of the substrates


   public static ArrayList<Molecule[]> getPermutations(Molecule[] substrates) {


      int N = substrates.length;


      ArrayList<Molecule[]> allPerms = new ArrayList<Molecule[]>();


      permute(substrates, N, allPerms);


      return allPerms;


   } // getPermutations(Molecule[])





   // recursively called method; permutes progressively shorter collections


   private static void permute(Molecule[] substrates, int n,


         ArrayList<Molecule[]> allPerms) {


      if (n == 1) {


         allPerms.add((Molecule[]) substrates.clone());


         return;


      } // if n == 1


      for (int i = 0; i < n; i++) {


         swap(substrates, i, n - 1);


         permute(substrates, n - 1, allPerms);


         swap(substrates, i, n - 1);


      } // for each i


   } // permute(Molecule[], int, ArrayList<Molecule[]>)





   // swap the Molecules at indices i and j


   private static void swap(Molecule[] substrates, int i, int j) {


      Molecule c;


      c = substrates[i]; substrates[i] = substrates[j]; substrates[j] = c;


   } // swap(Molecule[], int, int)





   // next four methods resize the number of substrates to match the number


   // needed for the reaction


   private static ArrayList<Molecule[]> addMultiSubstrates(


         ArrayList<Molecule[]> substrates, int desiredNumSubstrates) {


      int currentLength = substrates.get(0).length;


      println("SynthSolver.addMultiSubstrates: current number of substrates = "


            + currentLength + "; desired = " + desiredNumSubstrates);


      if (currentLength < desiredNumSubstrates) {


         println("Adding one substrate...");


         ArrayList<Molecule[]> allNewSubstrateArrays = new ArrayList<Molecule[]>();


         for (int i = 0; i < substrates.size(); i++) {


            allNewSubstrateArrays.addAll(addOneSubstrate(substrates.get(i)));


         }


         substrates = allNewSubstrateArrays;


         currentLength = substrates.get(0).length;


         println("Recursively calling addMultiSubstrates...");


         substrates = addMultiSubstrates(substrates, desiredNumSubstrates);


      }


      return substrates;


   } // addMultiSubstrates(ArrayList<Molecule[]>, int)





   private static ArrayList<Molecule[]> removeMultiSubstrates(


         ArrayList<Molecule[]> substrates, int desiredNumSubstrates) {


      int currentLength = substrates.get(0).length;


      println("SynthSolver.removeMultiSubstrates: current number of substrates = "


            + currentLength + "; desired = " + desiredNumSubstrates);


      if (currentLength > desiredNumSubstrates) {


         println("Removing one substrate...");


         ArrayList<Molecule[]> allNewSubstrateArrays = new ArrayList<Molecule[]>();


         for (int i = 0; i < substrates.size(); i++) {


            allNewSubstrateArrays.addAll(removeOneSubstrate(substrates.get(i)));


         }


         substrates = allNewSubstrateArrays;


         currentLength = substrates.get(0).length;


         println("Recursively calling removeMultiSubstrates...");


         substrates = removeMultiSubstrates(substrates, desiredNumSubstrates);


      }


      return substrates;


   } // removeMultiSubstrates(ArrayList<Molecule[]>, int)





   private static ArrayList<Molecule[]> addOneSubstrate(Molecule[] substrates) {


      int numSubstrates = substrates.length;


      ArrayList<Molecule[]> allNewSubstrateArrays = new ArrayList<Molecule[]>();


      for (int i = 0; i < numSubstrates; i++) {


         Molecule[] newSubstrates = new Molecule[numSubstrates + 1];


         for (int j = 0; j < numSubstrates + 1; j++) {


            if (j < numSubstrates) {


               newSubstrates[j] = substrates[j].cloneMolecule();


            } else {


               newSubstrates[j] = substrates[i].cloneMolecule();


            }


         }


         allNewSubstrateArrays.add(newSubstrates);


      }


      return allNewSubstrateArrays;


   } // addOneSubstrate(Molecule[])





   private static ArrayList<Molecule[]> removeOneSubstrate(Molecule[] substrates) {


      int numSubstrates = substrates.length;


      ArrayList<Molecule[]> allNewSubstrateArrays = new ArrayList<Molecule[]>();


      for (int i = 0; i < numSubstrates; i++) {


         Molecule[] newSubstrates = new Molecule[numSubstrates - 1];


         for (int j = 0; j < numSubstrates; j++) {


            if (j < i) {


               newSubstrates[j] = substrates[j].cloneMolecule();


            } else if (j > i) {


               newSubstrates[j - 1] = substrates[j].cloneMolecule();


            }


         }


         allNewSubstrateArrays.add(newSubstrates);


      }


      return allNewSubstrateArrays;


   } // removeOneSubstrate(Molecule[])


   


   private static String arrayPrintOut(Molecule[] molArray) {


      String out = "{";


      for (int i = 0; i < molArray.length; i++)


         out += molArray[i].toFormat("smiles") + ", ";


      out = (out.length() > 2 ?


            out.substring(0, out.length() - 2) + "}"


            : "{null}");


      return out;


   }

User 870ab5b546

22-07-2007 03:47:14

I can define a reaction property called "Number of reactants" that says how many reactants should be submitted for that reaction. So if the value of "Number of reactants" is 2, and the user submits 3 reactants, I can return null. This technique would prevent a user, e.g., from submitting two different Grignard reagents along with an ester to give a tertiary alcohol. If they submit just one Grignard reagent, I can use the code above to duplicate it before running the reaction.





However, I don't see a method in the API to obtain the value of a reaction property. I can parse the reaction definition string to look for the value, but it would be much better if I could use an API method to obtain it. Is there such a method? I couldn't find one in the documentation.

ChemAxon d76e6e95eb

23-07-2007 09:58:04

We have the following plans (already added to our internal feature request registration system):





- There will be a new property field in the reactions (name s not yet fixed), in which you can enter the reactant ratio (or stoichiometry) in the following form:


1:2 or 1:2:1


There will be a restriction, only one number can be different from 1. You will be able to overwrite this ratio as a runtime option. How will it behave?





Aromatic nitration of benzene with 1:1 ratio: nitrobenzene will be the main product


Aromatic nitration of benzene with 1:2 ratio: dinitrobenzene will be the main product


Aromatic nitration of benzene with 1:3 ratio: dinitrobenzene will be the main product (deactivation can be noticed in reactivity rule)


Aromatic nitration of toluene with 1:3 ratio: TNT will be the main product (dinitrotoluene is still active)





- The default reactant ratio will be 1 for each reactant if not specified





- We will support map numbers duplicated at one side of the reaction arrow, for example displaying all produced isomers in a single reaction scheme: A+ B >> C1 + C2 + D (C1 and C2 are isomers)





No deadlines are defined yet.

User 870ab5b546

23-07-2007 12:13:03

These plans sound very useful. Thanks.

User 870ab5b546

24-07-2007 23:29:45

A related problem: I have written a reaction that converts ROBn to ROH and BnH. If I have a single starting material that contains three OBn groups, and I run Reactor on it, it returns only the three products in which one OBn group is replaced with OH. Is there a way to get it to run the reaction repeatedly on all instances of the reactant in a single compound? Or do I need to take the products and resubject them to the same reaction conditions repeatedly until Reactor returns null?

ChemAxon d76e6e95eb

25-07-2007 19:26:04

According to the above described plan, we will hopefully solve this problem as well. I am afraid, that you need to run the reaction in a loop at the moment.

User 870ab5b546

25-07-2007 23:00:35

OK, I understand. But just so you know, the loop doesn't always work well. Two examples:





(1) Adding a Grignard reagent to a compound that contains two ketones, where the Grignard reagent is one of the reactants supplied by the user. To get the diol product, I would need to take the keto-alcohol product of the first reaction and recombine it with one of the original reactants!





(2) A substitution reaction in which the user submits a bifunctional compound such as BrCH2CH2OH. Here I will get an infinite recursive loop, because the product, BrCH2CH2OCH2CH2OH, can go ahead and react with itself again.





-- Bob

ChemAxon d76e6e95eb

26-07-2007 10:17:56

Very interesting points indeed. Certainly, looping is important, since one transformation can change the properties of the reagent, such as activation and deactivation issues.





A possible solution of your Grignard coupling example would be to divide it into two separate reaction steps, first is the addition, second is the hydrolysis.





Reacting multifunctional groups brings the complexity of polymerization reactions in the scene. Since you call the loop, you can avoid polymerization, just run the loop twice. The reaction ratio parameter will neither let you to specify infinite loops.

User 870ab5b546

26-07-2007 16:55:15

Gyuri wrote:
A possible solution of your Grignard coupling example would be to divide it into two separate reaction steps, first is the addition, second is the hydrolysis.
I must not have been clear. Consider O=C(C)CCCCCCC(C)=O. If I add one equivalent of CH3MgBr to this compound, I should get not only OC(C)(C)CCCCCCC(C)=O as a product (twice), but also OC(C)(C)CCCCCCC(C)(C)O, from two equivalents of the Grignard reacting with the ketone.





If I can specify two equivalents of CH3MgBr, looping should help, because only one equivalent of CH3MgBr will be consumed in loop 1, and I can combine unreacted reactants from loop 1 with products of loop 1 when I go into loop 2.
Gyuri wrote:
Reacting multifunctional groups brings the complexity of polymerization reactions in the scene. Since you call the loop, you can avoid polymerization, just run the loop twice. The reaction ratio parameter will neither let you to specify infinite loops.
Yes, I thought of this solution, but the permitted depth would be quite arbitrary, and there would always be cases where a user submitted a legitimate substrate that required one loop further. For example, suppose someone was doing a carbohydrate synthesis, and the last step was to hydrogenolyze all the OBn groups to OH groups? Carbohydrates can have 12, 20, or a larger number of OBn groups.





A better solution would be to recognize the situations in which polymerization can occur, and deal with them. A polymerization can occur in a reaction of group A with group B when two substrates both have a group A and a group B. Perhaps you could add a method, mayPolymerize()? If it returned true, we could restrict the reaction to give only products with up to a defined number of monomers. (We need to permit a little oligomerization, for example, in the synthesis of 18-crown-6 from ethylene oxide and KOH.) The point would be to avoid going to the trouble of calculating certain products if we know they are undesired as a class.

ChemAxon d76e6e95eb

06-08-2007 12:54:26

Handling polymerization reactions correclty is a complex task for a specific application, and we do not plan developing such a polymerization application. Our current Reactor application is focused on combichem synthesis, and the upcoming Metabolizer application if focused on the prediction of the metabolic fate of xenobiotics.


I can imagine, that one can develop a polymerization application similarly on the top of the Reactor engine, but we still find lots of challenging tasks in classical synthetic organic chemistry.

User 870ab5b546

06-08-2007 13:13:53

I guess I was not clear. I was suggesting that you develop a method that would recognize substrates that might polymerize under the reaction conditions so you could *avoid* their polymerization.

ChemAxon d76e6e95eb

06-08-2007 16:01:20

I am sorry for the misunderstanding. Polymerization cannot occur in reactor, since it currently converts only one functional group at a time.





I suppose, that this problem will not appear even if we add the new reactant rate future, as it will be limited to allow the specific surplus of one reactant only, so Reactor will be able to generate all isomers according to the given stoichiometry. No polymerization detection will be needed.





Please tell me if I still do not clearly understand your proposal.

User 870ab5b546

06-08-2007 16:20:23

But polymerization does become a problem if you loop through the reaction until no more reactions occur, as is currently required.

ChemAxon d76e6e95eb

06-08-2007 16:50:16

We do not plan to loop through until no more reaction occur. There will be an upper limit, like 1:5 or 1:3:1 or simply 100. No infinite loop will occur. You can consider this as a primitive way avoiding polimerization issues.