Repository: matchms/matchms

Issue:
ProgressReport can count one changed spectrum multiple times
Currently ProgressReport.add_to_report, adds a count for each field that has been changed, instead of each spectrum that has been changed. 
This is a bit counterintuitive. For a filter like derive_adduct_from_name, this will result in a count for changes and a count for additions, since the compound_name field is changed, and a value was added to adduct. 

Suggested change:
Remove the differentation between addition and changes, and just keep the column changes, which can only be counted once. The column removed spectra will of course also stay. 

Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=matchms_matchms&pullRequest=501)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=matchms_matchms&pullRequest=501&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=matchms_matchms&pullRequest=501&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=matchms_matchms&pullRequest=501&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=CODE_SMELL)

[![83.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '83.3%')](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_coverage&view=list) [83.3% Coverage](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=matchms_matchms&pullRequest=501)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=matchms_matchms&pullRequest=501&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=matchms_matchms&pullRequest=501&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=matchms_matchms&pullRequest=501&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=matchms_matchms&pullRequest=501&resolved=false&types=CODE_SMELL)

[![83.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/60-16px.png '83.3%')](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_coverage&view=list) [83.3% Coverage](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=matchms_matchms&pullRequest=501&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/matchms/Pipeline.py b/matchms/Pipeline.py
index 32214b4e..e8e941b0 100644
--- a/matchms/Pipeline.py
+++ b/matchms/Pipeline.py
@@ -5,11 +5,11 @@ from typing import Iterable, List, Optional, Union
 import yaml
 import matchms.similarity as mssimilarity
 from matchms import calculate_scores
+from matchms.filtering.SpectrumProcessor import SpectrumProcessor
 from matchms.importing.load_spectra import load_list_of_spectrum_files
 from matchms.logging_functions import (add_logging_to_file,
                                        reset_matchms_logger,
                                        set_matchms_logger_level)
-from matchms.SpectrumProcessor import SpectrumProcessor
 from matchms.typing import SpectrumType
 
 
diff --git a/matchms/__init__.py b/matchms/__init__.py
index 515e05f6..2533280b 100644
--- a/matchms/__init__.py
+++ b/matchms/__init__.py
@@ -1,3 +1,4 @@
+from matchms.filtering.SpectrumProcessor import SpectrumProcessor
 from . import exporting, filtering, importing, networking, plotting, similarity
 from .__version__ import __version__
 from .calculate_scores import calculate_scores
@@ -8,7 +9,6 @@ from .Metadata import Metadata
 from .Pipeline import Pipeline
 from .Scores import Scores
 from .Spectrum import Spectrum
-from .SpectrumProcessor import SpectrumProcessor
 
 
 _init_logger()
diff --git a/matchms/SpectrumProcessor.py b/matchms/filtering/SpectrumProcessor.py
similarity index 70%
rename from matchms/SpectrumProcessor.py
rename to matchms/filtering/SpectrumProcessor.py
index 812340aa..f8ce9280 100644
--- a/matchms/SpectrumProcessor.py
+++ b/matchms/filtering/SpectrumProcessor.py
@@ -4,8 +4,9 @@ from typing import Dict, Optional, Tuple, Union
 import numpy as np
 import pandas as pd
 from tqdm import tqdm
-import matchms.filtering as msfilters
 from matchms import Spectrum
+from matchms.filtering.filter_order_and_default_pipelines import (
+    ALL_FILTERS, FILTER_FUNCTION_NAMES, PREDEFINED_PIPELINES)
 
 
 class SpectrumProcessor:
@@ -55,15 +56,15 @@ class SpectrumProcessor:
             filter function and the second element is a dictionary containing additional arguments for the function.
         """
         if isinstance(filter_spec, str):
-            if filter_spec not in FILTER_FUNCTIONS:
-                raise ValueError("Unknown filter type. Should be known filter name or function.")
-            filter_func = FILTER_FUNCTIONS[filter_spec]
+            if filter_spec not in FILTER_FUNCTION_NAMES:
+                raise ValueError(f"Unknown filter type: {filter_spec} Should be known filter name or function.")
+            filter_func = FILTER_FUNCTION_NAMES[filter_spec]
         elif isinstance(filter_spec, (tuple, list)):
             filter_name, filter_args = filter_spec
-            if filter_name not in FILTER_FUNCTIONS:
-                raise ValueError("Unknown filter type. Should be known filter name or function.")
-            filter_func = partial(FILTER_FUNCTIONS[filter_name], **filter_args)
-            filter_func.__name__ = FILTER_FUNCTIONS[filter_name].__name__
+            if filter_name not in FILTER_FUNCTION_NAMES:
+                raise ValueError(f"Unknown filter type: {filter_name} Should be known filter name or function.")
+            filter_func = partial(FILTER_FUNCTION_NAMES[filter_name], **filter_args)
+            filter_func.__name__ = FILTER_FUNCTION_NAMES[filter_name].__name__
         else:
             raise TypeError("filter_spec should be a string or a tuple or list")
 
@@ -194,96 +195,6 @@ class SpectrumProcessor:
         return summary_string
 
 
-# List all filters in a functionally working order
-ALL_FILTERS = [msfilters.make_charge_int,
-               msfilters.add_compound_name,
-               msfilters.derive_adduct_from_name,
-               msfilters.derive_formula_from_name,
-               msfilters.clean_compound_name,
-               msfilters.interpret_pepmass,
-               msfilters.add_precursor_mz,
-               msfilters.add_retention_index,
-               msfilters.add_retention_time,
-               msfilters.derive_ionmode,
-               msfilters.correct_charge,
-               # msfilters.derive_adduct_from_name,  # run again? Or improve those filters?
-               # msfilters.derive_formula_from_name,  # run again? Or improve those filters?
-               msfilters.require_precursor_mz,
-               msfilters.add_parent_mass,
-               msfilters.harmonize_undefined_inchikey,
-               msfilters.harmonize_undefined_inchi,
-               msfilters.harmonize_undefined_smiles,
-               msfilters.repair_inchi_inchikey_smiles,
-               msfilters.derive_smiles_from_inchi,
-               msfilters.repair_smiles_from_compound_name,
-               msfilters.derive_inchi_from_smiles,
-               msfilters.derive_inchikey_from_inchi,
-               msfilters.clean_adduct,
-               msfilters.repair_smiles_of_salts,
-               msfilters.repair_precursor_is_parent_mass,
-               msfilters.repair_parent_mass_is_mol_wt,
-               msfilters.repair_adduct_based_on_smiles,
-               msfilters.repair_parent_mass_match_smiles_wrapper,
-               msfilters.repair_not_matching_annotation,
-               msfilters.require_correct_ionmode,
-               msfilters.require_precursor_below_mz,
-               msfilters.require_valid_annotation,
-               msfilters.require_parent_mass_match_smiles,
-               msfilters.normalize_intensities,
-               msfilters.select_by_intensity,
-               msfilters.select_by_mz,
-               msfilters.select_by_relative_intensity,
-               msfilters.remove_peaks_around_precursor_mz,
-               msfilters.remove_peaks_outside_top_k,
-               msfilters.reduce_to_number_of_peaks,
-               msfilters.require_minimum_number_of_peaks,
-               msfilters.require_minimum_of_high_peaks,
-               msfilters.add_fingerprint,
-               msfilters.add_losses,
-              ]
-
-FILTER_FUNCTIONS = {x.__name__: x for x in ALL_FILTERS}
-
-MINIMAL_FILTERS = ["make_charge_int",
-                   "interpret_pepmass",
-                   "derive_ionmode",
-                   "correct_charge",
-                   ]
-BASIC_FILTERS = MINIMAL_FILTERS \
-    + ["add_compound_name",
-       "derive_adduct_from_name",
-       "derive_formula_from_name",
-       "clean_compound_name",
-       "add_precursor_mz",
-    ]
-DEFAULT_FILTERS = BASIC_FILTERS \
-    + ["require_precursor_mz",
-       "add_parent_mass",
-       "harmonize_undefined_inchikey",
-       "harmonize_undefined_inchi",
-       "harmonize_undefined_smiles",
-       "repair_inchi_inchikey_smiles",
-       "normalize_intensities",
-    ]
-FULLY_ANNOTATED_PROCESSING = DEFAULT_FILTERS \
-    + ["clean_adduct",
-       "derive_inchi_from_smiles",
-       "derive_smiles_from_inchi",
-       "derive_inchikey_from_inchi",
-       ("require_correct_ionmode", {"ion_mode_to_keep": "both"}),
-       ("require_parent_mass_match_smiles", {'mass_tolerance': 0.1}),
-       "repair_not_matching_annotation"
-       "require_valid_annotation",
-    ]
-
-PREDEFINED_PIPELINES = {
-    "minimal": MINIMAL_FILTERS,
-    "basic": BASIC_FILTERS,
-    "default": DEFAULT_FILTERS,
-    "fully_annotated": FULLY_ANNOTATED_PROCESSING,
-}
-
-
 class ProcessingReport:
     """Class to keep track of spectrum changes during filtering.
     """
diff --git a/matchms/filtering/filter_order_and_default_pipelines.py b/matchms/filtering/filter_order_and_default_pipelines.py
new file mode 100644
index 00000000..361f7850
--- /dev/null
+++ b/matchms/filtering/filter_order_and_default_pipelines.py
@@ -0,0 +1,88 @@
+from matchms import filtering as msfilters
+
+
+# List all filters in a functionally working order
+ALL_FILTERS = [msfilters.make_charge_int,
+               msfilters.add_compound_name,
+               msfilters.derive_adduct_from_name,
+               msfilters.derive_formula_from_name,
+               msfilters.clean_compound_name,
+               msfilters.interpret_pepmass,
+               msfilters.add_precursor_mz,
+               msfilters.add_retention_index,
+               msfilters.add_retention_time,
+               msfilters.derive_ionmode,
+               msfilters.correct_charge,
+               # msfilters.derive_adduct_from_name,  # run again? Or improve those filters?
+               # msfilters.derive_formula_from_name,  # run again? Or improve those filters?
+               msfilters.require_precursor_mz,
+               msfilters.add_parent_mass,
+               msfilters.harmonize_undefined_inchikey,
+               msfilters.harmonize_undefined_inchi,
+               msfilters.harmonize_undefined_smiles,
+               msfilters.repair_inchi_inchikey_smiles,
+               msfilters.derive_smiles_from_inchi,
+               msfilters.repair_smiles_from_compound_name,
+               msfilters.derive_inchi_from_smiles,
+               msfilters.derive_inchikey_from_inchi,
+               msfilters.clean_adduct,
+               msfilters.repair_smiles_of_salts,
+               msfilters.repair_precursor_is_parent_mass,
+               msfilters.repair_parent_mass_is_mol_wt,
+               msfilters.repair_adduct_based_on_smiles,
+               msfilters.repair_parent_mass_match_smiles_wrapper,
+               msfilters.repair_not_matching_annotation,
+               msfilters.require_valid_annotation,
+               msfilters.require_correct_ionmode,
+               msfilters.require_precursor_below_mz,
+               msfilters.require_parent_mass_match_smiles,
+               msfilters.normalize_intensities,
+               msfilters.select_by_intensity,
+               msfilters.select_by_mz,
+               msfilters.select_by_relative_intensity,
+               msfilters.remove_peaks_around_precursor_mz,
+               msfilters.remove_peaks_outside_top_k,
+               msfilters.reduce_to_number_of_peaks,
+               msfilters.require_minimum_number_of_peaks,
+               msfilters.require_minimum_of_high_peaks,
+               msfilters.add_fingerprint,
+               msfilters.add_losses,
+               ]
+FILTER_FUNCTION_NAMES = {x.__name__: x for x in ALL_FILTERS}
+MINIMAL_FILTERS = ["make_charge_int",
+                   "interpret_pepmass",
+                   "derive_ionmode",
+                   "correct_charge",
+                   ]
+BASIC_FILTERS = MINIMAL_FILTERS \
+                + ["add_compound_name",
+                   "derive_adduct_from_name",
+                   "derive_formula_from_name",
+                   "clean_compound_name",
+                   "add_precursor_mz",
+                   ]
+DEFAULT_FILTERS = BASIC_FILTERS \
+                  + ["require_precursor_mz",
+                     "add_parent_mass",
+                     "harmonize_undefined_inchikey",
+                     "harmonize_undefined_inchi",
+                     "harmonize_undefined_smiles",
+                     "repair_inchi_inchikey_smiles",
+                     "normalize_intensities",
+                     ]
+FULLY_ANNOTATED_PROCESSING = DEFAULT_FILTERS \
+                             + ["clean_adduct",
+                                "derive_inchi_from_smiles",
+                                "derive_smiles_from_inchi",
+                                "derive_inchikey_from_inchi",
+                                ("require_correct_ionmode", {"ion_mode_to_keep": "both"}),
+                                ("require_parent_mass_match_smiles", {'mass_tolerance': 0.1}),
+                                "repair_not_matching_annotation",
+                                "require_valid_annotation",
+                                ]
+PREDEFINED_PIPELINES = {
+    "minimal": MINIMAL_FILTERS,
+    "basic": BASIC_FILTERS,
+    "default": DEFAULT_FILTERS,
+    "fully_annotated": FULLY_ANNOTATED_PROCESSING,
+}
diff --git a/matchms/filtering/filter_utils/get_neutral_mass_from_smiles.py b/matchms/filtering/filter_utils/get_neutral_mass_from_smiles.py
index d0fb93c6..1a74d5fc 100644
--- a/matchms/filtering/filter_utils/get_neutral_mass_from_smiles.py
+++ b/matchms/filtering/filter_utils/get_neutral_mass_from_smiles.py
@@ -1,6 +1,11 @@
+import logging
+from typing import Optional
 from matchms.constants import PROTON_MASS
 
 
+logger = logging.getLogger("matchms")
+
+
 try:  # rdkit is not included in pip package
     from rdkit import Chem
     from rdkit.Chem import Descriptors
@@ -37,7 +42,6 @@ def get_monoisotopic_neutral_mass(smiles: str) -> float:
     return _get_neutral_mass(smiles, True)
 
 
-
 def get_molecular_weight_neutral_mass(smiles: str) -> float:
     """Get the (average) molecular weight for the isotopic distribution of the SMILES code.
 
@@ -53,7 +57,7 @@ def get_molecular_weight_neutral_mass(smiles: str) -> float:
     return _get_neutral_mass(smiles, False)
 
 
-def _get_neutral_mass(smiles:str, monoisotopic: bool) -> float:
+def _get_neutral_mass(smiles: str, monoisotopic: bool) -> Optional[float]:
     """Get neutral mass of molecule, either average or most abundant monoisotopic mass.
 
     Args:
@@ -69,6 +73,9 @@ def _get_neutral_mass(smiles:str, monoisotopic: bool) -> float:
     if not _has_rdkit:
         raise ImportError(rdkit_missing_message)
     mol = Chem.MolFromSmiles(smiles)
+    if mol is None:
+        logger.warning("No mass could be calculated for smiles: %s, since it is not a valid smiles.", smiles)
+        return None
     mass = Descriptors.ExactMolWt(mol) if monoisotopic else Descriptors.MolWt(mol)
     charge = sum(atom.GetFormalCharge() for atom in mol.GetAtoms())
     neutral_mass = mass + -charge * PROTON_MASS
diff --git a/matchms/filtering/metadata_processing/repair_adduct_based_on_smiles.py b/matchms/filtering/metadata_processing/repair_adduct_based_on_smiles.py
index b577997a..c48cf453 100644
--- a/matchms/filtering/metadata_processing/repair_adduct_based_on_smiles.py
+++ b/matchms/filtering/metadata_processing/repair_adduct_based_on_smiles.py
@@ -11,7 +11,7 @@ logger = logging.getLogger("matchms")
 
 def repair_adduct_based_on_smiles(spectrum_in: Spectrum,
                                   mass_tolerance,
-                                  accept_parent_mass_is_mol_wt):
+                                  accept_parent_mass_is_mol_wt = True):
     """If the parent mass is wrong due to a wrong of is derived from the precursor mz
     To do this the charge and adduct are used"""
     if spectrum_in is None:
@@ -30,6 +30,8 @@ def repair_adduct_based_on_smiles(spectrum_in: Spectrum,
 
     adducts_df = load_known_adducts()
     smiles_mass = get_monoisotopic_neutral_mass(changed_spectrum.get("smiles"))
+    if smiles_mass is None:
+        return changed_spectrum
     parent_masses = (precursor_mz - adducts_df["correction_mass"]) / adducts_df["mass_multiplier"]
     mass_differences = abs(parent_masses-smiles_mass)
 
diff --git a/matchms/filtering/metadata_processing/repair_parent_mass_is_mol_wt.py b/matchms/filtering/metadata_processing/repair_parent_mass_is_mol_wt.py
index 747037d8..3a0826c6 100644
--- a/matchms/filtering/metadata_processing/repair_parent_mass_is_mol_wt.py
+++ b/matchms/filtering/metadata_processing/repair_parent_mass_is_mol_wt.py
@@ -27,8 +27,10 @@ def repair_parent_mass_is_mol_wt(spectrum_in: Spectrum, mass_tolerance: float):
     # Check if parent mass matches the smiles mass
     parent_mass = spectrum.get("parent_mass")
     smiles = spectrum.get("smiles")
-    smiles_mass = get_molecular_weight_neutral_mass(smiles)
-    mass_difference = parent_mass - smiles_mass
+    smiles_molecular_weight = get_molecular_weight_neutral_mass(smiles)
+    if smiles_molecular_weight is None:
+        return spectrum
+    mass_difference = parent_mass - smiles_molecular_weight
     if abs(mass_difference) < mass_tolerance:
         correct_mass = get_monoisotopic_neutral_mass(smiles)
         spectrum.set("parent_mass", correct_mass)
diff --git a/matchms/filtering/metadata_processing/repair_parent_mass_match_smiles_wrapper.py b/matchms/filtering/metadata_processing/repair_parent_mass_match_smiles_wrapper.py
index 30a1e81e..9647b277 100644
--- a/matchms/filtering/metadata_processing/repair_parent_mass_match_smiles_wrapper.py
+++ b/matchms/filtering/metadata_processing/repair_parent_mass_match_smiles_wrapper.py
@@ -6,34 +6,30 @@ from matchms.typing import SpectrumType
 from .repair_parent_mass_is_mol_wt import repair_parent_mass_is_mol_wt
 from .repair_precursor_is_parent_mass import repair_precursor_is_parent_mass
 from .repair_smiles_of_salts import repair_smiles_of_salts
-from .require_parent_mass_match_smiles import require_parent_mass_match_smiles
+from .require_parent_mass_match_smiles import \
+    _check_smiles_and_parent_mass_match
 
 
 logger = logging.getLogger("matchms")
 
 
-def repair_parent_mass_match_smiles_wrapper(spectrum_in: SpectrumType, mass_tolerance: float = 0.2) -> Optional[SpectrumType]:
+def repair_parent_mass_match_smiles_wrapper(spectrum_in: SpectrumType,
+                                            mass_tolerance: float = 0.2) -> Optional[SpectrumType]:
     """Wrapper function for repairing a mismatch between parent mass and smiles mass"""
     if spectrum_in is None:
         return None
     spectrum = spectrum_in.clone()
 
-    if require_parent_mass_match_smiles(spectrum, mass_tolerance) is not None:
-        return spectrum
-
-    spectrum = repair_smiles_of_salts(spectrum, mass_tolerance)
-    if require_parent_mass_match_smiles(spectrum, mass_tolerance) is not None:
-        return spectrum
-
-    spectrum = repair_precursor_is_parent_mass(spectrum, mass_tolerance)
-    if require_parent_mass_match_smiles(spectrum, mass_tolerance) is not None:
-        return spectrum
-
-    spectrum = repair_parent_mass_is_mol_wt(spectrum, mass_tolerance)
-    if require_parent_mass_match_smiles(spectrum, mass_tolerance) is not None:
-        return spectrum
-
-    spectrum = repair_adduct_based_on_smiles(spectrum, mass_tolerance, accept_parent_mass_is_mol_wt=True)
-    if require_parent_mass_match_smiles(spectrum, mass_tolerance) is not None:
-        return spectrum
+    filters_to_apply = [repair_smiles_of_salts,
+                        repair_precursor_is_parent_mass,
+                        repair_parent_mass_is_mol_wt,
+                        repair_adduct_based_on_smiles,
+                        ]
+    for filter_function in filters_to_apply:
+        if _check_smiles_and_parent_mass_match(smiles=spectrum.get("smiles"),
+                                               parent_mass=spectrum.get("parent_mass"),
+                                               mass_tolerance=mass_tolerance):
+            return spectrum
+        spectrum = filter_function(spectrum_in=spectrum,
+                                   mass_tolerance=mass_tolerance)
     return spectrum
diff --git a/matchms/filtering/metadata_processing/repair_precursor_is_parent_mass.py b/matchms/filtering/metadata_processing/repair_precursor_is_parent_mass.py
index 8478a0b2..05c22440 100644
--- a/matchms/filtering/metadata_processing/repair_precursor_is_parent_mass.py
+++ b/matchms/filtering/metadata_processing/repair_precursor_is_parent_mass.py
@@ -29,6 +29,8 @@ def repair_precursor_is_parent_mass(spectrum_in: Spectrum,
 
     smiles = spectrum.get("smiles")
     smiles_mass = get_monoisotopic_neutral_mass(smiles)
+    if smiles_mass is None:
+        return spectrum
     mass_difference = precursor_mz - smiles_mass
     if abs(mass_difference) < mass_tolerance:
         logger.info("Parent mass was changed from %s to %s", spectrum.get("parent_mass"), smiles_mass)
diff --git a/matchms/filtering/metadata_processing/repair_smiles_of_salts.py b/matchms/filtering/metadata_processing/repair_smiles_of_salts.py
index 7f31c8f0..579899ed 100644
--- a/matchms/filtering/metadata_processing/repair_smiles_of_salts.py
+++ b/matchms/filtering/metadata_processing/repair_smiles_of_salts.py
@@ -2,6 +2,8 @@ import itertools
 import logging
 from matchms.filtering.filter_utils.get_neutral_mass_from_smiles import \
     get_monoisotopic_neutral_mass
+from matchms.filtering.filter_utils.smile_inchi_inchikey_conversions import \
+    is_valid_smiles
 
 
 logger = logging.getLogger("matchms")
@@ -17,6 +19,9 @@ def repair_smiles_of_salts(spectrum_in,
     spectrum = spectrum_in.clone()
 
     smiles = spectrum.get("smiles")
+    if not is_valid_smiles(smiles):
+        logger.warning("No valid smiles, please run require_valid_annotation before")
+        return spectrum
     parent_mass = spectrum.get("parent_mass")
     possible_ion_combinations = _create_possible_ions(smiles)
     if not possible_ion_combinations:
diff --git a/matchms/filtering/metadata_processing/require_parent_mass_match_smiles.py b/matchms/filtering/metadata_processing/require_parent_mass_match_smiles.py
index 3ea46378..f1fcf9a4 100644
--- a/matchms/filtering/metadata_processing/require_parent_mass_match_smiles.py
+++ b/matchms/filtering/metadata_processing/require_parent_mass_match_smiles.py
@@ -38,6 +38,8 @@ def require_parent_mass_match_smiles(spectrum_in: SpectrumType,
 def _check_smiles_and_parent_mass_match(smiles, parent_mass, mass_tolerance) -> bool:
     """Returns True if smiles and parent mass are matching"""
     smiles_mass = get_monoisotopic_neutral_mass(smiles)
+    if smiles_mass is None:
+        return False
     mass_difference = parent_mass - smiles_mass
     if abs(mass_difference) < mass_tolerance:
         return True


Test patch:
diff --git a/tests/filtering/test_all_filter_order.py b/tests/filtering/test_all_filter_order.py
new file mode 100644
index 00000000..28a6dc6e
--- /dev/null
+++ b/tests/filtering/test_all_filter_order.py
@@ -0,0 +1,90 @@
+"""These tests are meant to check that certain conditions are maintained,
+when changing the ALL_FILTERS order in the future"""
+import ast
+import os
+from typing import Callable, List
+import pytest
+from matchms import filtering as msfilters
+from matchms.filtering.filter_order_and_default_pipelines import (
+    ALL_FILTERS, PREDEFINED_PIPELINES)
+from matchms.filtering.SpectrumProcessor import SpectrumProcessor
+
+
+REPAIR_PARENT_MASS_SMILES_FILTERS = \
+    [msfilters.repair_smiles_of_salts, msfilters.repair_precursor_is_parent_mass,
+     msfilters.repair_parent_mass_is_mol_wt, msfilters.repair_adduct_based_on_smiles,
+     msfilters.repair_parent_mass_match_smiles_wrapper, ]
+ANNOTATION_REPARATIONS = [msfilters.repair_inchi_inchikey_smiles, msfilters.derive_smiles_from_inchi,
+                          msfilters.repair_smiles_from_compound_name, msfilters.derive_inchi_from_smiles,
+                          msfilters.derive_inchikey_from_inchi, ]
+
+
+@pytest.mark.parametrize("early_filters, later_filters", [
+    [[msfilters.repair_not_matching_annotation], [msfilters.require_valid_annotation]],
+    [REPAIR_PARENT_MASS_SMILES_FILTERS, [msfilters.require_parent_mass_match_smiles]],
+    [ANNOTATION_REPARATIONS + [msfilters.clean_adduct], REPAIR_PARENT_MASS_SMILES_FILTERS],
+    [[msfilters.add_precursor_mz,], [msfilters.require_precursor_mz,]],
+])
+def test_all_filter_order(early_filters: List[Callable], later_filters: List[Callable]):
+    """Tests if early_filter is run before later_filter"""
+    for early_filter in early_filters:
+        for later_filter in later_filters:
+            for filter_index, filter_function in enumerate(ALL_FILTERS):
+                if early_filter == filter_function:
+                    early_filter_index = filter_index
+                if later_filter == filter_function:
+                    later_filter_index = filter_index
+            assert later_filter_index > early_filter_index, \
+                f"The filter {early_filter.__name__} should be before {later_filter.__name__}"
+
+
+def test_all_filters_is_complete():
+    """Checks that the global varible ALL_FILTERS contains all the available filters
+
+    This is important, since performing tests in the wrong order can make some filters useless.
+    """
+    def get_functions_from_file(file_path):
+        """Gets all python functions in a file"""
+        with open(file_path, 'r', encoding="utf-8") as file:
+            tree = ast.parse(file.read(), filename=file_path)
+        functions = []
+        for node in ast.walk(tree):
+            if isinstance(node, ast.FunctionDef):
+                functions.append(node.name)
+        return functions
+
+    current_dir = os.path.dirname(os.path.abspath(__file__))
+    filtering_directory = os.path.join(current_dir, "../../matchms/filtering")
+    directories_with_filters = ["metadata_processing",
+                                "peak_processing"]
+
+    all_filters = [filter.__name__ for filter in ALL_FILTERS]
+    list_of_filter_function_names = []
+    for directory_with_filters in directories_with_filters:
+        directory_with_filters = os.path.join(filtering_directory, directory_with_filters)
+        scripts = os.listdir(directory_with_filters)
+        for script in scripts:
+            # Remove __init__
+            if script[0] == "_":
+                break
+            if script[-3:] == ".py":
+                functions = get_functions_from_file(os.path.join(directory_with_filters, script))
+                for function in functions:
+                    if function[0] != "_":
+                        list_of_filter_function_names.append((script, function))
+    for script, filter_function in list_of_filter_function_names:
+        assert filter_function in all_filters, \
+            f"The function {filter_function} in the script {script} is not given in ALL_FILTERS, " \
+            f"this should be added to ensure a correct order of filter functions." \
+            f"If this function is not a filter add a _ before the function name"
+
+
+def test_all_filters_no_duplicates():
+    all_filters = [filter.__name__ for filter in ALL_FILTERS]
+    assert len(all_filters) == len(set(all_filters)), "One of the filters appears twice in ALL_FILTERS"
+
+
+def test_create_predefined_pipelines():
+    """Tests if all predefined pipelines can be run"""
+    for pipeline_name in PREDEFINED_PIPELINES:
+        SpectrumProcessor(pipeline_name)
diff --git a/tests/filtering/test_spectrum_processor.py b/tests/filtering/test_spectrum_processor.py
index ce7b9bd3..5c6ef7ed 100644
--- a/tests/filtering/test_spectrum_processor.py
+++ b/tests/filtering/test_spectrum_processor.py
@@ -1,9 +1,7 @@
-import ast
-import os
 import numpy as np
 import pytest
 from matchms import SpectrumProcessor
-from matchms.SpectrumProcessor import ALL_FILTERS, ProcessingReport
+from matchms.filtering.SpectrumProcessor import ProcessingReport
 from ..builder_Spectrum import SpectrumBuilder
 
 
@@ -177,49 +175,3 @@ def test_add_filter_with_matchms_filter(spectrums):
     assert filters[-1].__name__ == "require_correct_ionmode"
     spectrums, _ = processor.process_spectrums(spectrums, create_report=True)
     assert not spectrums, "Expected to be empty list"
-
-
-def test_all_filters_is_complete():
-    """Checks that the global varible ALL_FILTERS contains all the available filters
-
-    This is important, since performing tests in the wrong order can make some filters useless.
-    """
-    def get_functions_from_file(file_path):
-        """Gets all python functions in a file"""
-        with open(file_path, 'r', encoding="utf-8") as file:
-            tree = ast.parse(file.read(), filename=file_path)
-        functions = []
-        for node in ast.walk(tree):
-            if isinstance(node, ast.FunctionDef):
-                functions.append(node.name)
-        return functions
-
-    current_dir = os.path.dirname(os.path.abspath(__file__))
-    filtering_directory = os.path.join(current_dir, "../../matchms/filtering")
-    directories_with_filters = ["metadata_processing",
-                                "peak_processing"]
-
-    all_filters = [filter.__name__ for filter in ALL_FILTERS]
-    list_of_filter_function_names = []
-    for directory_with_filters in directories_with_filters:
-        directory_with_filters = os.path.join(filtering_directory, directory_with_filters)
-        scripts = os.listdir(directory_with_filters)
-        for script in scripts:
-            # Remove __init__
-            if script[0] == "_":
-                break
-            if script[-3:] == ".py":
-                functions = get_functions_from_file(os.path.join(directory_with_filters, script))
-                for function in functions:
-                    if function[0] != "_":
-                        list_of_filter_function_names.append((script, function))
-    for script, filter_function in list_of_filter_function_names:
-        assert filter_function in all_filters, \
-            f"The function {filter_function} in the script {script} is not given in ALL_FILTERS, " \
-            f"this should be added to ensure a correct order of filter functions." \
-            f"If this function is not a filter add a _ before the function name"
-
-
-def test_all_filters_no_duplicates():
-    all_filters = [filter.__name__ for filter in ALL_FILTERS]
-    assert len(all_filters) == len(set(all_filters)), "One of the filters appears twice in ALL_FILTERS"