001package com.hfg.chem.solution; 002 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.List; 008 009import com.hfg.chem.Matter; 010import com.hfg.units.*; 011 012//------------------------------------------------------------------------------ 013/** 014 Represents an aqueous solution. 015 <div> 016 @author J. Alex Taylor, hairyfatguy.com 017 </div> 018 */ 019//------------------------------------------------------------------------------ 020// com.hfg XML/HTML Coding Library 021// 022// This library is free software; you can redistribute it and/or 023// modify it under the terms of the GNU Lesser General Public 024// License as published by the Free Software Foundation; either 025// version 2.1 of the License, or (at your option) any later version. 026// 027// This library is distributed in the hope that it will be useful, 028// but WITHOUT ANY WARRANTY; without even the implied warranty of 029// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 030// Lesser General Public License for more details. 031// 032// You should have received a copy of the GNU Lesser General Public 033// License along with this library; if not, write to the Free Software 034// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 035// 036// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 037// jataylor@hairyfatguy.com 038//------------------------------------------------------------------------------ 039 040public class AqueousSolution 041{ 042 // TODO: Should probably be a Set 043 private List<MixtureComponent> mComponents = new ArrayList<>(5); 044 private Quantity mTargetQuantity; 045 private Quantity mActualQuantity; 046 047 // We don't want the auto-scaling to come back with wacky units like cL 048 // so we'll restrict the list of possible scaling factors. 049 private static SI_ScalingFactor[] sStandardMassScalingFactors; 050 private static SI_ScalingFactor[] sStandardVolumeScalingFactors; 051 static 052 { 053 sStandardMassScalingFactors = new SI_ScalingFactor[] { 054 SI_ScalingFactor.kilo, 055 SI_ScalingFactor.one, 056 SI_ScalingFactor.milli, 057 SI_ScalingFactor.micro, 058 SI_ScalingFactor.nano 059 }; 060 061 sStandardVolumeScalingFactors = new SI_ScalingFactor[] { 062 SI_ScalingFactor.one, 063 SI_ScalingFactor.milli, 064 SI_ScalingFactor.micro, 065 SI_ScalingFactor.nano 066 }; 067 } 068 069 //########################################################################### 070 // CONSTRUCTORS 071 //########################################################################### 072 073 //--------------------------------------------------------------------------- 074 public AqueousSolution() 075 { 076 077 } 078 079 //########################################################################### 080 // PUBLIC METHODS 081 //########################################################################### 082 083 //--------------------------------------------------------------------------- 084 /** 085 The target quantity is the amount of solution intended to be made. 086 The target quantity should be specified either in terms of volume or in terms of mass. 087 * @param inValue the target quantity of this aqueous solution 088 * @return this solution object to enable method chaining 089 */ 090 public AqueousSolution setTargetQuantity(Quantity inValue) 091 { 092 mTargetQuantity = inValue; 093 return this; 094 } 095 096 //--------------------------------------------------------------------------- 097 public Quantity getTargetQuantity() 098 { 099 return mTargetQuantity; 100 } 101 102 //--------------------------------------------------------------------------- 103 /** 104 The actual quantity should be specified either in terms of volume or in terms of mass. 105 * @param inValue the actual quantity of this aqueous solution 106 * @return this solution object to enable method chaining 107 */ 108 public AqueousSolution setActualQuantity(Quantity inValue) 109 { 110 mActualQuantity = inValue; 111 return this; 112 } 113 114 //--------------------------------------------------------------------------- 115 public Quantity getActualQuantity() 116 { 117 return mActualQuantity; 118 } 119 120 //--------------------------------------------------------------------------- 121 public AqueousSolution subtract(Quantity inValue) 122 { 123 mActualQuantity = mActualQuantity.subtract(inValue); 124 return this; 125 } 126 127 //--------------------------------------------------------------------------- 128 public AqueousSolution defineComponent(Matter inSubstance, Quantity inConcentration) 129 { 130 mComponents.add(new MixtureComponent(inSubstance, inConcentration)); 131 return this; 132 } 133 134 //--------------------------------------------------------------------------- 135 public AqueousSolution defineComponent(MixtureComponent inComponent) 136 { 137 mComponents.add(inComponent); 138 return this; 139 } 140 141 //--------------------------------------------------------------------------- 142 public Collection<MixtureComponent> getComponents() 143 { 144 return Collections.unmodifiableCollection(mComponents); 145 } 146 147 //--------------------------------------------------------------------------- 148 public Quantity getQuantity(Matter inMatter) 149 { 150 Quantity quantity = null; 151 for (MixtureComponent component : mComponents) 152 { 153 if (component.getSubstance().equals(inMatter)) 154 { 155 quantity = component.getQuantity(); 156 break; 157 } 158 } 159 160 return quantity; 161 } 162 163 //--------------------------------------------------------------------------- 164 public Quantity calcQuantityOfStockSolutionNeeded(AqueousSolution inStockSolution) 165 { 166 Quantity stockQuantityNeeded = null; 167 168 Quantity componentTargetQuantity = null; 169 Matter stockComponent = inStockSolution.getComponents().iterator().next().getSubstance(); 170 171 for (MixtureComponent component : mComponents) 172 { 173 if (component.getSubstance().equals(stockComponent)) 174 { 175 componentTargetQuantity = component.getQuantity(); 176 break; 177 } 178 } 179 180 Quantity stockComponentQuantity = inStockSolution.getQuantity(stockComponent); 181 182 Quantity componentQuantityNeeded = new Quantity(componentTargetQuantity.doubleValue(), componentTargetQuantity.getUnit().getNumerator()); 183 184 Quantity componentDenominatorQuantity = new Quantity(1, componentTargetQuantity.getUnit().getDenominator()); 185 double scalingFactor = componentDenominatorQuantity.convertTo(getTargetQuantity().getUnit()).doubleValue() * getTargetQuantity().doubleValue(); 186 187 componentQuantityNeeded = componentQuantityNeeded.multiplyBy(scalingFactor); 188 189 if (componentQuantityNeeded.getUnit().getQuantityType().equals(QuantityType.AMOUNT_OF_SUBSTANCE)) 190 { 191 Unit stockSolutionNumerator = inStockSolution.getQuantity(stockComponent).getUnit().getNumerator(); 192 if (stockSolutionNumerator.getQuantityType().equals(QuantityType.AMOUNT_OF_SUBSTANCE)) 193 { 194 componentQuantityNeeded = componentQuantityNeeded.convertTo(AmountOfSubstanceUnit.mole); 195 componentQuantityNeeded = componentQuantityNeeded.divideBy(inStockSolution.getQuantity(stockComponent).doubleValue()); 196 197 stockQuantityNeeded = new Quantity(componentQuantityNeeded.doubleValue(), inStockSolution.getQuantity(stockComponent).getUnit().getDenominator()); 198 } 199 else if (stockSolutionNumerator.getQuantityType().equals(QuantityType.MASS)) 200 { 201 componentQuantityNeeded = componentQuantityNeeded.convertTo(AmountOfSubstanceUnit.mole); 202 // Convert from moles to grams using the molecular weight 203 componentQuantityNeeded = new Quantity(componentQuantityNeeded.multiplyBy(stockComponent.getAverageMass()).doubleValue(), MassUnit.gram); 204 205 stockQuantityNeeded = new Quantity(componentQuantityNeeded.divideBy(stockComponentQuantity.doubleValue()).doubleValue(), stockComponentQuantity.getUnit().getDenominator()); 206 } 207 } 208 else if (componentQuantityNeeded.getUnit().getQuantityType().equals(QuantityType.MASS)) 209 { 210 Unit stockSolutionNumerator = inStockSolution.getQuantity(stockComponent).getUnit().getNumerator(); 211 if (stockSolutionNumerator.getQuantityType().equals(QuantityType.AMOUNT_OF_SUBSTANCE)) 212 { 213 componentQuantityNeeded = componentQuantityNeeded.convertTo(MassUnit.gram); 214 // Convert from grams to moles using the molecular weight 215 componentQuantityNeeded = new Quantity(componentQuantityNeeded.divideBy(stockComponent.getAverageMass()).doubleValue(), AmountOfSubstanceUnit.mole); 216 217 stockQuantityNeeded = new Quantity(componentQuantityNeeded.divideBy(stockComponentQuantity.doubleValue()).doubleValue(), stockComponentQuantity.getUnit().getDenominator()); 218 } 219 else if (stockSolutionNumerator.getQuantityType().equals(QuantityType.MASS)) 220 { 221 componentQuantityNeeded = componentQuantityNeeded.convertTo(stockSolutionNumerator); 222 componentQuantityNeeded = componentQuantityNeeded.divideBy(inStockSolution.getQuantity(stockComponent).doubleValue()); 223 224 stockQuantityNeeded = new Quantity(componentQuantityNeeded.doubleValue(), inStockSolution.getQuantity(stockComponent).getUnit().getDenominator()); 225 } 226 } 227 else if (componentQuantityNeeded.getUnit().getQuantityType().equals(QuantityType.VOLUME)) 228 { 229 Unit stockSolutionNumerator = inStockSolution.getQuantity(stockComponent).getUnit().getNumerator(); 230 if (stockSolutionNumerator.getQuantityType().equals(QuantityType.AMOUNT_OF_SUBSTANCE)) 231 { 232 componentQuantityNeeded = componentQuantityNeeded.convertTo(MassUnit.gram); 233 // Convert from grams to moles using the molecular weight 234 componentQuantityNeeded = new Quantity(componentQuantityNeeded.divideBy(stockComponent.getAverageMass()).doubleValue(), AmountOfSubstanceUnit.mole); 235 236 stockQuantityNeeded = new Quantity(componentQuantityNeeded.divideBy(stockComponentQuantity.doubleValue()).doubleValue(), stockComponentQuantity.getUnit().getDenominator()); 237 } 238 else if (stockSolutionNumerator.getQuantityType().equals(QuantityType.MASS)) 239 { 240 componentQuantityNeeded = componentQuantityNeeded.convertTo(stockSolutionNumerator); 241 componentQuantityNeeded = componentQuantityNeeded.divideBy(inStockSolution.getQuantity(stockComponent).doubleValue()); 242 243 stockQuantityNeeded = new Quantity(componentQuantityNeeded.doubleValue(), inStockSolution.getQuantity(stockComponent).getUnit().getDenominator()); 244 } 245 else if (stockSolutionNumerator.getQuantityType().equals(QuantityType.VOLUME)) 246 { 247 componentQuantityNeeded = componentQuantityNeeded.convertTo(stockSolutionNumerator); 248 componentQuantityNeeded = componentQuantityNeeded.divideBy(inStockSolution.getQuantity(stockComponent).doubleValue()); 249 250 stockQuantityNeeded = new Quantity(componentQuantityNeeded.doubleValue(), inStockSolution.getQuantity(stockComponent).getUnit().getDenominator()); 251 } 252 } 253 254 SI_ScalingFactor[] allowedAutoScalingFactors = null; 255 if (stockQuantityNeeded.getUnit().getQuantityType().equals(QuantityType.VOLUME)) 256 { 257 allowedAutoScalingFactors = sStandardVolumeScalingFactors; 258 } 259 else if (stockQuantityNeeded.getUnit().getQuantityType().equals(QuantityType.MASS)) 260 { 261 allowedAutoScalingFactors = sStandardMassScalingFactors; 262 } 263 264 265 stockQuantityNeeded = stockQuantityNeeded.autoScale(allowedAutoScalingFactors); 266 267 return stockQuantityNeeded; 268 } 269 270 //--------------------------------------------------------------------------- 271 public Quantity calcMassOfComponentNeeded(Matter inComponent) 272 { 273 Quantity componentTargetQuantity = null; 274 275 for (MixtureComponent component : mComponents) 276 { 277 if (component.getSubstance().equals(inComponent)) 278 { 279 componentTargetQuantity = component.getQuantity(); 280 break; 281 } 282 } 283 284 285 Quantity componentQuantityNeeded = new Quantity(componentTargetQuantity.doubleValue(), componentTargetQuantity.getUnit().getNumerator()); 286 287 Quantity componentDenominatorQuantity = new Quantity(1, componentTargetQuantity.getUnit().getDenominator()); 288 double scalingFactor = componentDenominatorQuantity.convertTo(getTargetQuantity().getUnit()).doubleValue() * getTargetQuantity().doubleValue(); 289 290 componentQuantityNeeded = componentQuantityNeeded.multiplyBy(scalingFactor); 291 292 if (componentQuantityNeeded.getUnit().getQuantityType().equals(QuantityType.AMOUNT_OF_SUBSTANCE)) 293 { 294 componentQuantityNeeded = componentQuantityNeeded.convertTo(AmountOfSubstanceUnit.mole); 295 // Convert from moles to grams using the molecular weight 296 componentQuantityNeeded = new Quantity(componentQuantityNeeded.multiplyBy(inComponent.getAverageMass()).doubleValue(), MassUnit.gram); 297 } 298 299 componentQuantityNeeded = componentQuantityNeeded.autoScale(sStandardMassScalingFactors); 300 301 return componentQuantityNeeded; 302 } 303}