Changeset 2025


Ignore:
Timestamp:
Sep 25, 2013, 2:29:03 PM (8 years ago)
Author:
Nicklas Nordborg
Message:

References #504: Allow target volume/library in a pool to be changed

This should now be implemented, but since #503 will change how the calculations are done again, the final fine-tuning will be made as part of that ticket instead.

Location:
extensions/net.sf.basedb.reggie/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • extensions/net.sf.basedb.reggie/trunk/resources/libprep/create_pools.jsp

    r1987 r2025  
    3333
    3434<script language="JavaScript">
    35 var debug = false;
     35var debug = 1;
    3636var currentStep = 1;
     37
     38var targetVolumePerLibIsValid = false;
    3739
    3840// Loaded from servlet when getting Library information
    3941var TARGET_MOLARITY_IN_POOL;
    40 var TARGET_VOLUME_IN_POOL_PER_LIB;
     42var LIMIT_FOR_EXTRA_LARGE_MIX;
     43var EXTRA_LARGE_MIX_FACTOR;
    4144
    4245function init()
     
    123126function goCreate()
    124127{
    125 
     128  if (!targetVolumePerLibIsValid) return;
     129 
    126130  var submitInfo = {};
    127131  submitInfo.pools = [];
    128132  submitInfo.flagged = [];
    129  
     133  var frm = document.forms['reggie'];
     134  submitInfo.targetVolumeInPoolPerLib = parseFloat(frm.target_volume.value);
     135   
    130136  var numPools = Plate.getPools();
    131137  for (var poolNo = 0; poolNo < numPools; poolNo++)
     
    220226  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
    221227
     228  // Load Libraries and related info from the selected bioplate
     229  var request = Ajax.getXmlHttpRequest();
     230  try
     231  {
     232    showLoadingAnimation('Loading information about libraries...');
     233    var url = '../Pool.servlet?ID=<%=ID%>&cmd=GetLibraryInfoForPlate&bioplate='+libPlate.id;   
     234    request.open("GET", url, false);
     235    request.send(null);
     236  }
     237  finally
     238  {
     239    hideLoadingAnimation();
     240  }
     241 
     242  if (debug) Main.debug(request.responseText);
     243  var response = JSON.parse(request.responseText);
     244  if (response.status != 'ok')
     245  {
     246    setFatalError(response.message);
     247    return false;
     248  }
     249
     250 
    222251  var schema = PoolSchema.getById(libPlate.poolSchema);
    223252  var barcodeVariant = PoolSchema.getBarcodeVariantByName(schema, libPlate.barcodeVariant);
    224253  Plate.setPoolSchema(schema);
    225254  WellPainter.barcodeVariant = barcodeVariant;
    226 
     255  Plate.init(8, 12, schema, WellPainter);
     256
     257  var poolInfo = response.poolInfo;
     258  TARGET_MOLARITY_IN_POOL = poolInfo.targetMolarity;
     259  EXTRA_LARGE_MIX_FACTOR = poolInfo.extraLargeMixFactor;
     260  LIMIT_FOR_EXTRA_LARGE_MIX = poolInfo.limitForExtraLargeMix;
     261 
    227262  setInnerHTML('pool_schema', schema.name);
    228263  setInnerHTML('barcode_variant', barcodeVariant.name);
    229  
    230   // Load Libraries and related info from the selected bioplate
    231   var request = Ajax.getXmlHttpRequest();
    232   try
    233   {
    234     showLoadingAnimation('Loading information about libraries...');
    235     var url = '../Pool.servlet?ID=<%=ID%>&cmd=GetLibraryInfoForPlate&bioplate='+libPlate.id;   
    236     request.open("GET", url, false);
    237     request.send(null);
    238   }
    239   finally
    240   {
    241     hideLoadingAnimation();
    242   }
    243  
    244   if (debug) Main.debug(request.responseText);
    245   var response = JSON.parse(request.responseText);
    246   if (response.status != 'ok')
    247   {
    248     setFatalError(response.message);
    249     return false;
    250   }
    251  
    252   var poolInfo = response.poolInfo;
    253   TARGET_MOLARITY_IN_POOL = poolInfo.targetMolarity;
    254   TARGET_VOLUME_IN_POOL_PER_LIB = poolInfo.targetVolumePerLib;
    255  
    256264  setInnerHTML('target_molarity', Numbers.formatNumber(TARGET_MOLARITY_IN_POOL, 1) + ' nM');
    257   setInnerHTML('target_volume', Numbers.formatNumber(TARGET_VOLUME_IN_POOL_PER_LIB, 1) + ' µl');
    258  
    259   // Pre-process the Library items
    260   var list = response.libraries;
    261   for (var i = 0; i < list.length; i++)
    262   {
    263     checkAndPreProcessLibrary(list[i], schema, barcodeVariant);
    264   }
    265  
    266   viewAsPlate(list, schema, barcodeVariant)
     265
     266  var libs = response.libraries;
     267  for (var i = 0; i < libs.length; i++)
     268  {
     269    var lib = libs[i];
     270    var well = lib.bioWell;
     271    lib.excludeFromPool = false;
     272    Plate.getWell(well.row, well.column).setExtract(lib);
     273  }
     274
     275  PoolSchema.buildPoolTableRow(schema, 12);
     276  setTargetVolumePerLib(poolInfo.targetVolumePerLib);
    267277}
    268278
     
    304314}
    305315
    306 
    307 function checkAndPreProcessLibrary(lib, schema, barcodeVariant)
     316function setTargetVolumePerLib(targetVolumePerLib)
     317{
     318  var frm = document.forms['reggie'];
     319  frm.target_volume.value = targetVolumePerLib;
     320 
     321  targetVolumePerLibIsValid = false;
     322  if (targetVolumePerLib < 2 || targetVolumePerLib > 5)
     323  {
     324    setInputStatus('target_volume', 'Must be between 2 and 5µl', 'invalid');
     325    return;
     326  }
     327  setInputStatus('target_volume', '', 'valid');
     328  targetVolumePerLibIsValid = true;
     329
     330  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
     331  var schema = PoolSchema.getById(libPlate.poolSchema);
     332  var barcodeVariant = PoolSchema.getBarcodeVariantByName(schema, libPlate.barcodeVariant);
     333 
     334  // Pre-process the Library items
     335  var wells = Plate.getWells();
     336  for (var i = 0; i < wells.length; i++)
     337  {
     338    var lib = wells[i].extract;
     339    if (lib)
     340    {
     341      calculateMixingVolumes(lib, targetVolumePerLib, schema, barcodeVariant);
     342    }
     343  }
     344 
     345  Plate.paint(Plate.getWells());
     346  updatePoolData();
     347}
     348
     349function calculateMixingVolumes(lib, targetVolumePerLib, schema, barcodeVariant)
    308350{
    309351  var well = lib.bioWell;
     
    322364  }
    323365 
    324   if (lib.volume)
    325   {
     366  if (lib.molarity)
     367  {
     368    if (lib.molarity < 1.0)
     369    {
     370      remarks[remarks.length] = 'Low molarity';
     371    }
     372
    326373    if (lib.speedVacConc != null)
    327374    {
    328375      remarks[remarks.length] = 'SpeedVac';
    329376    }
    330     if (lib.volume > TARGET_VOLUME_IN_POOL_PER_LIB + 0.001)  // +0.001 to allow for rounding errors
    331     {
    332       remarks[remarks.length] = 'Use '+TARGET_VOLUME_IN_POOL_PER_LIB+' µl';
     377   
     378    // Calculate mix volumes, but do not correct for negative volumes
     379    // due to low concentration (this will be notified in the protocols)
     380    // Round to 1 decimal since that is the expected accuracy of pipettes
     381    // Remember to use the rounded values in the calculations
     382    var usedVolumeForMix = Math.round(10 * targetVolumePerLib * TARGET_MOLARITY_IN_POOL / lib.molarity) / 10;
     383    var mixFactor = 1.0;
     384   
     385    if (usedVolumeForMix < LIMIT_FOR_EXTRA_LARGE_MIX)
     386    {
     387      // Extra large mix is required if the usedVolume is small due to
     388      // difficulties to pipette the exact amount for small volumes
     389      mixFactor = EXTRA_LARGE_MIX_FACTOR;
     390      // Re-calculate volume to get the rounding correct
     391      usedVolumeForMix = Math.round(10 * mixFactor * targetVolumePerLib * TARGET_MOLARITY_IN_POOL / lib.molarity) / 10 / mixFactor;
     392    }
     393   
     394    lib.volume = usedVolumeForMix;
     395    lib.eb = targetVolumePerLib - usedVolumeForMix;
     396    lib.mixMolarity = lib.molarity * usedVolumeForMix / targetVolumePerLib;
     397    lib.mixFactor = mixFactor;
     398   
     399    if (lib.volume > targetVolumePerLib + 0.001)  // +0.001 to allow for rounding errors
     400    {
     401      remarks[remarks.length] = 'Use ' + targetVolumePerLib +' µl';
    333402    }
    334403
     
    337406      // Larger mix than default
    338407      var mixedVolume = (lib.volume+lib.eb)*(lib.mixFactor);
    339       remarks[remarks.length] = 'Mix ' + Numbers.formatNumber(mixedVolume, 0) + 'µl';
    340     }
    341 
     408      remarks[remarks.length] = 'Mix ' + Numbers.formatNumber(mixedVolume, 1) + 'µl';
     409    }
     410   
    342411  }
    343412  lib.stratagene = lib.name.indexOf(GENERIC_STRATAGENE_NAME) == 0;
     
    346415}
    347416
    348 function viewAsPlate(list, schema, barcodeVariant)
    349 {
    350   Plate.init(8, 12, schema, WellPainter);
    351  
    352   for (var i = 0; i < list.length; i++)
    353   {
    354     var lib = list[i];
    355     var well = lib.bioWell;
    356     lib.excludeFromPool = false;
    357     Plate.getWell(well.row, well.column).setExtract(lib);
    358   }
    359 
    360   WellPainter.barcodeVariant = barcodeVariant;
    361   Plate.paint(Plate.getWells());
    362   PoolSchema.buildPoolTableRow(schema, 12);
    363   updatePoolData();
    364 }
    365417
    366418function updatePoolData()
    367419{
     420  var frm = document.forms['reggie'];
     421  targetVolumePerLib = parseFloat(frm.target_volume.value);
     422 
    368423  for (var poolNo = 0; poolNo < Plate.getPools(); poolNo++)
    369424  {
     
    379434      {
    380435        poolMolarity += Math.min(lib.molarity, lib.mixMolarity);
    381         poolVolume += TARGET_VOLUME_IN_POOL_PER_LIB;
     436        poolVolume += targetVolumePerLib;
    382437        numLibs++;
    383438      }
     
    393448}
    394449
     450function targetVolumeOnBlur()
     451{
     452  var frm = document.forms['reggie'];
     453  var targetVolumePerLib = parseFloat(frm.target_volume.value);
     454  setTargetVolumePerLib(targetVolumePerLib);
     455}
     456
    395457var WellPainter = function()
    396458{
     
    413475    {
    414476      cls += ' flagged';
     477    }
     478    if (lib && lib.molarity < 1.0)
     479    {
     480      cls += ' low-molarity';
    415481    }
    416482    return cls;
     
    434500        text += '<span class="volume">'+Numbers.formatNumber(lib.volume*mixFactor, 1) + 'µl</span>';
    435501        text += '<span class="eb">'+Numbers.formatNumber(lib.eb*mixFactor, 1)+'µl</span>';
    436         text += '<div class="remarks">'+ lib.remarks.join('; ') + '</div>';
     502        if (lib.remarks) text += '<div class="remarks">'+ lib.remarks.join('; ') + '</div>';
    437503      }
    438504      if (lib.comment)
     
    663729{
    664730  background-image: url('../images/flag.png');
     731  background-position: 98% 5%;
     732  background-repeat: no-repeat;
     733}
     734
     735.well.low-molarity
     736{
     737  background-image: url('../images/warning.png') !important;
    665738  background-position: 98% 5%;
    666739  background-repeat: no-repeat;
     
    719792        <td class="prompt"></td>
    720793        <td class="input"> - or -</td>
    721         <td class="status" id="bioplate.status"></td>
     794        <td class="status"></td>
    722795        <td class="help"></td>
    723796      </tr>
     
    749822          <span id="pool_schema"></span>
    750823        </td>
     824        <td class="status"></td>
    751825        <td class="help"></td>
    752826      </tr>
     
    756830          <span id="barcode_variant"></span>
    757831        </td>
     832        <td class="status"></td>
    758833        <td class="help"></td>
    759834      </tr>
     
    763838          <span id="target_molarity"></span>
    764839        </td>
     840        <td class="status"></td>
    765841        <td class="help"></td>
    766842      </tr>
    767843      <tr valign="top">
    768         <td class="prompt">Target volume / lib</td>
     844        <td class="prompt">Average volume / lib</td>
    769845        <td class="input">
     846          <input type="text" class="text" name="target_volume"
     847            value="5" style="width: 4em;"
     848            onblur="targetVolumeOnBlur()"
     849            onkeypress="return Numbers.numberOnly(event)"> µl
    770850          <span id="target_volume"></span>
    771851        </td>
    772         <td class="help"></td>
     852        <td class="status" id="target_volume.status"></td>
     853        <td class="help"><span id="target_volume.message" class="message" style="display: none;"></span>
     854          Select a target volume when mixing each lib to 2nM before pooling.
     855          The actual volume may be larger or lower due to low/high concentration.</td>
    773856      </tr>
    774857      </table>
  • extensions/net.sf.basedb.reggie/trunk/resources/libprep/pool_protocol2.jsp

    r2003 r2025  
    216216        // Larger mix than default
    217217        var mixedVolume = (lib.volume+lib.eb)*(lib.mixFactor);
    218         remarks[remarks.length] = 'Mix ' + Numbers.formatNumber(mixedVolume, 0) + 'µl';
     218        remarks[remarks.length] = 'Mix ' + Numbers.formatNumber(mixedVolume, 1) + 'µl - use ' + Numbers.formatNumber(lib.volume+lib.eb, 1) + 'µl in pool';
    219219      }
    220220
     
    291291        tbody.appendChild(tr);
    292292      }
    293      
    294       setInnerHTML('molarity.'+pool.id, Numbers.formatNumber(pool.molarity, 2) + ' nM; ' + Numbers.formatNumber(1000*pool.originalQuantity / pool.conc, 0)+' µl');
     293      var poolVol = 1000*pool.originalQuantity / pool.conc;
     294      var volPerLib = poolVol / pool.libraries.length;
     295      setInnerHTML('molarity.'+pool.id, Numbers.formatNumber(pool.molarity, 2) + ' nM; ' + Numbers.formatNumber(poolVol, 0)+' µl');
     296      setInnerHTML('volPerLib.'+pool.id, Numbers.formatNumber(volPerLib, 0) + '/' + Numbers.formatNumber(volPerLib*2, 0) + ' µl');
    295297      Main.show('pool.'+pool.id);
    296298    }
     
    642644          <th>DNA</th>
    643645          <th class="workplate">Work</th>
    644           <th colspan="2">2nM, 5/10µl</th>
     646          <th colspan="2">2nM, <span id="volPerLib.<%=poolId%>"></span></th>
    645647          <th></th>
    646648        </tr>
  • extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/servlet/PoolServlet.java

    r1987 r2025  
    5858
    5959  /**
    60     µl to use from each lib mixed to 2 nM in the pool
     60    Default volume in µl to use from each lib mixed to 2 nM in the pool
    6161  */
    62   private static final float TARGET_VOLUME_IN_POOL_PER_LIB = 5f;
     62  private static final float DEFAULT_TARGET_VOLUME_IN_POOL_PER_LIB = 5f;
    6363 
    6464  /**
     
    203203        {
    204204          lib.loadBioPlateLocation();
    205           addPoolingCalculationsToLib(dc, lib, null);
     205          addPoolingCalculationsToLib(dc, lib, null, libs.size());
    206206          lib.setAnnotation("comment", lib.getExtract().getDescription());
    207207          jsonLibs.add(lib.asJSONObject());
     
    214214        JSONObject poolInfo = new JSONObject();
    215215        poolInfo.put("targetMolarity", TARGET_MOLARITY_IN_POOL);
    216         poolInfo.put("targetVolumePerLib", TARGET_VOLUME_IN_POOL_PER_LIB);
     216        poolInfo.put("targetVolumePerLib", DEFAULT_TARGET_VOLUME_IN_POOL_PER_LIB);
     217        poolInfo.put("extraLargeMixFactor", EXTRA_LARGE_MIX_FACTOR);
     218        poolInfo.put("limitForExtraLargeMix", LIMIT_FOR_EXTRA_LARGE_MIX);
    217219        json.put("poolInfo", poolInfo);
    218220      }
     
    284286          {
    285287            lib.loadBioPlateLocation();
    286             addPoolingCalculationsToLib(dc, lib, pool);
     288            addPoolingCalculationsToLib(dc, lib, pool, libs.size());
    287289            lib.setAnnotation("comment", lib.getExtract().getDescription());
    288290            jsonLibs.add(lib.asJSONObject());
     
    299301        JSONObject poolInfo = new JSONObject();
    300302        poolInfo.put("targetMolarity", TARGET_MOLARITY_IN_POOL);
    301         poolInfo.put("targetVolumePerLib", TARGET_VOLUME_IN_POOL_PER_LIB);
     303        poolInfo.put("extraLargeMixFactor", EXTRA_LARGE_MIX_FACTOR);
     304        poolInfo.put("limitForExtraLargeMix", LIMIT_FOR_EXTRA_LARGE_MIX);
     305        poolInfo.put("targetVolumePerLib", DEFAULT_TARGET_VOLUME_IN_POOL_PER_LIB);
    302306        json.put("poolInfo", poolInfo);
    303307      }
     
    344348        JSONArray jsonPools = (JSONArray)jsonReq.get("pools");
    345349        JSONArray jsonFlagged = (JSONArray)jsonReq.get("flagged");
     350        Number targetVolumeInPoolPerLib = (Number)jsonReq.get("targetVolumeInPoolPerLib");
    346351       
    347352        dc = sc.newDbControl();
     
    384389            // Calculate resulting molarity given the used volumes
    385390            float molarityForPool = TARGET_MOLARITY_IN_POOL;
    386             if (usedVolumeForPool > TARGET_VOLUME_IN_POOL_PER_LIB)
     391            if (usedVolumeForPool > targetVolumeInPoolPerLib.floatValue())
    387392            {
    388393              // We can never use more than the target volume
    389               usedVolumeForPool = TARGET_VOLUME_IN_POOL_PER_LIB;
     394              usedVolumeForPool = targetVolumeInPoolPerLib.floatValue();
    390395              usedEbForPool = 0f;
    391396              molarityForPool = molarity;
     
    563568 
    564569 
    565   private void addPoolingCalculationsToLib(DbControl dc, Library lib, PooledLibrary pool)
     570  private void addPoolingCalculationsToLib(DbControl dc, Library lib, PooledLibrary pool, int numLibs)
    566571  {
    567572    // Load original and SpeedVac:ed concentrations
     
    587592      Float usedConc = speedVacConc == null ? originalConc : speedVacConc;
    588593      Float usedVolumeForMix = 1000 * usedQuantity / usedConc;
     594     
     595      Float poolConc = (Float)Annotationtype.POOL_CONC.getAnnotationValue(dc, pool.getExtract());
     596      Float poolQuantity = pool.getExtract().getOriginalQuantity();
     597      Float TARGET_VOLUME_IN_POOL_PER_LIB = 1000 * poolQuantity / poolConc / numLibs;
     598     
    589599      Float ebVolumeForMix = TARGET_VOLUME_IN_POOL_PER_LIB - usedVolumeForMix;
    590600      lib.setAnnotation("volume", usedVolumeForMix);
     
    608618      lib.setAnnotation("dils", result.size());
    609619    }
     620    /*
    610621    else
    611622    {
     
    614625      // Round to 1 decimal since that is the expected accuracy of pipettes
    615626      // Remember to use the rounded values in the calculations
    616       float usedVolumeForMix = Reggie.round(TARGET_VOLUME_IN_POOL_PER_LIB * TARGET_MOLARITY_IN_POOL / molarity, 1);
     627      float usedVolumeForMix = Reggie.round(targetVolumePerLib * TARGET_MOLARITY_IN_POOL / molarity, 1);
    617628      float mixFactor = 1.0f;
    618629      if (usedVolumeForMix < LIMIT_FOR_EXTRA_LARGE_MIX)
     
    622633        mixFactor = EXTRA_LARGE_MIX_FACTOR;
    623634        // Re-calculate volume to get the rounding correct
    624         usedVolumeForMix = Reggie.round(mixFactor * TARGET_VOLUME_IN_POOL_PER_LIB * TARGET_MOLARITY_IN_POOL / molarity, 1) / mixFactor;
     635        usedVolumeForMix = Reggie.round(mixFactor * targetVolumePerLib * TARGET_MOLARITY_IN_POOL / molarity, 1) / mixFactor;
    625636      }
    626637     
    627       float ebVolumeForMix = TARGET_VOLUME_IN_POOL_PER_LIB - usedVolumeForMix;
     638      float ebVolumeForMix = targetVolumePerLib - usedVolumeForMix;
    628639      lib.setAnnotation("volume", usedVolumeForMix);
    629640      lib.setAnnotation("eb", ebVolumeForMix);
    630       lib.setAnnotation("mixMolarity", molarity * usedVolumeForMix / TARGET_VOLUME_IN_POOL_PER_LIB);
     641      lib.setAnnotation("mixMolarity", molarity * usedVolumeForMix / targetVolumePerLib);
    631642      lib.setAnnotation("mixFactor", mixFactor);
    632643    }
     644    */
    633645  }
    634646 
Note: See TracChangeset for help on using the changeset viewer.