Search in sources :

Example 21 with Structure

use of de.bioforscher.jstructure.model.structure.Structure in project jstructure by JonStargaryen.

the class A01_CreatePyMolRenderJobsForEarlyFoldingResidues method composePyMolCommand.

private static String composePyMolCommand(String line) {
    String[] split = line.split(";");
    String entryId = split[0];
    String pdbId = split[1];
    List<Integer> experimentIds = Pattern.compile(",").splitAsStream(split[2].replaceAll("\\[", "").replaceAll("]", "")).map(Integer::valueOf).collect(Collectors.toList());
    Structure structure = StructureParser.fromPdbId(pdbId).parse();
    Chain chain = structure.chains().findFirst().get();
    Start2FoldXmlParser.parseSpecificExperiment(chain, Start2FoldConstants.XML_DIRECTORY.resolve(entryId + ".xml"), experimentIds);
    List<Integer> earlyFoldingResidues = chain.aminoAcids().filter(aminoAcid -> aminoAcid.getFeature(Start2FoldResidueAnnotation.class).isEarly()).map(AminoAcid::getResidueIdentifier).map(ResidueIdentifier::getResidueNumber).collect(Collectors.toList());
    return "delete all" + System.lineSeparator() + "fetch " + pdbId + ", async=0" + System.lineSeparator() + // hide non-relevant stuff
    "hide everything" + System.lineSeparator() + "show cartoon, chain A" + System.lineSeparator() + // decolor everything
    "color grey80" + System.lineSeparator() + "zoom (chain A)" + System.lineSeparator() + earlyFoldingResidues.stream().map(res -> "color efr, resi " + res).collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator() + "ray" + System.lineSeparator() + "png " + Start2FoldConstants.PYMOL_DIRECTORY.resolve(entryId + "-efr.png") + System.lineSeparator();
}
Also used : Start2FoldResidueAnnotation(de.bioforscher.jstructure.efr.model.Start2FoldResidueAnnotation) Files(java.nio.file.Files) ResidueIdentifier(de.bioforscher.jstructure.model.identifier.ResidueIdentifier) Structure(de.bioforscher.jstructure.model.structure.Structure) IOException(java.io.IOException) StructureParser(de.bioforscher.jstructure.model.structure.StructureParser) Collectors(java.util.stream.Collectors) Start2FoldXmlParser(de.bioforscher.jstructure.efr.parser.Start2FoldXmlParser) Start2FoldConstants(de.bioforscher.jstructure.efr.Start2FoldConstants) List(java.util.List) AminoAcid(de.bioforscher.jstructure.model.structure.aminoacid.AminoAcid) Chain(de.bioforscher.jstructure.model.structure.Chain) StandardFormat(de.bioforscher.jstructure.StandardFormat) Pattern(java.util.regex.Pattern) Chain(de.bioforscher.jstructure.model.structure.Chain) AminoAcid(de.bioforscher.jstructure.model.structure.aminoacid.AminoAcid) Start2FoldResidueAnnotation(de.bioforscher.jstructure.efr.model.Start2FoldResidueAnnotation) Structure(de.bioforscher.jstructure.model.structure.Structure)

Example 22 with Structure

use of de.bioforscher.jstructure.model.structure.Structure in project jstructure by JonStargaryen.

the class A02_WriteDatasetCsv method handleLine.

