// TASK: define output plates // INPUT: "output_platetype", "outputs" //OUTPUT: "output_plates" -- these each have components in wells // "output_assignments" -- map with arrays of assignment strings, i.e. {tea: [plate1:A:1, plate1:A:2...] }etc. func output_plate_setup(request *LHRequest) *LHRequest { //(map[string]*wtype.LHPlate, map[string][]string) { output_platetype := (*request).Output_platetype if output_platetype == nil || output_platetype.ID == "" { wutil.Error(errors.New("plate_setup: No output plate type defined")) } if (*request).Output_major_group_layouts == nil { wutil.Error(errors.New("plate setup: Output major groups undefined")) } output_plates := (*request).Output_plates if len(output_plates) == 0 { output_plates = make(map[string]*wtype.LHPlate, len(request.Output_major_group_layouts)) } // just assign based on number of groups opl := request.Output_plate_layout for i := 0; i < len(request.Output_major_group_layouts); i++ { //p := wtype.New_Plate(request.Output_platetype) p := factory.GetPlateByType(request.Output_platetype.Type) output_plates[p.ID] = p opl[i] = p.ID name := fmt.Sprintf("Output_plate_%d", i+1) p.PlateName = name } (*request).Output_plate_layout = opl (*request).Output_plates = output_plates return request }
func (w *LHWell) Add(p Physical) { switch t := p.(type) { default: wutil.Error(errors.New(fmt.Sprintf("LHWell: Cannot add type %T", t))) case *LHSolution: // do something case *LHComponent: w.WContents = append(w.WContents, p.(*LHComponent)) w.Currvol += p.(*LHComponent).Vol p.(*LHComponent).LContainer = w } }
// TASK: Determine number of tip boxes of each type // INPUT: instructions //OUTPUT: arrays of tip boxes func (lh *Liquidhandler) Tip_box_setup(request *LHRequest) *LHRequest { tip_box_type := (*request).Tip_Type if tip_box_type == nil || tip_box_type.ID == "" { wutil.Error(errors.New("tip_box_setup: No tip_box type defined")) } tip_boxes := (*request).Tips if len(tip_boxes) == 0 { tip_boxes = make([]*wtype.LHTipbox, 0) } // the instructions are generated at this point so we just need to go through and count the tips used // of each type instrx := request.Instructions ntips := make(map[string]int) for _, ins := range instrx { if ins.InstructionType() == lhdriver.LOD { ttype := ins.GetParameter("TIPTYPE").([]string)[0] ntips[ttype] += ins.GetParameter("MULTI").(int) } } for tiptype, ntip := range ntips { // need to make sure the names match up here tbt := factory.GetTipByType(tiptype) ntbx := ntip/tbt.NTips + 1 for i := 0; i < ntbx; i++ { tbt2 := factory.GetTipByType(tiptype) tip_boxes = append(tip_boxes, tbt2) } } (*request).Tips = tip_boxes // need to fix the tip situation in the properties structure lh.Properties.RemoveTipBoxes() for _, tb := range tip_boxes { lh.Properties.AddTipBox(tb) } return request }
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 AdvancedExecutionPlanner(request *LHRequest, parameters *liquidhandling.LHProperties) *LHRequest { // in the first instance we assume this is done component-wise // we also need to identify dependencies, i.e. if certain components // are only available after other actions // this will only work if the components to be added are to go in the same order // get the layout groups minorlayoutgroups := request.Output_minor_group_layouts ass := request.Output_assignments inass := request.Input_assignments output_solutions := request.Output_solutions input_plates := request.Input_plates output_plate_layout := request.Output_plate_layout plate_lookup := request.Plate_lookup // highly inelegant... we should swap Tip Box Setup // around to take place after, then this whole thing // is a non-issue tt := make([]*wtype.LHTip, 1) tt[0] = request.Tip_Type.Tiptype parameters.Tips = tt instructions := liquidhandling.NewRobotInstructionSet(nil) // need to deal with solutions order := request.Input_order for _, name := range order { //fmt.Println(name) // cmpa holds a list of inputs required per destination // i.e. if destination X requires 15 ul of h2o this will be listed separately // at this point all of the requests must be for volumes, // we need a mapping from these to where they belong // do we? /* cmpa:=value.([]map[string]interface{}) cmp_map:=make(map[string]interface{}, len(cmpa)) for _,cmp:=range cmpa{ srcid:=cmp.Srcid cmp_map[srcid]=cmp } */ for n, g := range minorlayoutgroups { grp := []string(g) // get the group assignment string assignment := ass[n] // the assignment has the format plateID:row:column:incrow:inccol // where inc defines how the next one is to be calculated // e.g. {GUID}:A:1:1:0 // {GUID}:A:1:0:1 asstx := strings.Split(assignment, ":") plate := asstx[0] toplatenum := wutil.ParseInt(plate) row := wutil.AlphaToNum(asstx[1]) col := wutil.ParseInt(asstx[2]) incrow := wutil.ParseInt(asstx[3]) inccol := wutil.ParseInt(asstx[4]) whats := make([]string, len(grp)) pltfrom := make([]string, len(grp)) pltto := make([]string, len(grp)) plttypefrom := make([]string, len(grp)) plttypeto := make([]string, len(grp)) wellfrom := make([]string, len(grp)) wellto := make([]string, len(grp)) vols := make([]*wunit.Volume, len(grp)) fvols := make([]*wunit.Volume, len(grp)) tvols := make([]*wunit.Volume, len(grp)) for i, solID := range grp { sol := output_solutions[solID] // we need to get the relevant component out smpl := get_aggregate_component(sol, name) // we need to know where this component was assigned to inassignmentar := []string(inass[name]) inassignment, ok := get_assignment(inassignmentar, &input_plates, smpl.Vol) if !ok { wutil.Error(errors.New(fmt.Sprintf("No input assignment for %s with vol %-4.1f", name, smpl.Vol))) } inasstx := strings.Split(inassignment, ":") inplt := inasstx[0] inrow := string(inasstx[1]) incol := wutil.ParseInt(inasstx[2]) // we can fill the structure now whats[i] = name pltfrom[i] = plate_lookup[string(inplt)] pltto[i] = plate_lookup[output_plate_layout[toplatenum]] wellfrom[i] = inrow + strconv.Itoa(incol) wellto[i] = wutil.NumToAlpha(row) + strconv.Itoa(col) v := wunit.NewVolume(smpl.Vol, smpl.Vunit) v2 := wunit.NewVolume(0.0, "ul") vols[i] = &v // TODO Get the proper volumes here fvols[i] = &v2 tvols[i] = &v2 row += incrow col += inccol } ins := liquidhandling.NewTransferInstruction(whats, pltfrom, pltto, wellfrom, wellto, plttypefrom, plttypeto, vols, fvols, tvols /*, parameters.Cnfvol*/) instructions.Add(ins) } } inx := instructions.Generate(request.Policies, parameters) instrx := make([]liquidhandling.TerminalRobotInstruction, len(inx)) for i := 0; i < len(inx); i++ { instrx[i] = inx[i].(liquidhandling.TerminalRobotInstruction) } request.Instructions = instrx return request }
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 }