// take a sample of this liquid to be used to make the solution up to // a particular total volume func SampleForTotalVolume(l wtype.Liquid, v wunit.Volume) *wtype.LHComponent { ret := wtype.NewLHComponent() ret.CName = l.Name() ret.Tvol = v.RawValue() ret.Vunit = v.Unit().PrefixedSymbol() ret.LContainer = l.Container().(*wtype.LHWell) ret.CName = l.Name() ret.Extra = l.GetExtra() ret.Smax = l.GetSmax() ret.Visc = l.GetVisc() return ret }
// this is pretty dodgy... we will have to be quite careful here // the core problem is how to maintain a list of components and volumes // but respect the physical fact that we can't actually unmix things func (w *LHWell) Remove(v wunit.Volume) Physical { defer w.updateVolume() ret := w.WContents[0] if ret.Vol > v.SIValue() { ret.Vol = v.SIValue() w.WContents[0].Vol -= v.SIValue() } else { w.WContents = w.WContents[1:len(w.WContents)] } return ret }
func (lhc *LHComponent) Sample(v wunit.Volume) Liquid { // need to jig around with units a bit here // Should probably just make Vunit, Cunit etc. wunits anyway meas := wunit.ConcreteMeasurement{lhc.Vol, wunit.ParsePrefixedUnit(lhc.Vunit)} // we need some logic potentially if v.SIValue() > meas.SIValue() { wutil.Error(errors.New(fmt.Sprintf("LHComponent ID: %s Not enough volume for sample", lhc.ID))) } else if v.SIValue() == meas.SIValue() { return lhc } smp := CopyLHComponent(lhc) // need a convention here smp.Vol = v.RawValue() smp.Vunit = v.Unit().PrefixedSymbol() meas.Subtract(&v.ConcreteMeasurement) lhc.Vol = meas.RawValue() return smp }
func solution_setup(request *LHRequest, prms *liquidhandling.LHProperties) (map[string]*wtype.LHSolution, map[string]float64) { solutions := request.Output_solutions // index of components used to make up to a total volume, along with the required total mtvols := make(map[string][]float64, 10) // index of components with concentration targets, along with the target concentrations mconcs := make(map[string][]float64, 10) // keep a list of components which have fixed stock concentrations fixconcs := make([]*wtype.LHComponent, 0) // maximum solubilities of each component Smax := make(map[string]float64, 10) // maximum total volume of any solution containing each component hshTVol := make(map[string]float64) // find the minimum and maximum required concentrations // across all the solutions for _, solution := range solutions { components := solution.Components // we need to identify the concentration components // and the total volume components, if we have // concentrations but no tvols we have to return // an error arrCncs := make([]*wtype.LHComponent, 0, len(components)) arrTvol := make([]*wtype.LHComponent, 0, len(components)) cmpvol := 0.0 totalvol := 0.0 for _, component := range components { // what sort of component is it? conc := component.Conc tvol := component.Tvol if conc != 0.0 { arrCncs = append(arrCncs, component) } else if tvol != 0.0 { tv := component.Tvol if totalvol == 0.0 || totalvol == tv { totalvol = tv } else { // error wutil.Error(errors.New(fmt.Sprintf("Inconsistent total volumes %-6.4f and %-6.4f at component %s", totalvol, tv, component.Name))) } } else { cmpvol += component.Vol } } // add everything to the maps for _, cmp := range arrCncs { nm := cmp.CName cnc := cmp.Conc _, ok := Smax[nm] if !ok { Smax[nm] = cmp.Smax } if cmp.StockConcentration != 0.0 { fixconcs = append(fixconcs, cmp) continue } var cncslc []float64 cncslc, ok = mconcs[nm] if !ok { cncslc = make([]float64, 0, 10) } cncslc = append(cncslc, cnc) mconcs[nm] = cncslc _, ok = hshTVol[nm] if !ok || hshTVol[nm] > totalvol { hshTVol[nm] = totalvol } } // now the total volumes for _, cmp := range arrTvol { nm := cmp.CName tvol := cmp.Tvol var tvslc []float64 tvslc, ok := mtvols[nm] if !ok { tvslc = make([]float64, 0, 10) } tvslc = append(tvslc, tvol) mtvols[nm] = tvslc } } // end solutions // so now we should be able to make stock concentrations // first we need the min and max for each minrequired := make(map[string]float64, len(mconcs)) maxrequired := make(map[string]float64, len(mconcs)) //TODO this needs to be migrated elsewhere var vmin wunit.Volume = wunit.NewVolume(1.0, "ul") if prms.CurrConf != nil { vmin = *(prms.CurrConf.Minvol) } for cmp, arr := range mconcs { min := wutil.FMin(arr) max := wutil.FMax(arr) minrequired[cmp] = min maxrequired[cmp] = max // if smax undefined we need to deal - we assume infinite solubility!! _, ok := Smax[cmp] if !ok { Smax[cmp] = 9999999 wutil.Warn(fmt.Sprintf("Max solubility undefined for component %s -- assuming infinite solubility!", cmp)) } } stockconcs := choose_stock_concentrations(minrequired, maxrequired, Smax, vmin.RawValue(), hshTVol) // handle any errors here // add the fixed concentrations into stockconcs for _, cmp := range fixconcs { stockconcs[cmp.CName] = cmp.StockConcentration } // nearly there now! Need to turn all the components into volumes, then we're done // make an array for the new solutions newSolutions := make(map[string]*wtype.LHSolution, len(solutions)) for _, solution := range solutions { components := solution.Components arrCncs := make([]*wtype.LHComponent, 0, len(components)) arrTvol := make([]*wtype.LHComponent, 0, len(components)) arrSvol := make([]*wtype.LHComponent, 0, len(components)) cmpvol := 0.0 totalvol := 0.0 totalvolunit := "" for _, component := range components { // what sort of component is it? // what is the total volume ? if component.Conc != 0.0 { arrCncs = append(arrCncs, component) } else if component.Tvol != 0.0 { arrTvol = append(arrTvol, component) tv := component.Tvol totalvolunit = component.Vunit if totalvol == 0.0 || totalvol == tv { totalvol = tv } else { // error wutil.Error(errors.New(fmt.Sprintf("Inconsistent total volumes %-6.4f and %-6.4f at component %s", totalvol, tv, component.Name))) } } else { // need to add in the volume taken up by any volume components cmpvol += component.Vol arrSvol = append(arrSvol, component) } } // first we add the volumes to the concentration components arrFinalComponents := make([]*wtype.LHComponent, 0, len(components)) for _, component := range arrCncs { name := component.CName cnc := component.Conc vol := totalvol * cnc / stockconcs[name] cmpvol += vol component.Vol = vol component.Vunit = totalvolunit component.StockConcentration = stockconcs[name] arrFinalComponents = append(arrFinalComponents, component) } // next we get the final volume for total volume components for _, component := range arrTvol { vol := totalvol - cmpvol component.Vol = vol arrFinalComponents = append(arrFinalComponents, component) } // then we add the rest arrFinalComponents = append(arrFinalComponents, arrSvol...) // finally we replace the components in this solution solution.Components = arrFinalComponents // and put the new solution in the array newSolutions[solution.ID] = solution } return newSolutions, stockconcs }