From 34cb8167f71e4a98f3326b7b6c02d3865266700e Mon Sep 17 00:00:00 2001 From: Jonas Schaub <44881147+JonasSchaub@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:36:20 +0200 Subject: [PATCH 01/11] Initialization of new fragmenter class --- .../algorithm/CDKExhaustiveFragmenter.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java new file mode 100644 index 00000000..d2d79555 --- /dev/null +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -0,0 +1,105 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2021 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * 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 . + */ + +package de.unijena.cheminf.mortar.model.fragmentation.algorithm; + +import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; +import javafx.beans.property.Property; +import org.openscience.cdk.interfaces.IAtomContainer; + +import java.util.List; +import java.util.Map; + +public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { + @Override + public List> settingsProperties() { + return null; + } + + @Override + public Map getSettingNameToTooltipTextMap() { + return null; + } + + @Override + public Map getSettingNameToDisplayNameMap() { + return null; + } + + @Override + public String getFragmentationAlgorithmName() { + return null; + } + + @Override + public String getFragmentationAlgorithmDisplayName() { + return null; + } + + @Override + public FragmentSaturationOption getFragmentSaturationSetting() { + return null; + } + + @Override + public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { + return null; + } + + @Override + public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { + + } + + @Override + public IMoleculeFragmenter copy() { + return null; + } + + @Override + public void restoreDefaultSettings() { + + } + + @Override + public List fragmentMolecule(IAtomContainer aMolecule) throws NullPointerException, IllegalArgumentException, CloneNotSupportedException { + return null; + } + + @Override + public boolean shouldBeFiltered(IAtomContainer aMolecule) { + return false; + } + + @Override + public boolean shouldBePreprocessed(IAtomContainer aMolecule) throws NullPointerException { + return false; + } + + @Override + public boolean canBeFragmented(IAtomContainer aMolecule) throws NullPointerException { + return false; + } + + @Override + public IAtomContainer applyPreprocessing(IAtomContainer aMolecule) throws NullPointerException, IllegalArgumentException, CloneNotSupportedException { + return null; + } +} From 8e5d817cb2c7d183afe0f13b3daeef06be4d0c46 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Sat, 12 Oct 2024 20:44:42 +0200 Subject: [PATCH 02/11] work in progress: first rudimentary implementation of the exhaustive fragmentation utility of the CDK, as a fragmentation option --- .../fragmentation/FragmentationService.java | 10 +- .../algorithm/CDKExhaustiveFragmenter.java | 213 +++++++++++++++--- .../mortar/message/Message_en_GB.properties | 4 + 3 files changed, 200 insertions(+), 27 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java index 9acf735c..1f80ea9c 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/FragmentationService.java @@ -29,6 +29,7 @@ import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.data.FragmentDataModel; import de.unijena.cheminf.mortar.model.data.MoleculeDataModel; +import de.unijena.cheminf.mortar.model.fragmentation.algorithm.CDKExhaustiveFragmenter; import de.unijena.cheminf.mortar.model.fragmentation.algorithm.ErtlFunctionalGroupsFinderFragmenter; import de.unijena.cheminf.mortar.model.fragmentation.algorithm.IMoleculeFragmenter; import de.unijena.cheminf.mortar.model.fragmentation.algorithm.ScaffoldGeneratorFragmenter; @@ -187,6 +188,11 @@ public class FragmentationService { */ private final IMoleculeFragmenter scaffoldGF; // + /** + * Exhaustive fragmenter + */ + private final IMoleculeFragmenter cdkEF; + // /** * SettingsContainer to hold settings. */ @@ -206,13 +212,15 @@ public class FragmentationService { */ public FragmentationService(SettingsContainer aSettingsContainer) { //Note: Every fragmenter class should only be added once to the array or there will be problems with setting persistence! - this.fragmenters = new IMoleculeFragmenter[3]; + this.fragmenters = new IMoleculeFragmenter[4]; this.ertlFGF = new ErtlFunctionalGroupsFinderFragmenter(); this.fragmenters[0] = this.ertlFGF; this.sugarRUF = new SugarRemovalUtilityFragmenter(); this.fragmenters[1] = this.sugarRUF; this.scaffoldGF = new ScaffoldGeneratorFragmenter(); this.fragmenters[2] = this.scaffoldGF; + this.cdkEF = new CDKExhaustiveFragmenter(); + this.fragmenters[3] = this.cdkEF; // Objects.requireNonNull(aSettingsContainer, "aSettingsContainer must not be null"); this.settingsContainer = aSettingsContainer; diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index d2d79555..d3352da9 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -1,105 +1,266 @@ /* * MORTAR - MOlecule fRagmenTAtion fRamework - * Copyright (C) 2021 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) * * Source code is available at * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * 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. + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package de.unijena.cheminf.mortar.model.fragmentation.algorithm; +import de.unijena.cheminf.mortar.gui.util.GuiUtil; +import de.unijena.cheminf.mortar.message.Message; +import de.unijena.cheminf.mortar.model.io.Importer; +import de.unijena.cheminf.mortar.model.util.BasicDefinitions; +import de.unijena.cheminf.mortar.model.util.IDisplayEnum; import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; + import javafx.beans.property.Property; +import javafx.beans.property.SimpleIntegerProperty; + +import org.openscience.cdk.fragment.ExhaustiveFragmenter; import org.openscience.cdk.interfaces.IAtomContainer; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; +/** + * Wrapper class that makes the exhaustive fragmentation from the CDK, available for MORTAR. + */ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { + + /** + * The default value for the minimum fragment size used for the fragmentation. + */ + public static final int DEFAULT_MINIMUM_FRAGMENT_SIZE = 6; + // + /** + * The name of the algorithm used for fragmentation. + */ + public static final String ALGORITHM_NAME = "Exhaustive Fragmenter"; + // + /** + * The minimum size of the returned fragments. + */ + private final SimpleIntegerProperty minimumFragmentSize; + // + /** + * All settings of this fragmenter, encapsulated in JavaFX properties for binding in GUI. + */ + private final List> settings; + // + /** + * Map to store pairs of {@literal }. + */ + private final HashMap settingNameTooltipTextMap; + // + /** + * Map to store pairs of {@literal }. + */ + private final HashMap settingNameDisplayNameMap; + // + /** + * Instance of ExhaustiveFragmenter class to fragment a molecule. + */ + private final ExhaustiveFragmenter cdkExhaustiveFragmenter; + // + /** + * A property that has a constant from the IMoleculeFragmenter.FragmentSaturationOption enum as value. + */ + private final SimpleIDisplayEnumConstantProperty fragmentSaturationSetting; + // + /** + * Logger of this class. + */ + private static final Logger LOGGER = Logger.getLogger(CDKExhaustiveFragmenter.class.getName()); + // + // + // + /** + * TODO: Fix the case where the application crashes with a StackOverflow when closing the Exhaustive fragmenter settings + * Constructor, all settings are initialised with their default values as declared in the respective public constants. + */ + public CDKExhaustiveFragmenter() { + int tmpNumberOfSettings = 1; + this.settings = new ArrayList<>(tmpNumberOfSettings); + this.settingNameTooltipTextMap = new HashMap<>(tmpNumberOfSettings, + BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.settingNameDisplayNameMap = new HashMap<>(tmpNumberOfSettings, + BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.minimumFragmentSize = new SimpleIntegerProperty(this, + "Minimum Size for the returned fragments", + 6) { + @Override + public void set(int newValue) { + try { + //throws IllegalArgumentException + CDKExhaustiveFragmenter.this.minimumFragmentSize.set(newValue); + } catch (IllegalArgumentException anException) { + CDKExhaustiveFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); + //re-throws the exception to properly reset the binding + throw anException; + } + super.set(newValue); + } + }; + this.settings.add(this.minimumFragmentSize); + this.settingNameTooltipTextMap.put(this.minimumFragmentSize.getName(), + Message.get("CDKExhaustiveFragmenter.minFragmentSize.tooltip")); + this.settingNameDisplayNameMap.put(this.minimumFragmentSize.getName(), + Message.get("CDKExhaustiveFragmenter.minFragmentSize.displayName")); + this.fragmentSaturationSetting = new SimpleIDisplayEnumConstantProperty(this, "Fragment saturation setting", + IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT, IMoleculeFragmenter.FragmentSaturationOption.class) { + @Override + public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { + try { + super.set(newValue); + } catch (NullPointerException | IllegalArgumentException anException) { + CDKExhaustiveFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); + //re-throws the exception to properly reset the binding + throw anException; + } + } + }; + this.settings.add(this.fragmentSaturationSetting); + this.settingNameTooltipTextMap.put(this.fragmentSaturationSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.fragmentSaturationSetting.tooltip")); + this.settingNameDisplayNameMap.put(this.fragmentSaturationSetting.getName(), + Message.get("SugarRemovalUtilityFragmenter.fragmentSaturationSetting.displayName")); + this.cdkExhaustiveFragmenter = new ExhaustiveFragmenter(this.minimumFragmentSize.get()); + } + @Override public List> settingsProperties() { - return null; + return this.settings; } @Override public Map getSettingNameToTooltipTextMap() { - return null; + return this.settingNameTooltipTextMap; } @Override public Map getSettingNameToDisplayNameMap() { - return null; + return this.settingNameDisplayNameMap; } @Override public String getFragmentationAlgorithmName() { - return null; + return CDKExhaustiveFragmenter.ALGORITHM_NAME; } @Override public String getFragmentationAlgorithmDisplayName() { - return null; + return Message.get("CDKExhaustiveFragmenter.displayName"); } @Override - public FragmentSaturationOption getFragmentSaturationSetting() { - return null; + public IMoleculeFragmenter.FragmentSaturationOption getFragmentSaturationSetting() { + return (IMoleculeFragmenter.FragmentSaturationOption) this.fragmentSaturationSetting.get(); } @Override public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { - return null; + return this.fragmentSaturationSetting; } @Override public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { - + Objects.requireNonNull(anOption, "Given saturation option is null."); + this.fragmentSaturationSetting.set(anOption); } @Override public IMoleculeFragmenter copy() { - return null; + CDKExhaustiveFragmenter tmpCopy = new CDKExhaustiveFragmenter(); + tmpCopy.minimumFragmentSize.set(this.minimumFragmentSize.get()); + return tmpCopy; } @Override public void restoreDefaultSettings() { - + this.minimumFragmentSize.set(CDKExhaustiveFragmenter.DEFAULT_MINIMUM_FRAGMENT_SIZE); } @Override public List fragmentMolecule(IAtomContainer aMolecule) throws NullPointerException, IllegalArgumentException, CloneNotSupportedException { - return null; + // + Objects.requireNonNull(aMolecule, "Given molecule is null."); + boolean tmpCanBeFragmented = this.canBeFragmented(aMolecule); + if (!tmpCanBeFragmented) { + throw new IllegalArgumentException("Given molecule cannot be fragmented but should be filtered or preprocessed first."); + } + // + IAtomContainer tmpMoleculeClone = aMolecule.clone(); + List tmpFragments = new ArrayList<>(); + try { + this.cdkExhaustiveFragmenter.generateFragments(tmpMoleculeClone); + tmpFragments.addAll(List.of(this.cdkExhaustiveFragmenter.getFragmentsAsContainers())); + } catch (Exception anException) { + throw new IllegalArgumentException("An error occurred during fragmentation: " + anException.toString() + " Molecule Name: " + aMolecule.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)); + } + return tmpFragments; } @Override public boolean shouldBeFiltered(IAtomContainer aMolecule) { - return false; + return (Objects.isNull(aMolecule) || aMolecule.isEmpty()); } @Override public boolean shouldBePreprocessed(IAtomContainer aMolecule) throws NullPointerException { + Objects.requireNonNull(aMolecule, "Given molecule is null."); return false; } @Override public boolean canBeFragmented(IAtomContainer aMolecule) throws NullPointerException { - return false; + Objects.requireNonNull(aMolecule, "Given molecule is null."); + boolean tmpShouldBeFiltered = this.shouldBeFiltered(aMolecule); + boolean tmpShouldBePreprocessed = this.shouldBePreprocessed(aMolecule); + return !(tmpShouldBeFiltered || tmpShouldBePreprocessed); } @Override public IAtomContainer applyPreprocessing(IAtomContainer aMolecule) throws NullPointerException, IllegalArgumentException, CloneNotSupportedException { - return null; + Objects.requireNonNull(aMolecule, "Given molecule is null."); + boolean tmpShouldBeFiltered = this.shouldBeFiltered(aMolecule); + if (tmpShouldBeFiltered) { + throw new IllegalArgumentException("The given molecule cannot be preprocessed but should be filtered."); + } + if (!this.shouldBePreprocessed(aMolecule)) { + return aMolecule.clone(); + } + return aMolecule.clone(); } } diff --git a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties index ca540fc5..db1cf3a3 100644 --- a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties +++ b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties @@ -388,6 +388,10 @@ ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyA ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.OnlyAlkanes.tooltip = return only the non-functional-group alkane fragments of a molecule after fragmentation ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.displayName = functional groups and alkanes ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.tooltip = return both, functional groups and alkane fragments, after fragmentation +##CDKExhaustiveFragmenter## +CDKExhaustiveFragmenter.displayName = Exhaustive Fragmenter +CDKExhaustiveFragmenter.minFragmentSize.tooltip = Defines the minimum number of atoms (inclusive) that are required for one Fragment +CDKExhaustiveFragmenter.minFragmentSize.displayName = Minimum fragment size setting ##SugarRemovalUtilityFragmenter## SugarRemovalUtilityFragmenter.displayName = Sugar Removal Utility SugarRemovalUtilityFragmenter.returnedFragmentsSetting.tooltip = Defines which fragments should be returned, sugar moieties, the aglycone, or both From 0f0312413e9fcf9342dfbd4f4e2716912f7ee4c9 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Mon, 14 Oct 2024 01:02:06 +0200 Subject: [PATCH 03/11] work in progress: added hydrogen saturation and corrected the setting of the minimal fragment size of the exhaustive fragmenter --- .../algorithm/CDKExhaustiveFragmenter.java | 47 +++++++++++++------ .../mortar/message/Message_en_GB.properties | 2 + 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index d3352da9..b7d1510a 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -37,6 +37,8 @@ import org.openscience.cdk.fragment.ExhaustiveFragmenter; import org.openscience.cdk.interfaces.IAtomContainer; +import org.openscience.cdk.tools.CDKHydrogenAdder; +import org.openscience.cdk.tools.manipulator.AtomContainerManipulator; import java.util.ArrayList; import java.util.HashMap; @@ -99,25 +101,37 @@ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { // // /** - * TODO: Fix the case where the application crashes with a StackOverflow when closing the Exhaustive fragmenter settings * Constructor, all settings are initialised with their default values as declared in the respective public constants. */ public CDKExhaustiveFragmenter() { - int tmpNumberOfSettings = 1; - this.settings = new ArrayList<>(tmpNumberOfSettings); + int tmpNumberOfSettings = 2; this.settingNameTooltipTextMap = new HashMap<>(tmpNumberOfSettings, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); this.settingNameDisplayNameMap = new HashMap<>(tmpNumberOfSettings, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.cdkExhaustiveFragmenter = new ExhaustiveFragmenter(); this.minimumFragmentSize = new SimpleIntegerProperty(this, "Minimum Size for the returned fragments", - 6) { + DEFAULT_MINIMUM_FRAGMENT_SIZE) { @Override public void set(int newValue) { - try { - //throws IllegalArgumentException - CDKExhaustiveFragmenter.this.minimumFragmentSize.set(newValue); - } catch (IllegalArgumentException anException) { + if (newValue != 0) { + try { + //throws IllegalArgumentException + CDKExhaustiveFragmenter.this.cdkExhaustiveFragmenter.setMinimumFragmentSize(newValue); + } catch (IllegalArgumentException anException) { + CDKExhaustiveFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); + GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), + Message.get("Fragmenter.IllegalSettingValue.Header"), + anException.toString(), + anException); + //re-throws the exception to properly reset the binding + throw anException; + } + super.set(newValue); + } + else { + IllegalArgumentException anException = new IllegalArgumentException("The minimum fragment size can not be zero"); CDKExhaustiveFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), Message.get("Fragmenter.IllegalSettingValue.Header"), @@ -126,10 +140,8 @@ public void set(int newValue) { //re-throws the exception to properly reset the binding throw anException; } - super.set(newValue); } }; - this.settings.add(this.minimumFragmentSize); this.settingNameTooltipTextMap.put(this.minimumFragmentSize.getName(), Message.get("CDKExhaustiveFragmenter.minFragmentSize.tooltip")); this.settingNameDisplayNameMap.put(this.minimumFragmentSize.getName(), @@ -151,12 +163,13 @@ public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgum } } }; - this.settings.add(this.fragmentSaturationSetting); this.settingNameTooltipTextMap.put(this.fragmentSaturationSetting.getName(), - Message.get("SugarRemovalUtilityFragmenter.fragmentSaturationSetting.tooltip")); + Message.get("CDKExhaustiveFragmenter.fragmentSaturationSetting.tooltip")); this.settingNameDisplayNameMap.put(this.fragmentSaturationSetting.getName(), - Message.get("SugarRemovalUtilityFragmenter.fragmentSaturationSetting.displayName")); - this.cdkExhaustiveFragmenter = new ExhaustiveFragmenter(this.minimumFragmentSize.get()); + Message.get("CDKExhaustiveFragmenter.fragmentSaturationSetting.displayName")); + this.settings = new ArrayList<>(tmpNumberOfSettings); + this.settings.add(this.fragmentSaturationSetting); + this.settings.add(this.minimumFragmentSize); } @Override @@ -226,6 +239,12 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu try { this.cdkExhaustiveFragmenter.generateFragments(tmpMoleculeClone); tmpFragments.addAll(List.of(this.cdkExhaustiveFragmenter.getFragmentsAsContainers())); + if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION)) { + for (IAtomContainer fragment : tmpFragments) { + CDKHydrogenAdder.getInstance(fragment.getBuilder()).addImplicitHydrogens(fragment); + AtomContainerManipulator.convertImplicitToExplicitHydrogens(fragment); + } + } } catch (Exception anException) { throw new IllegalArgumentException("An error occurred during fragmentation: " + anException.toString() + " Molecule Name: " + aMolecule.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)); } diff --git a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties index db1cf3a3..c6b7cc58 100644 --- a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties +++ b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties @@ -392,6 +392,8 @@ ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.t CDKExhaustiveFragmenter.displayName = Exhaustive Fragmenter CDKExhaustiveFragmenter.minFragmentSize.tooltip = Defines the minimum number of atoms (inclusive) that are required for one Fragment CDKExhaustiveFragmenter.minFragmentSize.displayName = Minimum fragment size setting +CDKExhaustiveFragmenter.fragmentSaturationSetting.tooltip = Defines how open valences resulting from bond breakages during fragmentation should be saturated +CDKExhaustiveFragmenter.fragmentSaturationSetting.displayName = Fragment saturation setting ##SugarRemovalUtilityFragmenter## SugarRemovalUtilityFragmenter.displayName = Sugar Removal Utility SugarRemovalUtilityFragmenter.returnedFragmentsSetting.tooltip = Defines which fragments should be returned, sugar moieties, the aglycone, or both From 80e23818fb155e5f25030a1275298043bcac828b Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Tue, 15 Oct 2024 16:57:31 +0200 Subject: [PATCH 04/11] work in progress: functional implementation, except the saturation, of the exhaustive fragmentation and added the respective test class --- .../algorithm/CDKExhaustiveFragmenter.java | 43 ++++++-- .../CDKExhaustiveFragmenterTest.java | 97 +++++++++++++++++++ 2 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index b7d1510a..b39496ae 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -49,20 +49,29 @@ import java.util.logging.Logger; /** - * Wrapper class that makes the exhaustive fragmentation from the CDK, available for MORTAR. + * Wrapper class that makes the + * + * exhaustive fragmentation + * + * from the CDK, available for MORTAR. + * + * @author Tom Weiß + * @version 1.0.0.0 */ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { - + // /** * The default value for the minimum fragment size used for the fragmentation. */ - public static final int DEFAULT_MINIMUM_FRAGMENT_SIZE = 6; + private static final int DEFAULT_MINIMUM_FRAGMENT_SIZE = 6; // /** * The name of the algorithm used for fragmentation. */ - public static final String ALGORITHM_NAME = "Exhaustive Fragmenter"; + private static final String ALGORITHM_NAME = "Exhaustive Fragmenter"; + // // + // /** * The minimum size of the returned fragments. */ @@ -115,7 +124,7 @@ public CDKExhaustiveFragmenter() { DEFAULT_MINIMUM_FRAGMENT_SIZE) { @Override public void set(int newValue) { - if (newValue != 0) { + if (newValue > 0) { try { //throws IllegalArgumentException CDKExhaustiveFragmenter.this.cdkExhaustiveFragmenter.setMinimumFragmentSize(newValue); @@ -147,7 +156,7 @@ public void set(int newValue) { this.settingNameDisplayNameMap.put(this.minimumFragmentSize.getName(), Message.get("CDKExhaustiveFragmenter.minFragmentSize.displayName")); this.fragmentSaturationSetting = new SimpleIDisplayEnumConstantProperty(this, "Fragment saturation setting", - IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT, IMoleculeFragmenter.FragmentSaturationOption.class) { + IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT, FragmentSaturationOption.class) { @Override public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { try { @@ -171,7 +180,20 @@ public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgum this.settings.add(this.fragmentSaturationSetting); this.settings.add(this.minimumFragmentSize); } - + // + // + // + /** + * Returns the minimum fragment size currently set. + * + * @return the currently set minimum fragment size. + */ + public int getMinimumFragmentSize() { + return this.minimumFragmentSize.get(); + } + // + // + // @Override public List> settingsProperties() { return this.settings; @@ -198,8 +220,8 @@ public String getFragmentationAlgorithmDisplayName() { } @Override - public IMoleculeFragmenter.FragmentSaturationOption getFragmentSaturationSetting() { - return (IMoleculeFragmenter.FragmentSaturationOption) this.fragmentSaturationSetting.get(); + public FragmentSaturationOption getFragmentSaturationSetting() { + return (FragmentSaturationOption) this.fragmentSaturationSetting.get(); } @Override @@ -235,7 +257,7 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu } // IAtomContainer tmpMoleculeClone = aMolecule.clone(); - List tmpFragments = new ArrayList<>(); + List tmpFragments = new ArrayList<>(0); try { this.cdkExhaustiveFragmenter.generateFragments(tmpMoleculeClone); tmpFragments.addAll(List.of(this.cdkExhaustiveFragmenter.getFragmentsAsContainers())); @@ -282,4 +304,5 @@ public IAtomContainer applyPreprocessing(IAtomContainer aMolecule) throws NullPo } return aMolecule.clone(); } + // } diff --git a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java new file mode 100644 index 00000000..c2337feb --- /dev/null +++ b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java @@ -0,0 +1,97 @@ +/* + * MORTAR - MOlecule fRagmenTAtion fRamework + * Copyright (C) 2024 Felix Baensch, Jonas Schaub (felix.baensch@w-hs.de, jonas.schaub@uni-jena.de) + * + * Source code is available at + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.unijena.cheminf.mortar.model.fragmentation.algorithm; + +import javafx.beans.property.Property; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openscience.cdk.interfaces.IAtomContainer; +import org.openscience.cdk.silent.SilentChemObjectBuilder; +import org.openscience.cdk.smiles.SmiFlavor; +import org.openscience.cdk.smiles.SmilesGenerator; +import org.openscience.cdk.smiles.SmilesParser; + +import java.util.List; +import java.util.Locale; + +/** + * Class to test the correct workings of + * {@link de.unijena.cheminf.mortar.model.fragmentation.algorithm.CDKExhaustiveFragmenter}. + * + * @author Tom Weiß + * @version 1.0.0.0 + */ +public class CDKExhaustiveFragmenterTest { + + /** + * Constructor that sets the default locale to british english, which is important for the correct functioning of the + * fragmenter because the settings tooltips are imported from the message.properties file. + */ + CDKExhaustiveFragmenterTest() { + Locale.setDefault(Locale.of("en", "GB")); + } + // + /** + * Tests instantiation and basic settings retrieval. + * + * @throws Exception if anything goes wrong + */ + @Test + public void basicTest() throws Exception { + CDKExhaustiveFragmenter tmpFragmenter = new CDKExhaustiveFragmenter(); + Assertions.assertDoesNotThrow(tmpFragmenter::getFragmentationAlgorithmName); + Assertions.assertDoesNotThrow(tmpFragmenter::getFragmentationAlgorithmDisplayName); + for (Property tmpSetting : tmpFragmenter.settingsProperties()) { + Assertions.assertDoesNotThrow(tmpSetting::getName); + } + } + // + /** + * Does a test fragmentation on the COCONUT natural product CNP0151033. + * + * @throws Exception if anything goes wrong + */ + @Test + public void fragmentationTest() throws Exception { + SmilesParser tmpSmiPar = new SmilesParser(SilentChemObjectBuilder.getInstance()); + SmilesGenerator tmpSmiGen = new SmilesGenerator((SmiFlavor.Canonical)); + IAtomContainer tmpOriginalMolecule; + List tmpFragmentList; + CDKExhaustiveFragmenter tmpFragmenter = new CDKExhaustiveFragmenter(); + tmpFragmenter.setFragmentSaturationSetting(IMoleculeFragmenter.FragmentSaturationOption.HYDROGEN_SATURATION); + tmpOriginalMolecule = tmpSmiPar.parseSmiles( + //CNP0151033 + "O=C(OC1C(OCC2=COC(OC(=O)CC(C)C)C3C2CC(O)C3(O)COC(=O)C)OC(CO)C(O)C1O)C=CC4=CC=C(O)C=C4"); + Assertions.assertFalse(tmpFragmenter.shouldBeFiltered(tmpOriginalMolecule)); + Assertions.assertFalse(tmpFragmenter.shouldBePreprocessed(tmpOriginalMolecule)); + Assertions.assertTrue(tmpFragmenter.canBeFragmented(tmpOriginalMolecule)); + Assertions.assertDoesNotThrow(tmpFragmenter::getMinimumFragmentSize); + tmpFragmentList = tmpFragmenter.fragmentMolecule(tmpOriginalMolecule); + for (IAtomContainer tmpFragment : tmpFragmentList) { + Assertions.assertDoesNotThrow(() -> tmpSmiGen.create(tmpFragment)); + } + } +} From 03492bf8fc629065fd2b1776514a9dd77e87a373 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Tue, 15 Oct 2024 19:32:40 +0200 Subject: [PATCH 05/11] work in progress: fixed formatting error and modified fragmentation due to automatic saturation --- .../algorithm/CDKExhaustiveFragmenter.java | 19 +++++++++++-------- .../CDKExhaustiveFragmenterTest.java | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index b39496ae..b2528178 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -37,8 +37,6 @@ import org.openscience.cdk.fragment.ExhaustiveFragmenter; import org.openscience.cdk.interfaces.IAtomContainer; -import org.openscience.cdk.tools.CDKHydrogenAdder; -import org.openscience.cdk.tools.manipulator.AtomContainerManipulator; import java.util.ArrayList; import java.util.HashMap; @@ -261,12 +259,17 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu try { this.cdkExhaustiveFragmenter.generateFragments(tmpMoleculeClone); tmpFragments.addAll(List.of(this.cdkExhaustiveFragmenter.getFragmentsAsContainers())); - if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.HYDROGEN_SATURATION)) { - for (IAtomContainer fragment : tmpFragments) { - CDKHydrogenAdder.getInstance(fragment.getBuilder()).addImplicitHydrogens(fragment); - AtomContainerManipulator.convertImplicitToExplicitHydrogens(fragment); - } - } + // the fragmenter saturates by default and cant be modified to not to, so we need to revert this somehow + //TODO: find out if there is a way to revert the implicitly added hydrogens, following is not working right now +// if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.NO_SATURATION)) { +// for (IAtomContainer fragment : tmpFragments) { +// AtomContainerManipulator.clearAtomConfigurations(fragment); +// for (IAtom atom : fragment.atoms()) { +// atom.setImplicitHydrogenCount(null); +// } +// AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(fragment); +// } +// } } catch (Exception anException) { throw new IllegalArgumentException("An error occurred during fragmentation: " + anException.toString() + " Molecule Name: " + aMolecule.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)); } diff --git a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java index c2337feb..4f5e4cd0 100644 --- a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java @@ -26,6 +26,7 @@ package de.unijena.cheminf.mortar.model.fragmentation.algorithm; import javafx.beans.property.Property; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.openscience.cdk.interfaces.IAtomContainer; From 37e45d71659d0290ba20e4f38dfc17a3ecb36897 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Tue, 22 Oct 2024 02:34:08 +0200 Subject: [PATCH 06/11] work in progress: removed the saturation option --- .../algorithm/CDKExhaustiveFragmenter.java | 60 +++++-------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index b2528178..b9a029f4 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -29,14 +29,15 @@ import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.io.Importer; import de.unijena.cheminf.mortar.model.util.BasicDefinitions; -import de.unijena.cheminf.mortar.model.util.IDisplayEnum; import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleIntegerProperty; +import org.openscience.cdk.DefaultChemObjectBuilder; import org.openscience.cdk.fragment.ExhaustiveFragmenter; import org.openscience.cdk.interfaces.IAtomContainer; +import org.openscience.cdk.smiles.SmilesParser; import java.util.ArrayList; import java.util.HashMap; @@ -95,11 +96,6 @@ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { */ private final ExhaustiveFragmenter cdkExhaustiveFragmenter; // - /** - * A property that has a constant from the IMoleculeFragmenter.FragmentSaturationOption enum as value. - */ - private final SimpleIDisplayEnumConstantProperty fragmentSaturationSetting; - // /** * Logger of this class. */ @@ -153,29 +149,7 @@ public void set(int newValue) { Message.get("CDKExhaustiveFragmenter.minFragmentSize.tooltip")); this.settingNameDisplayNameMap.put(this.minimumFragmentSize.getName(), Message.get("CDKExhaustiveFragmenter.minFragmentSize.displayName")); - this.fragmentSaturationSetting = new SimpleIDisplayEnumConstantProperty(this, "Fragment saturation setting", - IMoleculeFragmenter.FRAGMENT_SATURATION_OPTION_DEFAULT, FragmentSaturationOption.class) { - @Override - public void set(IDisplayEnum newValue) throws NullPointerException, IllegalArgumentException { - try { - super.set(newValue); - } catch (NullPointerException | IllegalArgumentException anException) { - CDKExhaustiveFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), - Message.get("Fragmenter.IllegalSettingValue.Header"), - anException.toString(), - anException); - //re-throws the exception to properly reset the binding - throw anException; - } - } - }; - this.settingNameTooltipTextMap.put(this.fragmentSaturationSetting.getName(), - Message.get("CDKExhaustiveFragmenter.fragmentSaturationSetting.tooltip")); - this.settingNameDisplayNameMap.put(this.fragmentSaturationSetting.getName(), - Message.get("CDKExhaustiveFragmenter.fragmentSaturationSetting.displayName")); this.settings = new ArrayList<>(tmpNumberOfSettings); - this.settings.add(this.fragmentSaturationSetting); this.settings.add(this.minimumFragmentSize); } // @@ -219,18 +193,19 @@ public String getFragmentationAlgorithmDisplayName() { @Override public FragmentSaturationOption getFragmentSaturationSetting() { - return (FragmentSaturationOption) this.fragmentSaturationSetting.get(); + //TODO: there is currently no option to set the saturation in the exhaustive fragmenter of the cdk + return null; } @Override public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { - return this.fragmentSaturationSetting; + //TODO: there is currently no option to set the saturation in the exhaustive fragmenter of the cdk + return null; } @Override public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { - Objects.requireNonNull(anOption, "Given saturation option is null."); - this.fragmentSaturationSetting.set(anOption); + //TODO: there is currently no option to set the saturation in the exhaustive fragmenter of the cdk } @Override @@ -257,19 +232,16 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu IAtomContainer tmpMoleculeClone = aMolecule.clone(); List tmpFragments = new ArrayList<>(0); try { + List tmpSmiles = new ArrayList<>(); + SmilesParser tmpSmilesParser = new SmilesParser(DefaultChemObjectBuilder.getInstance()); this.cdkExhaustiveFragmenter.generateFragments(tmpMoleculeClone); - tmpFragments.addAll(List.of(this.cdkExhaustiveFragmenter.getFragmentsAsContainers())); - // the fragmenter saturates by default and cant be modified to not to, so we need to revert this somehow - //TODO: find out if there is a way to revert the implicitly added hydrogens, following is not working right now -// if (this.fragmentSaturationSetting.get().equals(FragmentSaturationOption.NO_SATURATION)) { -// for (IAtomContainer fragment : tmpFragments) { -// AtomContainerManipulator.clearAtomConfigurations(fragment); -// for (IAtom atom : fragment.atoms()) { -// atom.setImplicitHydrogenCount(null); -// } -// AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(fragment); -// } -// } + // there is also an option to extract atom containers directly with getFragmentsAsContainers but this oversaturates + // fragments described in this issue https://github.com/cdk/cdk/issues/1119 + tmpSmiles.addAll(List.of(this.cdkExhaustiveFragmenter.getFragments())); + for (String smile : tmpSmiles) { + tmpFragments.add(tmpSmilesParser.parseSmiles(smile)); + } + } catch (Exception anException) { throw new IllegalArgumentException("An error occurred during fragmentation: " + anException.toString() + " Molecule Name: " + aMolecule.getProperty(Importer.MOLECULE_NAME_PROPERTY_KEY)); } From e40d160eabb45552a1c948b09a2465f33da4ad84 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Tue, 22 Oct 2024 16:47:17 +0200 Subject: [PATCH 07/11] finalized implementation --- .../algorithm/CDKExhaustiveFragmenter.java | 14 +++++++------- .../mortar/message/Message_en_GB.properties | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index b9a029f4..9e3698d1 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -72,7 +72,8 @@ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { // // /** - * The minimum size of the returned fragments. + * The minimum size of the returned fragments. This size consists of all atoms, that are connected by more than + * a single bond or have more than one single bond. */ private final SimpleIntegerProperty minimumFragmentSize; // @@ -193,19 +194,19 @@ public String getFragmentationAlgorithmDisplayName() { @Override public FragmentSaturationOption getFragmentSaturationSetting() { - //TODO: there is currently no option to set the saturation in the exhaustive fragmenter of the cdk + //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. return null; } @Override public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { - //TODO: there is currently no option to set the saturation in the exhaustive fragmenter of the cdk + //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. return null; } @Override public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { - //TODO: there is currently no option to set the saturation in the exhaustive fragmenter of the cdk + //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. } @Override @@ -232,12 +233,11 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu IAtomContainer tmpMoleculeClone = aMolecule.clone(); List tmpFragments = new ArrayList<>(0); try { - List tmpSmiles = new ArrayList<>(); SmilesParser tmpSmilesParser = new SmilesParser(DefaultChemObjectBuilder.getInstance()); this.cdkExhaustiveFragmenter.generateFragments(tmpMoleculeClone); // there is also an option to extract atom containers directly with getFragmentsAsContainers but this oversaturates - // fragments described in this issue https://github.com/cdk/cdk/issues/1119 - tmpSmiles.addAll(List.of(this.cdkExhaustiveFragmenter.getFragments())); + // fragments described in this issue https://github.com/cdk/cdk/issues/1119. + List tmpSmiles = new ArrayList<>(List.of(this.cdkExhaustiveFragmenter.getFragments())); for (String smile : tmpSmiles) { tmpFragments.add(tmpSmilesParser.parseSmiles(smile)); } diff --git a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties index c6b7cc58..49761846 100644 --- a/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties +++ b/src/main/resources/de/unijena/cheminf/mortar/message/Message_en_GB.properties @@ -390,10 +390,8 @@ ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.d ErtlFunctionalGroupsFinderFragmenter.EFGFFragmenterReturnedFragmentsOption.All.tooltip = return both, functional groups and alkane fragments, after fragmentation ##CDKExhaustiveFragmenter## CDKExhaustiveFragmenter.displayName = Exhaustive Fragmenter -CDKExhaustiveFragmenter.minFragmentSize.tooltip = Defines the minimum number of atoms (inclusive) that are required for one Fragment +CDKExhaustiveFragmenter.minFragmentSize.tooltip = Defines the minimum number of atoms (inclusive) that are required for one Fragment. The number of atoms consists of all atoms, that are connected by more than one single bond or have more than one single bond. CDKExhaustiveFragmenter.minFragmentSize.displayName = Minimum fragment size setting -CDKExhaustiveFragmenter.fragmentSaturationSetting.tooltip = Defines how open valences resulting from bond breakages during fragmentation should be saturated -CDKExhaustiveFragmenter.fragmentSaturationSetting.displayName = Fragment saturation setting ##SugarRemovalUtilityFragmenter## SugarRemovalUtilityFragmenter.displayName = Sugar Removal Utility SugarRemovalUtilityFragmenter.returnedFragmentsSetting.tooltip = Defines which fragments should be returned, sugar moieties, the aglycone, or both From e9161a85865318fec45f51efabb06e88cc55b79b Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Tue, 22 Oct 2024 17:01:04 +0200 Subject: [PATCH 08/11] small refactor for consistency with other fragmenters --- .../algorithm/CDKExhaustiveFragmenter.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index 9e3698d1..999ad4e6 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -95,7 +95,7 @@ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { /** * Instance of ExhaustiveFragmenter class to fragment a molecule. */ - private final ExhaustiveFragmenter cdkExhaustiveFragmenter; + private final ExhaustiveFragmenter cdkEFInstance; // /** * Logger of this class. @@ -113,7 +113,7 @@ public CDKExhaustiveFragmenter() { BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); this.settingNameDisplayNameMap = new HashMap<>(tmpNumberOfSettings, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.cdkExhaustiveFragmenter = new ExhaustiveFragmenter(); + this.cdkEFInstance = new ExhaustiveFragmenter(); this.minimumFragmentSize = new SimpleIntegerProperty(this, "Minimum Size for the returned fragments", DEFAULT_MINIMUM_FRAGMENT_SIZE) { @@ -122,7 +122,7 @@ public void set(int newValue) { if (newValue > 0) { try { //throws IllegalArgumentException - CDKExhaustiveFragmenter.this.cdkExhaustiveFragmenter.setMinimumFragmentSize(newValue); + CDKExhaustiveFragmenter.this.cdkEFInstance.setMinimumFragmentSize(newValue); } catch (IllegalArgumentException anException) { CDKExhaustiveFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), @@ -234,10 +234,10 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu List tmpFragments = new ArrayList<>(0); try { SmilesParser tmpSmilesParser = new SmilesParser(DefaultChemObjectBuilder.getInstance()); - this.cdkExhaustiveFragmenter.generateFragments(tmpMoleculeClone); + this.cdkEFInstance.generateFragments(tmpMoleculeClone); // there is also an option to extract atom containers directly with getFragmentsAsContainers but this oversaturates // fragments described in this issue https://github.com/cdk/cdk/issues/1119. - List tmpSmiles = new ArrayList<>(List.of(this.cdkExhaustiveFragmenter.getFragments())); + List tmpSmiles = new ArrayList<>(List.of(this.cdkEFInstance.getFragments())); for (String smile : tmpSmiles) { tmpFragments.add(tmpSmilesParser.parseSmiles(smile)); } From 34e888506983e3773e48c6e42c3457325286d508 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Mon, 28 Oct 2024 05:04:20 +0100 Subject: [PATCH 09/11] minor fixes --- .../algorithm/CDKExhaustiveFragmenter.java | 99 +++++++++++-------- .../CDKExhaustiveFragmenterTest.java | 5 +- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index 999ad4e6..7f12abfc 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -29,14 +29,15 @@ import de.unijena.cheminf.mortar.message.Message; import de.unijena.cheminf.mortar.model.io.Importer; import de.unijena.cheminf.mortar.model.util.BasicDefinitions; +import de.unijena.cheminf.mortar.model.util.CollectionUtil; import de.unijena.cheminf.mortar.model.util.SimpleIDisplayEnumConstantProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleIntegerProperty; -import org.openscience.cdk.DefaultChemObjectBuilder; import org.openscience.cdk.fragment.ExhaustiveFragmenter; import org.openscience.cdk.interfaces.IAtomContainer; +import org.openscience.cdk.silent.SilentChemObjectBuilder; import org.openscience.cdk.smiles.SmilesParser; import java.util.ArrayList; @@ -52,22 +53,22 @@ * * exhaustive fragmentation * - * from the CDK, available for MORTAR. + * from the CDK available for MORTAR. * * @author Tom Weiß * @version 1.0.0.0 */ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { - // + // /** * The default value for the minimum fragment size used for the fragmentation. */ - private static final int DEFAULT_MINIMUM_FRAGMENT_SIZE = 6; + public static final int DEFAULT_MINIMUM_FRAGMENT_SIZE = 6; // /** * The name of the algorithm used for fragmentation. */ - private static final String ALGORITHM_NAME = "Exhaustive Fragmenter"; + public static final String ALGORITHM_NAME = "Exhaustive Fragmenter"; // // // @@ -75,7 +76,7 @@ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { * The minimum size of the returned fragments. This size consists of all atoms, that are connected by more than * a single bond or have more than one single bond. */ - private final SimpleIntegerProperty minimumFragmentSize; + private final SimpleIntegerProperty minimumFragmentSizeSetting; // /** * All settings of this fragmenter, encapsulated in JavaFX properties for binding in GUI. @@ -108,30 +109,22 @@ public class CDKExhaustiveFragmenter implements IMoleculeFragmenter { * Constructor, all settings are initialised with their default values as declared in the respective public constants. */ public CDKExhaustiveFragmenter() { - int tmpNumberOfSettings = 2; - this.settingNameTooltipTextMap = new HashMap<>(tmpNumberOfSettings, + int tmpNumberOfSettingsForTooltipMapSize = 1; + int tmpInitialCapacityForSettingNameTooltipTextMap = CollectionUtil.calculateInitialHashCollectionCapacity( + tmpNumberOfSettingsForTooltipMapSize, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); - this.settingNameDisplayNameMap = new HashMap<>(tmpNumberOfSettings, + this.settingNameTooltipTextMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, + BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); + this.settingNameDisplayNameMap = new HashMap<>(tmpInitialCapacityForSettingNameTooltipTextMap, BasicDefinitions.DEFAULT_HASH_COLLECTION_LOAD_FACTOR); this.cdkEFInstance = new ExhaustiveFragmenter(); - this.minimumFragmentSize = new SimpleIntegerProperty(this, + this.minimumFragmentSizeSetting = new SimpleIntegerProperty(this, "Minimum Size for the returned fragments", - DEFAULT_MINIMUM_FRAGMENT_SIZE) { + CDKExhaustiveFragmenter.DEFAULT_MINIMUM_FRAGMENT_SIZE) { @Override public void set(int newValue) { if (newValue > 0) { - try { - //throws IllegalArgumentException - CDKExhaustiveFragmenter.this.cdkEFInstance.setMinimumFragmentSize(newValue); - } catch (IllegalArgumentException anException) { - CDKExhaustiveFragmenter.LOGGER.log(Level.WARNING, anException.toString(), anException); - GuiUtil.guiExceptionAlert(Message.get("Fragmenter.IllegalSettingValue.Title"), - Message.get("Fragmenter.IllegalSettingValue.Header"), - anException.toString(), - anException); - //re-throws the exception to properly reset the binding - throw anException; - } + CDKExhaustiveFragmenter.this.cdkEFInstance.setMinimumFragmentSize(newValue); super.set(newValue); } else { @@ -146,27 +139,48 @@ public void set(int newValue) { } } }; - this.settingNameTooltipTextMap.put(this.minimumFragmentSize.getName(), + this.settingNameTooltipTextMap.put(this.minimumFragmentSizeSetting.getName(), Message.get("CDKExhaustiveFragmenter.minFragmentSize.tooltip")); - this.settingNameDisplayNameMap.put(this.minimumFragmentSize.getName(), + this.settingNameDisplayNameMap.put(this.minimumFragmentSizeSetting.getName(), Message.get("CDKExhaustiveFragmenter.minFragmentSize.displayName")); - this.settings = new ArrayList<>(tmpNumberOfSettings); - this.settings.add(this.minimumFragmentSize); + this.settings = new ArrayList<>(tmpNumberOfSettingsForTooltipMapSize); + this.settings.add(this.minimumFragmentSizeSetting); } // // // + /** + * Returns the setting for the minimum fragment size. + * + * @return the setting for the minimum fragment size. + */ + public SimpleIntegerProperty getMinimumFragmentSizeSetting() { + return this.minimumFragmentSizeSetting; + } /** * Returns the minimum fragment size currently set. * * @return the currently set minimum fragment size. */ public int getMinimumFragmentSize() { - return this.minimumFragmentSize.get(); + return this.minimumFragmentSizeSetting.get(); + } + // + // + // + + /** + * Returns the minimum fragment size currently set. + * + * @param minimumFragmentSize the new minimum fragment size. + */ + public void setMinimumFragmentSize(int minimumFragmentSize) { + this.minimumFragmentSizeSetting.set(minimumFragmentSize); } // // // + @Override public List> settingsProperties() { return this.settings; @@ -193,32 +207,36 @@ public String getFragmentationAlgorithmDisplayName() { } @Override - public FragmentSaturationOption getFragmentSaturationSetting() { + public FragmentSaturationOption getFragmentSaturationSetting() throws UnsupportedOperationException { //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. - return null; + // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not modifiable. + throw new UnsupportedOperationException("The saturation is currently not configurable for the " + CDKExhaustiveFragmenter.ALGORITHM_NAME); } @Override - public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() { + public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() throws UnsupportedOperationException { //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. - return null; + // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not modifiable. + throw new UnsupportedOperationException("The saturation is currently not configurable for the " + CDKExhaustiveFragmenter.ALGORITHM_NAME); } @Override - public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws NullPointerException { + public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws UnsupportedOperationException { //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. + // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not modifiable. + throw new UnsupportedOperationException("The saturation is currently not configurable for the " + CDKExhaustiveFragmenter.ALGORITHM_NAME); } @Override public IMoleculeFragmenter copy() { CDKExhaustiveFragmenter tmpCopy = new CDKExhaustiveFragmenter(); - tmpCopy.minimumFragmentSize.set(this.minimumFragmentSize.get()); + tmpCopy.minimumFragmentSizeSetting.set(this.minimumFragmentSizeSetting.get()); return tmpCopy; } @Override public void restoreDefaultSettings() { - this.minimumFragmentSize.set(CDKExhaustiveFragmenter.DEFAULT_MINIMUM_FRAGMENT_SIZE); + this.minimumFragmentSizeSetting.set(CDKExhaustiveFragmenter.DEFAULT_MINIMUM_FRAGMENT_SIZE); } @Override @@ -231,12 +249,12 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu } // IAtomContainer tmpMoleculeClone = aMolecule.clone(); - List tmpFragments = new ArrayList<>(0); + List tmpFragments = new ArrayList<>(tmpMoleculeClone.getAtomCount() / 2); try { - SmilesParser tmpSmilesParser = new SmilesParser(DefaultChemObjectBuilder.getInstance()); + SmilesParser tmpSmilesParser = new SmilesParser(SilentChemObjectBuilder.getInstance()); this.cdkEFInstance.generateFragments(tmpMoleculeClone); - // there is also an option to extract atom containers directly with getFragmentsAsContainers but this oversaturates - // fragments described in this issue https://github.com/cdk/cdk/issues/1119. + // TODO: there is also an option to extract atom containers directly with getFragmentsAsContainers but this + // oversaturates fragments described in this issue https://github.com/cdk/cdk/issues/1119. List tmpSmiles = new ArrayList<>(List.of(this.cdkEFInstance.getFragments())); for (String smile : tmpSmiles) { tmpFragments.add(tmpSmilesParser.parseSmiles(smile)); @@ -274,9 +292,6 @@ public IAtomContainer applyPreprocessing(IAtomContainer aMolecule) throws NullPo if (tmpShouldBeFiltered) { throw new IllegalArgumentException("The given molecule cannot be preprocessed but should be filtered."); } - if (!this.shouldBePreprocessed(aMolecule)) { - return aMolecule.clone(); - } return aMolecule.clone(); } // diff --git a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java index 4f5e4cd0..15d486c8 100644 --- a/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java +++ b/src/test/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenterTest.java @@ -82,17 +82,20 @@ public void fragmentationTest() throws Exception { IAtomContainer tmpOriginalMolecule; List tmpFragmentList; CDKExhaustiveFragmenter tmpFragmenter = new CDKExhaustiveFragmenter(); - tmpFragmenter.setFragmentSaturationSetting(IMoleculeFragmenter.FragmentSaturationOption.HYDROGEN_SATURATION); tmpOriginalMolecule = tmpSmiPar.parseSmiles( //CNP0151033 "O=C(OC1C(OCC2=COC(OC(=O)CC(C)C)C3C2CC(O)C3(O)COC(=O)C)OC(CO)C(O)C1O)C=CC4=CC=C(O)C=C4"); Assertions.assertFalse(tmpFragmenter.shouldBeFiltered(tmpOriginalMolecule)); Assertions.assertFalse(tmpFragmenter.shouldBePreprocessed(tmpOriginalMolecule)); Assertions.assertTrue(tmpFragmenter.canBeFragmented(tmpOriginalMolecule)); + Assertions.assertDoesNotThrow(tmpFragmenter::getMinimumFragmentSizeSetting); Assertions.assertDoesNotThrow(tmpFragmenter::getMinimumFragmentSize); tmpFragmentList = tmpFragmenter.fragmentMolecule(tmpOriginalMolecule); for (IAtomContainer tmpFragment : tmpFragmentList) { Assertions.assertDoesNotThrow(() -> tmpSmiGen.create(tmpFragment)); } + int tmpMinimumFragmentSize = 8; + tmpFragmenter.setMinimumFragmentSize(tmpMinimumFragmentSize); + Assertions.assertEquals(tmpMinimumFragmentSize, tmpFragmenter.getMinimumFragmentSize()); } } From 75f31a28ceb9e8f96da360721932183dd7197531 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Mon, 4 Nov 2024 19:09:25 +0100 Subject: [PATCH 10/11] applied requested fixes and added some further documentation to the exhaustive fragmenter class --- .../algorithm/CDKExhaustiveFragmenter.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index 7f12abfc..94e89be0 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -53,7 +53,8 @@ * * exhaustive fragmentation * - * from the CDK available for MORTAR. + * from the CDK available for MORTAR. It has a runtime of O(n²) where n is the number of splittable bonds. Splittable + * bonds are defined as non-ring, single bonds that are connected to at least one other atom by an additional bond * * @author Tom Weiß * @version 1.0.0.0 @@ -209,21 +210,21 @@ public String getFragmentationAlgorithmDisplayName() { @Override public FragmentSaturationOption getFragmentSaturationSetting() throws UnsupportedOperationException { //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. - // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not modifiable. + // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not configurable. throw new UnsupportedOperationException("The saturation is currently not configurable for the " + CDKExhaustiveFragmenter.ALGORITHM_NAME); } @Override public SimpleIDisplayEnumConstantProperty fragmentSaturationSettingProperty() throws UnsupportedOperationException { //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. - // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not modifiable. + // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not configurable. throw new UnsupportedOperationException("The saturation is currently not configurable for the " + CDKExhaustiveFragmenter.ALGORITHM_NAME); } @Override public void setFragmentSaturationSetting(FragmentSaturationOption anOption) throws UnsupportedOperationException { //TODO: there is currently no possibility to implement saturation settings for the exhaustive fragmenter. - // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not modifiable. + // Because the exhaustive fragmenter in the CDK saturates the fragments by default and is not configurable. throw new UnsupportedOperationException("The saturation is currently not configurable for the " + CDKExhaustiveFragmenter.ALGORITHM_NAME); } @@ -249,7 +250,9 @@ public List fragmentMolecule(IAtomContainer aMolecule) throws Nu } // IAtomContainer tmpMoleculeClone = aMolecule.clone(); - List tmpFragments = new ArrayList<>(tmpMoleculeClone.getAtomCount() / 2); + // a rough estimation of the number of unique fragments produced by this fragmenter. + int fragmentListSizeEstimation = tmpMoleculeClone.getAtomCount() / 2; + List tmpFragments = new ArrayList<>(fragmentListSizeEstimation); try { SmilesParser tmpSmilesParser = new SmilesParser(SilentChemObjectBuilder.getInstance()); this.cdkEFInstance.generateFragments(tmpMoleculeClone); From 8d5c796dc2ad98c323b3ea17758ab985756eef12 Mon Sep 17 00:00:00 2001 From: ToLeWeiss Date: Thu, 7 Nov 2024 11:12:24 +0100 Subject: [PATCH 11/11] correction for the performance and added further information --- .../fragmentation/algorithm/CDKExhaustiveFragmenter.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java index 94e89be0..a784bd66 100644 --- a/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java +++ b/src/main/java/de/unijena/cheminf/mortar/model/fragmentation/algorithm/CDKExhaustiveFragmenter.java @@ -53,8 +53,9 @@ * * exhaustive fragmentation * - * from the CDK available for MORTAR. It has a runtime of O(n²) where n is the number of splittable bonds. Splittable - * bonds are defined as non-ring, single bonds that are connected to at least one other atom by an additional bond + * from the CDK available for MORTAR. It has a performance of O(n!) where n is the number of splittable bonds. Splittable + * bonds are defined as non-ring, single bonds that are connected to at least one other atom, that is not an + * implicit hydrogen. * * @author Tom Weiß * @version 1.0.0.0