Skip to content

Commit

Permalink
Fixed several issues related to analyzing sequences containing only the
Browse files Browse the repository at this point in the history
randomized region. Added batch mode, allowing the user to import
multiplexed, pools without primers.
  • Loading branch information
drivenbyentropy committed Jul 15, 2021
1 parent f6aebc9 commit 898b857
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -987,8 +987,6 @@ private void computeIndexOrder(ProgressPaneController pp) {
// Temporarily store the counts in an array as random access to mapdb is slow but sequential access is fast
int[] counts = new int[aptamer_ids.length];

System.out.println("here");

// Provide a progress bar for this operation
pp.setShowProgressBar(true);

Expand Down Expand Up @@ -1019,9 +1017,6 @@ public void run() {
pp.setProgress(items_to_sort.doubleValue()/cycle_to_sort_by.getUniqueSize() );
pp.setProgressLabel(String.format("Retrieved %s/%s items (%.2f%%)", items_to_sort.get(), cycle_to_sort_by.getUniqueSize(), (items_to_sort.doubleValue()/cycle_to_sort_by.getUniqueSize())*100.0) );

System.out.println(items_to_sort.get() + " " + cycle_to_sort_by.getUniqueSize());
System.out.flush();

// Once every half a second should suffice
Thread.sleep(500);

Expand Down
17 changes: 16 additions & 1 deletion src/main/java/gui/wizards/newexperiment/DataModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public class DataModel {
private SimpleIntegerProperty aptaplexParserPrimerTolerance = new SimpleIntegerProperty( Configuration.getDefaults().containsKey("AptaplexParser.PrimerTolerance") ? Configuration.getDefaults().getInt("AptaplexParser.PrimerTolerance") : Configuration.getParameters().getInt("AptaplexParser.PrimerTolerance") );

private File lastSelectedDirectory = null;

private BooleanProperty noPrimers = new SimpleBooleanProperty(false);


/**
Expand Down Expand Up @@ -438,6 +440,19 @@ public SimpleIntegerProperty getAptaplexParserPrimerTolerance() {
public void setAptaplexParserPrimerTolerance(SimpleIntegerProperty aptaplexParserPrimerTolerance) {
this.aptaplexParserPrimerTolerance = aptaplexParserPrimerTolerance;
}


/**
* @return the noPrimers
*/
public BooleanProperty getNoPrimers() {
return noPrimers;
}

/**
* @param aptaplexParserPrimerTolerance the aptaplexParserPrimerTolerance to set
*/
public void setNoPrimers(BooleanProperty noPrimers) {
this.noPrimers = noPrimers;
}

}
70 changes: 66 additions & 4 deletions src/main/java/gui/wizards/newexperiment/Wizard1Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public class Wizard1Controller{
@FXML
private CheckBox rangeLengthCheckBox;

@FXML
private CheckBox noPrimersCheckbox;


private List<SelectionCycleDetailsController> selectionCycleDetailsControllers = new ArrayList<SelectionCycleDetailsController>();

Expand Down Expand Up @@ -141,6 +144,28 @@ public void init() {
randomizedRegionSizeLowerSpinner.getValueFactory().valueProperty().bindBidirectional(getDataModel().getRandomizedRegionSizeLower());
randomizedRegionSizeUpperSpinner.getValueFactory().valueProperty().bindBidirectional(getDataModel().getRandomizedRegionSizeUpper());

noPrimersCheckbox.selectedProperty().bindBidirectional(getDataModel().getNoPrimers());

// Prevent access to "No primer" checkbox if data is not demultiplexed or paired end.
noPrimersCheckbox.disableProperty().bind(Bindings.or(getDataModel().getIsDemultiplexed().not(), getDataModel().getIsPairedEnd()));

// Disable primer input if user has selected the noPrimerCheckbox
primer5TextField.disableProperty().bind(noPrimersCheckbox.selectedProperty());
primer3TextField.disableProperty().bind(noPrimersCheckbox.selectedProperty());

// Clear the primer text fields if the user checks the "No Primer" checkbox
noPrimersCheckbox.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
primer5TextField.clear();
primer3TextField.clear();
}
});

// If the checkbox is disabled, we need to unselect it
if (noPrimersCheckbox.isDisabled()) {
getDataModel().getNoPrimers().set(false);
}

// Make sure that either only the exact size or the range or non of them is selected
exactLengthCheckBox.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
Expand Down Expand Up @@ -210,8 +235,8 @@ public boolean validateData() {

// First the trivial things:

// Make sure the 5' primer is present
if (primer5TextField.textProperty().isEmpty().get()) {
// Make sure the 5' primer is present, but only if we are not in batch mode
if (primer5TextField.textProperty().isEmpty().get() && noPrimersCheckbox.selectedProperty().not().get()) {

ControlFXValidatorFactory.setTemporaryValidation(
primer5TextField,
Expand All @@ -225,8 +250,9 @@ public boolean validateData() {

}

// Make sure that either the 3' primer is present or the randomized region size is not 0
if (primer3TextField.textProperty().isEmpty().get() && randomizedRegionSizeSpinner.getValueFactory().getValue().equals(0)) {

// Make sure that either the 3' primer is present or the randomized region size is not 0 and we are not in batch mode
if (primer3TextField.textProperty().isEmpty().get() && randomizedRegionSizeSpinner.getValueFactory().getValue().equals(0) && noPrimersCheckbox.selectedProperty().not().get()) {

ControlFXValidatorFactory.setTemporaryValidation(
primer3TextField,
Expand All @@ -248,6 +274,36 @@ public boolean validateData() {

}

// Make sure that if we are in batch mode we have either a fixed randomized region size or a range
if (noPrimersCheckbox.selectedProperty().get() && exactLengthCheckBox.selectedProperty().not().get() && this.rangeLengthCheckBox.selectedProperty().not().get()) {

ControlFXValidatorFactory.setTemporaryValidation(
exactLengthCheckBox,
ControlFXValidatorFactory.AllwaysWrongValidator("Either an exact randomized region or a range must be specified."),
this.validationSupport,
ControlFXValidatorFactory.AllwaysCorrectValidator,
false
);

is_valid = false;

}

// Make sure that if an exact length is specified it must be greater than 0
if (this.exactLengthCheckBox.isSelected() && randomizedRegionSizeSpinner.getValueFactory().getValue() <= 0) {

ControlFXValidatorFactory.setTemporaryValidation(
randomizedRegionSizeSpinner,
ControlFXValidatorFactory.AllwaysWrongValidator("Randomized region size must be greater than zero."),
this.validationSupport,
ControlFXValidatorFactory.AllwaysCorrectValidator,
false
);

is_valid = false;

}

// Make sure that if a range for the randomized region size is specified, it is a valid one
if (this.rangeLengthCheckBox.isSelected()) {

Expand Down Expand Up @@ -757,6 +813,12 @@ public boolean validateData() {

}

if (getDataModel().getNoPrimers().get()) {

utilities.Configuration.getParameters().setProperty("AptaplexParser.BatchMode", true);

}

// Save to file
utilities.Configuration.writeConfiguration();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void init() {


/**
* Instaniates a new Experiment and starts AptaPLEX
* Instantiates a new Experiment and starts AptaPLEX
* @param event
*/
@FXML
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/gui/wizards/newexperiment/wizard1.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<BorderPane fx:id="rootBorderPane" prefHeight="620.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.wizards.newexperiment.Wizard1Controller">
<BorderPane fx:id="rootBorderPane" prefHeight="620.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.wizards.newexperiment.Wizard1Controller">
<bottom>
<fx:include fx:id="actionBar" source="actionBar.fxml" />
</bottom>
Expand Down Expand Up @@ -72,6 +72,11 @@
</TextField>
</children>
</HBox>
<HBox>
<children>
<CheckBox fx:id="noPrimersCheckbox" mnemonicParsing="false" text="Batch Mode (reads are assumed to contain only the randomized region)" />
</children>
</HBox>
<Label text="Randomized Region Size">
<font>
<Font name="System Bold" size="14.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void run() {

// now that we have the data, set any file backed implementations
// of StructurePool to read only. Do this only if the prediction has not
// been cancled by the user.
// been canceled by the user.
experiment.getStructurePool().setReadOnly();

closeButton.setDisable(false);
Expand Down
121 changes: 114 additions & 7 deletions src/main/java/lib/parser/aptaplex/AptaPlexConsumer.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@
* from the queue, processes them and adds them to the aptamer pool.
*/
public class AptaPlexConsumer implements Runnable {

/**
* True AptaPlex should run in batch mode. No primer or barcode checks will be performed
* and AptaPlex assumes the reads are preprocessed randomized regions only. In contrast to
* onlyRandomizedRegtionInData, batchMode will not add any primers back to these sequences.
*/
private boolean batchMode = Configuration.getParameters().getBoolean("AptaplexParser.BatchMode");

/**
* The queue to consume from
Expand Down Expand Up @@ -101,9 +108,9 @@ public class AptaPlexConsumer implements Runnable {
private byte[] primer5reverse = Configuration.getParameters().getString("Experiment.primer5").getBytes();

/**
* Access to the 5 prime primer
* Access to the 3 prime primer
*/
private byte[] primer3 = Configuration.getParameters().getString("Experiment.primer3").getBytes();
private byte[] primer3 = batchMode ? "".getBytes() : Configuration.getParameters().getString("Experiment.primer3").getBytes();

/**
* True if the sequences have previously been demultiplexed
Expand All @@ -112,6 +119,7 @@ public class AptaPlexConsumer implements Runnable {

/**
* True if the sequences have previously been stripped of their primers and barcodes
* Note that primers will be added again by AptaPlex
*/
private boolean onlyRandomizedRegionInData = Configuration.getParameters().getBoolean("AptaplexParser.OnlyRandomizedRegionInData");

Expand Down Expand Up @@ -275,15 +283,20 @@ public void run() {
contig = getContig(queueElement);

// ...check for undetermined nucleotides and fail if present...
if (!isValidSequence(contig)) {
// getContig returns null if invalid nucleotides are present.
if (contig == null) {
progress.totalInvalidContigs.incrementAndGet();
continue;
}


// Batch mode
if (this.batchMode) {
this.processBatchMode(contig);
}
// we need to differentiate between randomized region only mode,
// and fully parsable data
if (this.onlyRandomizedRegionInData) {
else if (this.onlyRandomizedRegionInData) {

this.processReadRandomizedRegionOnly(contig);

Expand Down Expand Up @@ -365,9 +378,6 @@ private byte[] getContig(Object queueElement) {
// keep taking elements from the queue
byte[] contig = null;

// Update the progress in a thread-safe manner
// progress.totalProcessedReads.incrementAndGet(); TODO: Move this to the end of run();

// process queueElement
read = (Read) queueElement;

Expand All @@ -394,6 +404,103 @@ private byte[] getContig(Object queueElement) {

}

/**
* Processes the current queueElement in batch mode. Assumes that the read consists only of the randomized region
* @param queueElement the element to be processed
* @return the error progress counter that was incremented in the last call of this function. null if success.
*/
private AtomicInteger processBatchMode(byte[] contig) {

// check if the contig conforms to the size restrictions
if (this.randomizedRegionSizeExactBound != null) {

if (contig.length != this.randomizedRegionSizeExactBound) {

progress.totalContigAssemblyFails.getAndIncrement();
return progress.totalContigAssemblyFails;

}

}

// otherwise we must have a bound
else {

if (this.randomizedRegionSizeLowerBound <= contig.length && this.randomizedRegionSizeUpperBound >= contig.length){

progress.totalContigAssemblyFails.getAndIncrement();
return progress.totalContigAssemblyFails;

}

}

// and add it to the selection cycle
if (!storeReverseComplement) { // Do we have to compute the reverse complement?

read.selection_cycle.addToSelectionCycle(
contig,
0,
contig.length
);

// Add metadata information
addAcceptedNucleotideDistributions(read.selection_cycle, contig, 0, contig.length);

} else { // We do!

// compute the complement...
for (int x = 0; x < contig.length; x++) {

switch (contig[x]) {
case 65:
contig[x] = 84;
break;

case 67:
contig[x] = 71;
break;

case 71:
contig[x] = 67;
break;

case 84:
contig[x] = 65;
break;
}
}

// ...and reverse it
for(int i = 0; i < contig.length / 2; i++)
{
byte temp = contig[i];
contig[i] = contig[contig.length - i - 1];
contig[contig.length - i - 1] = temp;
}

read.selection_cycle.addToSelectionCycle(
contig,
0,
contig.length
);

// Add metadata information
addAcceptedNucleotideDistributions(read.selection_cycle, contig, 0, contig.length);

}

// Store nucleotide distribution and quality score
addNuceotideDistributions();
addQualityScores();

progress.totalAcceptedReads.incrementAndGet();

// if we are here, all went well and we can go home.
return null;

}

/**
* Processes the current queueElement. Assumes that the read consists only of the randomized region
* @param queueElement the element to be processed
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/lib/parser/aptaplex/AptaPlexProducer.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public void run() {

reverse_export_path = Paths.get(exportPath.toString(), "undetermined_"+current_reverse_file_path.getFileName().toString() + (Configuration.getParameters().getBoolean("Export.compress") ? ".gz" : "" ));
ExportWriter reverse_export_writer = Configuration.getParameters().getBoolean("Export.compress") ? new CompressedExportWriter() : new UncompressedExportWriter();
reverse_export_writer.open(reverse_export_path);
reverse_export_writer.open(reverse_export_path);

}

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/utilities/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ public class Configuration {
// In addition, it is assumed that the data has already been demultiplexed.
defaults.put("AptaplexParser.UndeterminedToFile", false); // If set to true, AptaPlex will dump all read which failed processing for any reason to a fastq file located
// in the export folder of the project and named undetermined.fastq.gz
defaults.put("AptaplexParser.BatchMode", false); //If true, AptaPlex will parse all files without looking for primers and blindly
//accept every read that fits the specified size


// AptaSIM Options
defaults.put("Aptasim.HmmDegree", 2); // Degree of the Markov model
Expand Down

0 comments on commit 898b857

Please sign in to comment.