private static Optional<String> handleLine(String line) {
    try {
        System.out.println(line);
        String[] split = line.split(";");
        String entryId = split[0];
        String pdbId = split[1];
        List<Integer> experimentIds = Pattern.compile(",").splitAsStream(split[2].replaceAll("\\[", "").replaceAll("]", "")).map(Integer::valueOf).collect(Collectors.toList());
        Structure structure = StructureParser.fromPdbId(pdbId).parse();
        Chain chain = structure.chains().findFirst().get();
        Start2FoldXmlParser.parseStability(chain, Start2FoldConstants.XML_DIRECTORY.resolve(entryId + ".xml"));
        Start2FoldXmlParser.parseSpecificExperiment(chain, Start2FoldConstants.XML_DIRECTORY.resolve(entryId + ".xml"), experimentIds);
        EvolutionaryCouplingParser.parseHotSpotFile(chain, Start2FoldConstants.COUPLING_DIRECTORY.resolve(entryId.toUpperCase() + "_hs.html"));
        EQuantParser.parseEQuantFile(chain, Start2FoldConstants.EQUANT_DIRECTORY.resolve(entryId.toLowerCase() + ".equant-small.txt"));
        List<AminoAcid> earlyFoldingResidues = chain.aminoAcids().filter(aminoAcid -> aminoAcid.getFeature(Start2FoldResidueAnnotation.class).isEarly()).collect(Collectors.toList());
        List<AminoAcid> stableResidues = chain.aminoAcids().filter(aminoAcid -> aminoAcid.getFeature(Start2FoldResidueAnnotation.class).isStrong()).collect(Collectors.toList());
        List<Integer> functionalResidueNumbers = Start2FoldConstants.extractFunctionalResidueNumbers(split);
        List<AminoAcid> functionalResidues = new ArrayList<>();
        // do nothing if no annotation of functional residues exists
        if (!functionalResidueNumbers.isEmpty()) {
            FunctionalResidueParser.parse(chain, functionalResidueNumbers);
            chain.aminoAcids().filter(aminoAcid -> aminoAcid.getFeature(FunctionalResidueAnnotation.class).isFunctional()).forEach(functionalResidues::add);
        }
        List<AminoAcid> aminoAcids = chain.aminoAcids().collect(Collectors.toList());
        ResidueGraph conventionalProteinGraph = ResidueGraph.createResidueGraph(chain, ContactDefinitionFactory.createAlphaCarbonContactDefinition(8));
        return Optional.of(chain.aminoAcids().map(aminoAcid -> {
            GenericSecondaryStructure sse = aminoAcid.getFeature(GenericSecondaryStructure.class);
            HotSpotScoring hotSpotScoring = aminoAcid.getFeature(HotSpotScoring.class);
            PLIPInteractionContainer plipInteractionContainer = aminoAcid.getFeature(PLIPInteractionContainer.class);
            PLIPInteractionContainer nonLocalPlipInteractionContainer = new PLIPInteractionContainer(null, plipInteractionContainer.getInteractions().stream().filter(inter -> Math.abs(inter.getPartner1().getResidueIndex() - inter.getPartner2().getResidueIndex()) > 5).collect(Collectors.toList()));
            PLIPInteractionContainer localPlipInteractionContainer = new PLIPInteractionContainer(null, plipInteractionContainer.getInteractions().stream().filter(inter -> !nonLocalPlipInteractionContainer.getInteractions().contains(inter)).collect(Collectors.toList()));
            String equantScore = "NA";
            try {
                equantScore = StandardFormat.format(aminoAcid.getFeature(EQuantScore.class).getEvaluation());
            } catch (ComputationException e) {
                logger.warn("missing equant scoring for {}", aminoAcid);
            }
            String functionalAnnotation = "NA";
            if (functionalResidues.size() > 0) {
                functionalAnnotation = functionalResidues.contains(aminoAcid) ? "functional" : "non-functional";
            }
            ResidueTopologicPropertiesContainer residueTopologicPropertiesContainer = aminoAcid.getFeature(ResidueTopologicPropertiesContainer.class);
            double terminusDistance = aminoAcids.indexOf(aminoAcid);
            terminusDistance = Math.min(terminusDistance, aminoAcids.size() - terminusDistance);
            terminusDistance /= (double) aminoAcids.size();
            return pdbId + "," + "A" + "," + aminoAcid.getResidueIdentifier() + "," + aminoAcid.getOneLetterCode() + "," + sse.getSecondaryStructure().getReducedRepresentation() + "," + sse.getSecondaryStructure().getOneLetterRepresentation() + "," + sse.getSurroundingSecondaryStructureElement(aminoAcid).getSize() + "," + (aminoAcid.getFeature(AccessibleSurfaceArea.class).isExposed() ? "exposed" : "buried") + "," + StandardFormat.format(aminoAcid.getFeature(GeometricProperties.class).getDistanceToCentroid()) + "," + StandardFormat.format(terminusDistance) + "," + plipInteractionContainer.getHydrogenBonds().size() + "," + plipInteractionContainer.getHydrophobicInteractions().size() + "," + plipInteractionContainer.getBackboneInteractions().size() + "," + plipInteractionContainer.getInteractions().size() + "," + localPlipInteractionContainer.getHydrogenBonds().size() + "," + localPlipInteractionContainer.getHydrophobicInteractions().size() + "," + localPlipInteractionContainer.getBackboneInteractions().size() + "," + localPlipInteractionContainer.getInteractions().size() + "," + nonLocalPlipInteractionContainer.getHydrogenBonds().size() + "," + nonLocalPlipInteractionContainer.getHydrophobicInteractions().size() + "," + nonLocalPlipInteractionContainer.getBackboneInteractions().size() + "," + nonLocalPlipInteractionContainer.getInteractions().size() + "," + StandardFormat.format(aminoAcid.getFeature(EnergyProfile.class).getSolvationEnergy()) + "," + StandardFormat.format(aminoAcid.getFeature(EgorAgreement.class).getEgorPrediction()) + "," + equantScore + "," + StandardFormat.format(aminoAcid.getFeature(AccessibleSurfaceArea.class).getRelativeAccessibleSurfaceArea()) + "," + StandardFormat.format(aminoAcid.getFeature(LoopFraction.class).getLoopFraction()) + "," + hotSpotScoring.getEcCount() + "," + StandardFormat.format(hotSpotScoring.getCumStrength()) + "," + StandardFormat.format(hotSpotScoring.getEcStrength()) + "," + hotSpotScoring.getConservation() + "," + StandardFormat.format(residueTopologicPropertiesContainer.getFullPlip().getBetweenness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getFullPlip().getCloseness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getFullPlip().getClusteringCoefficient()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getHydrogenPlip().getBetweenness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getHydrogenPlip().getCloseness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getHydrogenPlip().getClusteringCoefficient()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getHydrophobicPlip().getBetweenness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getHydrophobicPlip().getCloseness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getHydrophobicPlip().getClusteringCoefficient()) + "," + conventionalProteinGraph.getContactsOf(aminoAcid).size() + "," + conventionalProteinGraph.getLocalContactsOf(aminoAcid).size() + "," + conventionalProteinGraph.getNonLocalContactsOf(aminoAcid).size() + "," + StandardFormat.format(residueTopologicPropertiesContainer.getConventional().getBetweenness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getConventional().getCloseness()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getConventional().getClusteringCoefficient()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getFullPlip().getDistinctNeighborhoodCount()) + "," + StandardFormat.format(residueTopologicPropertiesContainer.getConventional().getDistinctNeighborhoodCount()) + "," + (earlyFoldingResidues.contains(aminoAcid) ? "early" : "late") + "," + functionalAnnotation + "," + (stableResidues.contains(aminoAcid) ? "stable" : "unstable");
        }).collect(Collectors.joining(System.lineSeparator())));
    } catch (Exception e) {
        logger.info("calculation failed for {}", line, e);
        return Optional.empty();
    }
}
Also used : FunctionalResidueParser(de.bioforscher.jstructure.efr.parser.FunctionalResidueParser) EQuantParser(de.bioforscher.jstructure.efr.parser.EQuantParser) LoopFraction(de.bioforscher.jstructure.feature.loopfraction.LoopFraction) ComputationException(de.bioforscher.jstructure.model.feature.ComputationException) LoggerFactory(org.slf4j.LoggerFactory) EQuantScore(de.bioforscher.jstructure.efr.model.EQuantScore) Structure(de.bioforscher.jstructure.model.structure.Structure) GenericSecondaryStructure(de.bioforscher.jstructure.feature.sse.GenericSecondaryStructure) StructureParser(de.bioforscher.jstructure.model.structure.StructureParser) EgorAgreement(de.bioforscher.jstructure.feature.energyprofile.EgorAgreement) HotSpotScoring(de.bioforscher.jstructure.efr.model.HotSpotScoring) ArrayList(java.util.ArrayList) AminoAcid(de.bioforscher.jstructure.model.structure.aminoacid.AminoAcid) Chain(de.bioforscher.jstructure.model.structure.Chain) StandardFormat(de.bioforscher.jstructure.StandardFormat) EnergyProfile(de.bioforscher.jstructure.feature.energyprofile.EnergyProfile) Logger(org.slf4j.Logger) GeometricProperties(de.bioforscher.jstructure.feature.geometry.GeometricProperties) Start2FoldResidueAnnotation(de.bioforscher.jstructure.efr.model.Start2FoldResidueAnnotation) Files(java.nio.file.Files) IOException(java.io.IOException) Collectors(java.util.stream.Collectors) FunctionalResidueAnnotation(de.bioforscher.jstructure.efr.model.FunctionalResidueAnnotation) Start2FoldXmlParser(de.bioforscher.jstructure.efr.parser.Start2FoldXmlParser) Start2FoldConstants(de.bioforscher.jstructure.efr.Start2FoldConstants) List(java.util.List) AccessibleSurfaceArea(de.bioforscher.jstructure.feature.asa.AccessibleSurfaceArea) ResidueTopologicPropertiesContainer(de.bioforscher.jstructure.graph.ResidueTopologicPropertiesContainer) ResidueGraph(de.bioforscher.jstructure.graph.ResidueGraph) EvolutionaryCouplingParser(de.bioforscher.jstructure.efr.parser.EvolutionaryCouplingParser) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) PLIPInteractionContainer(de.bioforscher.jstructure.feature.interaction.PLIPInteractionContainer) ContactDefinitionFactory(de.bioforscher.jstructure.graph.contact.definition.ContactDefinitionFactory) Chain(de.bioforscher.jstructure.model.structure.Chain) ArrayList(java.util.ArrayList) GenericSecondaryStructure(de.bioforscher.jstructure.feature.sse.GenericSecondaryStructure) HotSpotScoring(de.bioforscher.jstructure.efr.model.HotSpotScoring) LoopFraction(de.bioforscher.jstructure.feature.loopfraction.LoopFraction) ComputationException(de.bioforscher.jstructure.model.feature.ComputationException) Structure(de.bioforscher.jstructure.model.structure.Structure) GenericSecondaryStructure(de.bioforscher.jstructure.feature.sse.GenericSecondaryStructure) GeometricProperties(de.bioforscher.jstructure.feature.geometry.GeometricProperties) AccessibleSurfaceArea(de.bioforscher.jstructure.feature.asa.AccessibleSurfaceArea) AminoAcid(de.bioforscher.jstructure.model.structure.aminoacid.AminoAcid) EgorAgreement(de.bioforscher.jstructure.feature.energyprofile.EgorAgreement) Start2FoldResidueAnnotation(de.bioforscher.jstructure.efr.model.Start2FoldResidueAnnotation) ResidueTopologicPropertiesContainer(de.bioforscher.jstructure.graph.ResidueTopologicPropertiesContainer) FunctionalResidueAnnotation(de.bioforscher.jstructure.efr.model.FunctionalResidueAnnotation) EQuantScore(de.bioforscher.jstructure.efr.model.EQuantScore) ComputationException(de.bioforscher.jstructure.model.feature.ComputationException) IOException(java.io.IOException) EnergyProfile(de.bioforscher.jstructure.feature.energyprofile.EnergyProfile) ResidueGraph(de.bioforscher.jstructure.graph.ResidueGraph) PLIPInteractionContainer(de.bioforscher.jstructure.feature.interaction.PLIPInteractionContainer)

Example 23 with Structure

use of de.bioforscher.jstructure.model.structure.Structure in project jstructure by JonStargaryen.

the class A02_EFConservationPerColumn method main.

public static void main(String[] args) throws IOException {
    // load column conservation
    class1Columns = extractSequenceConservationFromFile("/home/bittrich/git/aars_data/T05_msa/C1_conservation.txt").stream().map(Column::new).collect(Collectors.toList());
    class2Columns = extractSequenceConservationFromFile("/home/bittrich/git/aars_data/T05_msa/C2_conservation.txt").stream().map(Column::new).collect(Collectors.toList());
    Files.list(Paths.get("/home/bittrich/git/phd_sb_repo/data/aars/EFoldMine_code/ef-predictions/")).forEach(predictionPath -> {
        try {
            String aarsId = predictionPath.toFile().getName().split("\\.")[0];
            System.out.println("processing " + aarsId);
            // String[] split = aarsId.split("_");
            // String pdbId = split[0];
            // String chainId = split[1];
            String className = Files.exists(Paths.get("/home/bittrich/git/aars_data/T06_renumbered_structures/C1/" + aarsId + ".pdb")) ? "C1" : "C2";
            // Path originalStructurePath = Paths.get("/home/bittrich/git/aars_data/T06_renumbered_structures/" + className + "/" + aarsId + ".pdb");
            Path renumberedStructurePath = Paths.get("/home/bittrich/git/aars_data/T06_renumbered_structures/" + className + "/renum/" + aarsId + "_renum.pdb");
            // Structure originalStructure = StructureParser.fromPath(originalStructurePath).parse();
            Structure renumberedStructure = StructureParser.fromPath(renumberedStructurePath).parse();
            List<Column> columns = className.equals("C1") ? class1Columns : class2Columns;
            List<Double> values = Files.lines(predictionPath).skip(1).filter(line -> !line.startsWith("*")).map(line -> line.split("\\s+")[1]).map(Double::valueOf).collect(Collectors.toList());
            // assign EFpredictions to correct column
            for (int i = 0; i < values.size(); i++) {
                double value = values.get(i);
                // AminoAcid originalAminoAcid = originalStructure.getFirstChain().getAminoAcids().get(i);
                AminoAcid renumberedAminoAcid = renumberedStructure.getFirstChain().getAminoAcids().get(i);
                // check for sanity
                // if(!originalAminoAcid.getThreeLetterCode().equals(renumberedAminoAcid.getThreeLetterCode())) {
                // System.out.println(originalAminoAcid.toString() + " -> " + renumberedAminoAcid.toString() + " : " + value);
                // }
                Column column = columns.get(renumberedAminoAcid.getResidueIdentifier().getResidueNumber() - 1);
                column.addEfPrediction(value);
            }
            // assign functional annotation
            // from individual binding sites (this will miss structures where no ligands are present)
            // Path contactPath = Paths.get("/home/bittrich/git/aars_data/T09_interactions/" + className + "/contacts_per_structure.txt");
            // List<Integer> functionalPositions = Files.lines(contactPath)
            // .filter(line -> line.startsWith(aarsId))
            // .map(line -> line.split("\t")[1])
            // .map(line -> line.substring(1, line.length() - 1))
            // .flatMap(line -> Pattern.compile(",").splitAsStream(line))
            // .map(Integer::valueOf)
            // .collect(Collectors.toList());
            // consider all every observed binding sites across all aaRS types
            Path contactPath = Paths.get("/home/bittrich/git/aars_data/T09_interactions/" + className + "/contacts_all.txt");
            List<Integer> functionalPositions = Files.lines(contactPath).map(line -> line.split("\t")[1]).map(line -> line.substring(1, line.length() - 1)).flatMap(line -> Pattern.compile(",").splitAsStream(line)).map(Integer::valueOf).collect(Collectors.toList());
            for (int functionalPosition : functionalPositions) {
                columns.get(functionalPosition - 1).setFunctional(true);
            }
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    });
    System.out.println("class I");
    String class1Line = composeClassSpecificOutput(class1Columns, Paths.get("/home/bittrich/git/phd_sb_repo/data/aars/EFoldMine_code/C1_ef_frequencies.csv"), 81);
    System.out.println("class II");
    String class2Line = composeClassSpecificOutput(class2Columns, Paths.get("/home/bittrich/git/phd_sb_repo/data/aars/EFoldMine_code/C2_ef_frequencies.csv"), 75);
    Files.write(Paths.get("/home/bittrich/git/phd_sb_repo/data/aars/EFoldMine_code/summary.csv"), ("class;total;func;ef_cons;ef_avg;intersect_cons;intersect_avg" + System.lineSeparator() + "class1;" + class1Line + System.lineSeparator() + "class2;" + class2Line).getBytes());
}
Also used : Path(java.nio.file.Path) Files(java.nio.file.Files) Structure(de.bioforscher.jstructure.model.structure.Structure) IOException(java.io.IOException) StructureParser(de.bioforscher.jstructure.model.structure.StructureParser) Collectors(java.util.stream.Collectors) ArrayList(java.util.ArrayList) UncheckedIOException(java.io.UncheckedIOException) List(java.util.List) Paths(java.nio.file.Paths) StringJoiner(java.util.StringJoiner) AminoAcid(de.bioforscher.jstructure.model.structure.aminoacid.AminoAcid) StandardFormat(de.bioforscher.jstructure.StandardFormat) Pattern(java.util.regex.Pattern) Path(java.nio.file.Path) AminoAcid(de.bioforscher.jstructure.model.structure.aminoacid.AminoAcid) UncheckedIOException(java.io.UncheckedIOException) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException) Structure(de.bioforscher.jstructure.model.structure.Structure)

