Message Boards Message Boards

GROUPS:

What is best practice when assigning quantities of chemicals

Posted 6 years ago
6994 Views
|
4 Replies
|
3 Total Likes
|

I am modelling water quality and wish to assign concentrations of certain contaminants in water. In Mathematica 10 I can use:

mecoprop = Quantity[200, "ug/l"]
simazine = Quantity[100, "ug/l"]

but this is somewhat dangerous, as it allows me to treat all contaminants as equal, and I can then calculate the non-sensical:

In:=     mecoprop - simazine
Out=     Quantity[100, ("Micrograms")/("Liters")]

I would like to be able to use something like:

mecoprop = Quantity[200, "ug mecoprop /l"]
simazine = Quantity[100, "ug simazine /l"]

because this would force dimensional consistency - I couldn't subtract 100 ug/l simazine from 200 ug/l mecoprop. Like you can't take 3 oranges away from 5 apples... Apologies if there is an obvious answer, this is my second day with Mathematica...

4 Replies

One way to do this would be to use IndependentUnit. This can allow you define your own units without interfering with Quanity units.

mecoprop = Quantity[200, IndependentUnit["mecoprop"] "Micrograms"/"Liters"] simazine = Quantity[100, IndependentUnit["Simazine"] "Micrograms"/"Liters"]

Using this method will get you the behavior you desire but there is a draw back that will have to specify the exact string names for the regular units as well as separate them in to different strings. For example you must put "Micrograms" / "Liters", instead of simple abbreviations with the divide operator in the string like "ug / l".

As far as I know, there is no way to get Quantity to accept custom units to were you can just put Quantity[100, "ug simazine /l"]. This would definitely be more elegant but probably not possible. Anyway, I hope this helps.

Thank you Wesley, this seems to do exactly what I want - but it seems rather long winded, so I tried to define it in a function:

concSetUG[substance_, conc_] := substance = Quantity[conc, IndependentUnit[substance] "Micrograms"/"Liters"]

with the intention that I could simply use:

concSetUG["symazine", 100]

If I delete "substance =" from this it works fine (giving the output 150 ug symazine/L) but including the assignment I get "Set::setraw: "Cannot assign to raw object \!(\"symazine\").""

I also thought that a function to increase or decrease the concentration would be useful, however

concAddUG[substance_, conc_] := substance + Quantity[conc, IndependentUnit[substance] "Micrograms"/"Liters"]

doesn't seem to work either... If I define mecoprop as 150 ug mecoprop/L, then

concAddUG["mecoprop", 100]

I get "mecoprop + 100 ug mecoprop/L"

I am assuming from what you have done, you are attempting to make a function that takes two arguments (substance name and amount) and defines a variable with that name and assigns it a value of the concentration with the appropriate unit.

The issue with you first function is the assignment statement (substance = something). There is nothing wrong with the function it self which why there are no errors when you defined it. However when you used the function like concSetUG["symazine",100] it creates the appropriate unit quantity, but it then tries to the assign that quantity to the string "symazine." This is what is causing the error message you see: "cannot assign to raw data object".

The raw data object in this case is the string "symazine" which can't be assigned a value. In Mathematica assignment statements consists of assigning expressions to symbols, such as (a = 5) , but ("a" = 5) would not work.

Creating functions in Mathematica that can create variables is very tricky to implement but here is something that you could try.

concSetUG[substance_, conc_] := (Clear[substance]; substance =  Quantity[conc, IndependentUnit[ToString[substance]] "Micrograms"/"Liters"])
concAddUG[substance_ , conc_] := substance +  Quantity[conc, IndependentUnit[ToString[HoldForm[substance]]] "Micrograms"/
 "Liters"]
SetAttributes[{concSetUG, concAddUG}, HoldFirst]

The first function takes in the symbol name for the substance and clears the symbols of its previous value then assigns it's new value. To get the string necessary for IndependentUnit I just use ToString[] on the symbol.

One thing to note in my implementation is that you need to pass the symbol name into the function not the string. This is because I was to lazy to figure out how to get it to work with a string argument.

concSetUG[symazine, 100]

This will make a variable symazine the with the value Quantity[100, ("Micrograms" IndependentUnit["symazine"])/("Liters")]

Looking at the modified add function.

concAddUG[symazine,100]

This gives the value Quantity[200, ("Micrograms" IndependentUnit["symazine"])/("Liters")]

The last line 'SetAttributes{concSetUG, concAddUG}, HoldFirst]' is what allows these functions to work. It makes these functions pass by reference rather than pass by value. Without these statements the argument substance would be immediately evaluated to its value, which means you couldn't clear the symbol or take its string. If you want to learn more about how this works [here is a good place to start.

If I hope this is what you are looking for. If you have more questions I'll try to help if I can.

Thank you again for this, just one little correction to my original code, it is necessary to re-allocate the substance variable in the concAddUG function if you want it to permanently add your new quantity, as follows:

concAddUG[substance_, conc_] := 
 substance = substance + Quantity[conc,IndependentUnit[ToString[HoldForm[substance]]]"Micrograms"/"Liters"]
SetAttributes[{concSetUG, concAddUG}, HoldFirst]

The original gave the sum of the original quantity and the added quantity, but didn't actually amend the original quantity.

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract