/************************************************************************* * * * This file is part of the 20n/act project. * * 20n/act enables DNA prediction for synthetic biology/bioengineering. * * Copyright (C) 2017 20n Labs, Inc. * * * * Please direct all queries to act@20n.com. * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * *************************************************************************/ package com.act.biointerpretation.sars; import chemaxon.formats.MolExporter; import chemaxon.formats.MolFormatException; import chemaxon.formats.MolImporter; import chemaxon.sss.SearchConstants; import chemaxon.sss.search.MolSearch; import chemaxon.sss.search.MolSearchOptions; import chemaxon.sss.search.SearchException; import chemaxon.struc.Molecule; import chemaxon.struc.RxnMolecule; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; import java.util.List; import java.util.stream.Collectors; /** * A SAR that accepts only single substrates which contain a particular substructure. */ public class OneSubstrateSubstructureSar implements Sar { private static final Logger LOGGER = LogManager.getFormatterLogger(OneSubstrateSubstructureSar.class); private static final String INCHI_SETTINGS = "inchi:AuxNone"; private static final String PRINT_FAILURE = "FAILED_TO_PRINT_SAR"; /** * Search loosely by default to avoid weeding out potentially viable matches. */ private static final MolSearchOptions LOOSE_SEARCH_OPTIONS = new MolSearchOptions(SearchConstants.SUBSTRUCTURE); static { LOOSE_SEARCH_OPTIONS.setStereoModel(SearchConstants.STEREO_IGNORE); LOOSE_SEARCH_OPTIONS.setVagueBondLevel(SearchConstants.VAGUE_BOND_LEVEL4); LOOSE_SEARCH_OPTIONS.setTimeoutLimitMilliseconds(1000); } private Molecule substructure; private MolSearch searcher; /** * For Json reading. */ private OneSubstrateSubstructureSar() { searcher = new MolSearch(); searcher.setSearchOptions(LOOSE_SEARCH_OPTIONS); } public OneSubstrateSubstructureSar(Molecule substructure, MolSearchOptions searchOptions) { this(); this.substructure = substructure; searcher.setQuery(substructure); } public OneSubstrateSubstructureSar(Molecule substructure) { this(substructure, LOOSE_SEARCH_OPTIONS); } @Override public boolean test(List<Molecule> substrates) { // This class of SARs is only valid on single-substrate reactions. if (substrates.size() != 1) { return false; } // Return true if the searcher finds a match searcher.setTarget(substrates.get(0)); try { return searcher.findFirst() != null; } catch (SearchException e) { // Log error but don't propagate upward. Have never seen this before. LOGGER.error("Error on testing substrates with SAR %s", getSubstructureInchi()); return false; } } @JsonProperty("substructure_inchi") private String getSubstructureInchi() { try { return MolExporter.exportToFormat(substructure, INCHI_SETTINGS); } catch (IOException e) { LOGGER.error("IOException on exporting sar to inchi, %s", e.getMessage()); return PRINT_FAILURE; } } /** * For JSON reading * * @param inchi Inchi to set as substructure, as read from json. * @throws MolFormatException */ private void setSubstructureInchi(String inchi) throws MolFormatException { substructure = MolImporter.importMol(inchi); searcher.setQuery(substructure); } public static class Factory implements SarFactory { private final McsCalculator mcsCalculator; public Factory(McsCalculator mcsCalculator) { this.mcsCalculator = mcsCalculator; } @Override public Sar buildSar(List<RxnMolecule> reactions) { if (!DbAPI.areAllOneSubstrate(reactions)) { throw new IllegalArgumentException("Reactions are not all one substrate."); } List<Molecule> substrates = reactions.stream().map(rxn -> rxn.getReactants()[0]).collect(Collectors.toList()); Molecule sarMcs = mcsCalculator.getMCS(substrates); return new OneSubstrateSubstructureSar(sarMcs); } } }