Example 24 with Structure

use of de.bioforscher.jstructure.model.structure.Structure in project jstructure by JonStargaryen.

the class ProteinLigandInteractionProfilerTest method test_ligand_5in3.

@Test
public void test_ligand_5in3() {
    Structure structure = StructureParser.fromPdbId("5in3").parse();
    InteractionContainer interactions = instance.annotateLigandInteractions(structure);
    System.out.println(interactions);
}
Also used : InteractionContainer(de.bioforscher.jstructure.feature.plip.model.InteractionContainer) Structure(de.bioforscher.jstructure.model.structure.Structure) Test(org.junit.Test)

Example 25 with Structure

use of de.bioforscher.jstructure.model.structure.Structure in project jstructure by JonStargaryen.

the class ProteinLigandInteractionProfilerTest method test_ligand_1eve.

@Test
public void test_ligand_1eve() {
    Structure structure = StructureParser.fromPdbId("1eve").parse();
    Group ligand = structure.select().chainId("A").residueNumber(2001).asGroup();
    Group res84 = structure.select().residueNumber(84).asGroup();
    Group res279 = structure.select().residueNumber(279).asGroup();
    Group res330 = structure.select().residueNumber(330).asGroup();
    InteractionContainer interactions = instance.annotateLigandInteractions(structure).getSubsetOfInteractions(ligand);
    Assert.assertFalse(interactions.getSubsetOfInteractions(res84).getPiStackingInteractions().isEmpty());
    Assert.assertFalse(interactions.getSubsetOfInteractions(res279).getPiStackingInteractions().isEmpty());
    Assert.assertFalse(interactions.getSubsetOfInteractions(res330).getPiCationInteractions().isEmpty());
}
Also used : InteractionContainer(de.bioforscher.jstructure.feature.plip.model.InteractionContainer) Group(de.bioforscher.jstructure.model.structure.Group) Structure(de.bioforscher.jstructure.model.structure.Structure) Test(org.junit.Test)

Aggregations

Structure (de.bioforscher.jstructure.model.structure.Structure)61 IOException (java.io.IOException)45 Collectors (java.util.stream.Collectors)40 Chain (de.bioforscher.jstructure.model.structure.Chain)39 Files (java.nio.file.Files)35 StructureParser (de.bioforscher.jstructure.model.structure.StructureParser)30 Path (java.nio.file.Path)26 AminoAcid (de.bioforscher.jstructure.model.structure.aminoacid.AminoAcid)23 List (java.util.List)22 StandardFormat (de.bioforscher.jstructure.StandardFormat)21 Logger (org.slf4j.Logger)20 LoggerFactory (org.slf4j.LoggerFactory)20 Test (org.junit.Test)19 Group (de.bioforscher.jstructure.model.structure.Group)18 UncheckedIOException (java.io.UncheckedIOException)18 Pattern (java.util.regex.Pattern)17 Stream (java.util.stream.Stream)17 Jsoup (org.jsoup.Jsoup)17 ComputationException (de.bioforscher.jstructure.model.feature.ComputationException)16 java.util (java.util)15