More Power (arr, arr, argh)

Tim “the tool man” approves.

“More models”.. I get this request often, and my usual answer is, in as nice a way as possible, do the work yourself lazy ass. I gave you the tools, learn how to use them.

But most people don’t get it. This sh*t isn’t carved into stone tablets by the hand of God. The way pharmacists are taught these days everything has to be in writing somewhere. So, here it is: Wagner: Fundamentals of Pharmacokinetics, nineteen-f**king-SEVENTY-FIVE. Yup, that’s right, almost FORTY f**king years ago. You weren’t even a gleam in your daddy’s eye. So, look it up old school in an actual lie-bary (sic) because it ain’t on the f**king inter-webs.

I’ve described the Wagner method before, in great detail here . No lie-bary required!

And now I’ve done all that work for you too! All it takes is a suck-up email to appeal to my altruism: “I like your program a lot because it is serious about PK/PD and pushes the concept of PK target attainment. As such, to optimize dosing with Abx is critical and apps to me are the future. I request that you build out the PK ability to dose other common beta lactams as well as the aztreonam and ceftazidime. I realize this can be done by entering the necessary parameters in the customizable part of the app but it would be easier for us if this was built in.”

Well, here it is.

I’ve also built the functionality to download these models into the Antibiotic Kinetics for Windows program.

AbPK how to
More Models!

Eventually this functionality will be added to the iOS and Android versions.

Group sing-along:

APK Eureka

Since adding EurekaLog to APK I’ve been able to track down the causes of some perplexing errors.


“X is not a valid floating point value”, this error has puzzled me for years. I have actually never seen it myself in 15+ years of using the program. I am only aware of it because of scattered reports from users.

Once nice feature of the EurekaLog report is a screenshot of the user’s current screen.

As usual, it is the user causing the error. Instead of entering a valid decimal value, ie, [1.02], they accidentally add a decimal point or two: [1.02.] or [1..02], etc. This error has cropped up twice in only 30 reports. I never would have imagined that someone would do something so fundamentally wrong. I also believe that it is impossible to imagine every possible scenario of software usage. I think it would take a troop of monkeys randomly pounding on multiple keyboards to approximate the myriad of potential mistakes.

Regardless, any software program must be able to prevent a user’s dumb mistake from causing a fatal error. All of the input boxes for numeric values in APK are coupled to procedures to prevent non-numeric input:
procedure TfrmMain.EditRecentSCrKeyPress(Sender: TObject; var Key: Char);
if not (Key in ['0'..'9', DecimalSep, #8]) then
Key := #0;

However, that’s not enough. I was under the impression that the StrToFloat function was fault tolerant, i.e., if the input was wrong, it would output a zero.
RecentSCr :=0;
if EditRecentSCr.Text <> '' then
RecentSCr := StrToFloat(EditRecentSCr.Text);

Unfortunately that’s not true. Instead, execution is halted with a fatal error and the program crashes.

The little known expression SysUtils.TryStrToFloat does have fault tolerance.
RecentSCr :=0;
if EditRecentSCr.Text <> '' then
TryStrToFloat(EditRecentSCr.Text, RecentSCr);
if RecentSCr <= 0 then begin ValidPatData := False; MessageDlg('Data entry error', 'Serum creatinine is a required field.', mtError, [mbOK], 100, dckActiveForm); EditRecentSCr.SetFocus; Exit; end;

Alternatively one can test the boolean result of TryStrToFloat within an if..then block to flag an error:
if TryStrToFloat(EditKel.Text, KelSD) = False then
IsOkay := False;
ErrMsg := 'Kel SD';

So, I've spent the better part of 3 days re-coding all the StrToFloat calls to TryStrToFloat (also StrToInt -> TryStrToInt). And I've been able to prevent fatal errors.

However, if the field is linked to a database, the type of error appears to be unavoidable. Although it's not a fatal error, it still generates a EurekaLog error dialog.

I feel that the EurekaLog dialog doesn't really fit this situation. I'd rather catch the error, display a dialog, and rollback the edit which produced the error. But I've hit a brick wall, unable to get to the event triggering the error. I've tried BeforePost, PostError, BeforeEdit, and EditError, none of these events are trapped when a db field is edited. In the case of database fields it appears that no event is triggered. WTF!?


Another error that EurekaLog discovered is "unable to save settings". The most fundamental requirement for use of this program is to have full read and write privileges to the APK folder. Without that, you can't really use the software. Again, regardless, any software program should not allow a user's dumb mistake to cause a fatal error. So, I combed through all code, and surrounded all ini writes in try..try..finally..except blocks:
UserIni := TIniFile.Create(AppDataPath + 'screen.ini');
UserIni.WriteInteger('Window Placement', 'Main.Top', frmMain.Top);
UserIni.WriteInteger('Window Placement', 'Main.Left', frmMain.Left);
on E:Exception do
MessageDlg('Error saving window placement',
mtError, [mbOK], 690, dckActiveForm);
Action := caFree;


The other common error that I've addressed in this update is "range check error". Unfortunately there are not enough reports to establish a pattern, so I simply switched off range error checking. Within the compiler settings, range error checking is turned off by default. I only switched it on because some Delphi guru advised it. Well, sorry guru, in the end your advice caused more problems than it solved.

Peace out.

Post script

Today I figured out how to bypass the EurekaLog dialog for the EDatabaseError 'not a valid floating point value':

  1. Activated Exception Filters in the EurekaLog options
  2. Added EDatabaseError
  3. Changed handler to RLT

Result is this "somewhat" more user friendly dialog, without the unnecessary "Send Error Report" option.:

30 years

It was 30 years ago that I first released the Kinetics program for the IBM PC. 1984 was also the year this classic film was released:

I had a yellow bug and a hair cut almost like KB’s:

Kinetics originated a couple of years earlier on a Timex Sinclair ZX81. Advertised as the first computer for under $100, it cost $99 (a lot for a new college graduate). About all you could do with the Sinclair was to write programs, it came with BASIC burned into the ROM. I’ve always been a hands-on learner, willing to jump into something before knowing everything about it. So I played around with the Sinclair for weeks until one day it all clicked, and I was able to write programs that actually did what I wanted.

1984 was also the year of the famous Apple super bowl commercial by Ridley Scott.

Oh, how I longed for an Apple PC, but there was no way I could afford one. Instead I bought an IBM PC jr.

It had two floppy drives and two cartridge slots on the front panel. The keyboard was line-of-sight wireless, and would only connect when it was positioned just so. To save money I bought the amber monochrome monitor instead of color. Back in those days computers came with printed manuals (because they really did need them). I wish I had kept those IBM manuals, they were gorgeous and well written:

The Kinetics program was originally written in IBM BASIC. On the PCjr, BASIC was supplied on a cartridge. The advantage being that a real programming language was always ready without taking up system memory. Being stored in ROM, the BASIC would load very quickly, not needing access to the floppy disk or other storage.

1984 was also the year that PC-SIG was founded. They published an annual mail order catalog of public domain and user supported software. Programmers could submit their work. A friend gave me a copy of the compiler for IBM BASIC. So I compiled the Kinetics program and sent it to PC-SIG. If memory serves it was published in the 3rd edition, 1986.

Before the internet there was CompuServe and the dial up Bulletin Board System. ASHP operated a BBS they called Pharm-Net. It was an electronic meeting ground and forum for exchange of ideas. Renato Cataldo, who was with ASHP at the time, asked me to post the Kinetics program in the download area of Pharm-Net. If memory serves that was around 1987.

My uncle I.J. retired from IBM in the early eighties. He was so tired of keeping up with the latest tech that he vowed never to buy another new electronic device. He still has a rotary phone to this day. The rest of us have not been so inclined.

Who needs google glass if you have this 1984 invention?

FreeKin update

It’s a FreeKin update yo’.

I can’t believe it’s been 12 years since I wrote this FreeKin modeler program. It was originally written in Delphi 5, which I no longer have installed on my PC. However, I was pleasantly surprised when Delphi XE2 imported it without a hitch.

I made a few FreeKin tweaks here and there:

  • Increased the size of the fonts and the dialogs.
  • Added a save to xls function.
  • Improved the report function.
  • Wrote a new help file (RTFM).
  • Miscellaneous improvements to the interface flow.

I doubt if anyone else has ever used this FreeKin modeler program, but I love it. Not just for the double entendre name, but also for the simplicity and no nonsense practicality.

Enjoy FreeKin, I do!

Main FreeKin Screen

Main FreeKin Screen

What’s old is new again

There was an excellent session at the Midyear entitled “Infectious Diseases Update: Using Guidelines to Optimize Treatment”. Dr. Michael Ryback spoke about the “Implications of the Vancomycin Consensus Guidelines”. He pointed out the shortcomings of the 2009 guidelines, specifically those patient populations who were omitted from the guidelines, obese patients being the largest omission (pun intended).

Dr. Ryback is on the panel currently revising these guidelines and he spoke about some of the changes the panel are considering. Most interesting to me was the (possible) recommendation to draw two post-dose serum levels following the loading dose.

To which I say a resounding yes. Considering the large interpatient variability in Vancomycin pk, why “guess” what a patient’s dosing requirements might be? Why wait 2-3 days to find out if your recommended dose is therapeutic or toxic? And then fiddle around for days with dose changes and more levels. With two levels following the loading you can nail down the patient’s vancomycin pk right off the bat. Home run.

Usually when I mention this as a solution to a younger pharmacist, I either get that “you’re crazy” look or a blank stare. Apparently these things have to come down from a mountain, carved into stone tablets. Or, at the very least, from someone with a half dozen initials behind their name. Sheep go to heaven, while goats go to hell (or at least exiled to the graveyard shift).

Regardless, this type of pk analysis is actually the original Sawchuck and Zaske method for gentamicin dosing, from 1977. All but forgotten except for a few devotees.

Kinetic model for gentamicin dosing with the use of individual patient parameters.

The “First dose 2- or 3-point” method of serum level analysis has been an option in all of my pk programs from the very beginning, some 30 years ago. Here’s a screen shot from the latest AbPK for Windows:

I certainly hope this recommendation makes it to the new Vancomycin consensus document. Or, at least, it opens the eyes of some of the braver sheep.

Windows help devolution

I have been writing Windows help files for nearly 30 years. It’s a very time consuming process to set up and maintain application help files. And just when I think I have a stable, workable system, Microsoft pulls the rug out from underneath me.

With the introduction of Windows Vista in 2006, Microsoft deprecated WinHelp and no longer includes it with the factory images of Windows Vista, Windows 7 and Windows 8.

HTML_help is Microsoft’s replacement for WinHelp. However, the Windows HTML_help viewer is crippled. It will only open CHM help files on a PC, not those stored on an intranet. Instead of content, the viewer displays the absolutely meaningless “Navigation canceled” message:

I have never understood this. An intranet is an internal network, so why in the world would it be considered a security risk to open a file on your own internal network? Apparently, Internet Explorer (tightly integrated into the Windows OS), is not able to discern the difference between the internet and an intranet.

So, I have been sticking with WinHelp as it is freely available for download from Microsoft for Vista, Windows 7 and (surprisingly) Windows 8, with both 32- and 64- bit versions:

Not until my PC at work was (finally) updated to Windows 7, did I realize that WinHelp is now just as useless as HTML_help. Windows 7 now blocks WinHelp from opening HLP help files stored on an intranet.

Again, WTF? What is the security risk of opening a file on your own internal intranet?

I could not find a trustworthy third party HLP file viewer, so I decided to switch to the CHM help format and seek out a replacement for Microsoft’s HTML_help viewer for intranet applications. After hours of web searching, I eventually found xCHM, a free, open-source, multi-platform, functional replacement for Microsoft’s HTML_help viewer.

Although it doesn’t have all the bells and whistles, xCHM does, at least, open CHM help files stored on an intranet.

In order to utilize xCHM, the help calls in APK had to be rewritten. Logically there are three scenarios:

  1. If the program is running from a local drive, the default help handling system is called.
  2. If the program is running on an intranet, and xCHM is available, then xCHM is called to display the relevant help topic.
  3. If on an intranet, and xCHM is not found, then an error dialog is displayed with a link to web help on

Code snippets

Determine if the program is running locally or via intranet:
function TfrmMain.IsOnLocalDrive(): Boolean;
aDrive: string;
aDrive := ExtractFileDrive(Application.ExeName);
if (GetDriveType(PChar(aDrive)) = DRIVE_REMOVABLE) or
(GetDriveType(PChar(aDrive)) = DRIVE_FIXED) then
Result := True
Result := False;

Determine which help system is available:
function TfrmMain.LocalHelpAvailable(): integer;
if IsOnLocalDrive then
result := 1
if XchmInstalled then
result := 2
result := 0;

Intercept the application help call and choose which help system to use:
function TfrmMain.ApplicationEvents1Help(Command: Word; Data: Integer;
var CallHelp: Boolean): Boolean;
MyHelpKeyWord : string;
case LocalHelpAvailable of
0: begin
// On network and xchm is *not* installed
CallHelp := False;
1: CallHelp := True; // Local drive
2: begin
// On network and xchm *is* installed
CallHelp := False;
// Get HelpContext of current active control
MyHelpKeyWord := IntToStr(Screen.ActiveControl.HelpContext);
// If control HelpContext is blank -> Get Parent HelpContext
if MyHelpKeyWord='' then
MyHelpKeyWord := IntToStr(Screen.ActiveControl.Parent.HelpContext);
// If parent is blank and parent is not a form, get form HelpContext
if MyHelpKeyWord='' then
if Screen.ActiveControl.Parent <> Screen.ActiveControl.Owner then
MyHelpKeyWord := IntToStr(Screen.ActiveForm.HelpContext);
// Fallback: if control, parent, and form HelpContext are all blank -> display start up topic
if MyHelpKeyWord='' then
MyHelpKeyWord := '10';

Shell out to xCHM:
procedure TfrmMain.ShellToXchm(HelpTopic:string);
strParameters : string;
// Create parameter string -- Requires short path name!
strParameters := ' /c ' + HelpTopic + ' ' + GetShortName(PathName) + 'apk.chm';
// Shell out to xCHM
ShellExecute(Application.Handle, 'open', 'xchm.exe', PChar(strParameters), PChar(PathName), SW_SHOW);

Looking back, this has probably been an exercise in futility. It seems as though most pharmacists either don’t know there is a help system built into the program, or they don’t care. Either way, no one has bothered to contact me to report a problem. Why am I not at all surprised?

Unicode support for RikiTikiWiki

The lack of Unicode support in the wiki is something that has bugged me for a couple of years. M$ Word is the usual source of these non-standard characters, such things as em- and en-dashes and curly (smart) quotes. If these non-ASCII (non-keyboard) characters were pasted into a topic they would be displayed as garbled nonsense in the viewer and editor.




This project was written in Delphi 2007 which does not natively support Unicode. I have XE, but I have no desire to go through the hair-pulling, forehead-slapping ordeal of updating all of the components in this project to yet another version of Delphi.

Since it did not support Unicode, the most likely suspect to me was the standard TRichEdit component. So, back in April I purchased the TMS Unicode component package. Well, that didn’t get very far because there was a major bug in the search function. Really, what good is a wiki without a search function? So, I emailed support and they put it on their to-do list. Eventually it got fixed and I updated that component this morning. Unfortunately that didn’t fix anything. So began the dogged pursuit of a fix.

The next suspicious component on the list was the HTML viewer. So, I found an update to the 5 year old open source component on After removing the old and re-compiling the new, no change.

Still believing it was a component problem, I looked into the database driver as the culprit. I pulled up SQL manager and did a dump of the raw data. It was fine, all the proper Unicode characters were there.

Then I began to believe that it had to be a problem with the variable declaration. All the experts on stack overflow had an opinion on what was the best string type to use for Unicode, and they were all different. So, I systematically changed the variable holding the text to every oddball string type available in Delphi: Utf8String, WideString, ANSIstring, and RawByteString. It didn’t matter, the output was still garbled.

I finally came across the open source Fundamentals code library on SourceForge. Included in that library were various decoder routines for converting character sets and encodings to and from Unicode. A function within it called UTF8StringToUnicodeString was the trick. Finally, it displays Unicode formatted text properly.


I hope all you copy-and-pasters appreciate how much fricking work this was. And don’t get me started on what a gawd-awful-piece-of-shit-poor-excuse-for-a-word-processor M$ Word is.

APK update

Sometimes it’s good to have a day off in the middle of the work week. I finished up something that I’ve had on my to-do list for years: a distribution plot for population analysis of Vol dist. The plot compares the 3-sigma normal distribution curve (green line) vs actual data points (red bars). Although it’s helpful to have descriptive statistics, nothing beats a picture:
Vd density plot

The population analysis report had to be re-arranged to fit the new plot on the page . I believe it is an easier read now. Here’s a comparison of new vs old:

Other improvements to the population analysis tool include:

  • BMI analysis option
  • CL vs Kel regression option
  • Automatically create/save graphs

While testing all these changes with real-world data I noticed a handful of Vd values in the consult table that were Zero. Further investigation revealed that these were *huge* patients with Vd’s > 100 liters. When I originally set up the APK database format over twenty years ago I did not forsee the U.S. obesity epidemic. Nowadays it is not unusual for us to see patients who are 300, 400, 500+ pounds, a sad commentary on the state of our country’s health. Also, I didn’t realize if the database engine encounters a number exceeding the defined field, it just skips it! It doesn’t flag an error, it just puts in a zero… WTF? Anyway, this update includes a change to the consults table, increasing the width of the Vd field from 4 to 5. This will accommodate patients up to 700 kg. If that limit is exceeded, please stop this planet and let me off.


Fun with ASP.NET

I’ve been trying off and on for 5 months to get the AJAX toolkit to run on my ASP.NET WebApp site. The web host only supports ASP.NET 2.0, so finding relevant information on an 8 year old technology is challenging.

I first tried adding a reference to the DLL in an aspx page:

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

The page crashed on load with this Parser Error Message: “Could not load file or assembly ‘AjaxControlToolkit’ or one of its dependencies. The system cannot find the file specified.”

I contacted the web host who stated “There is only ASP.NET 2.0 AJAX Extensions installed on the server. AJAX toolkit is not installed on the server.”

I had to ask if there was any possibility that the AJAX toolkit could be installed. The web host replied “There are two work arounds for this, first that you manually upload the DLL files into /cgi-bin folder and include the path of the DLL’s into your code and then let us know we will setup full permissions on /cgi-bin folder. Second work-around is to setup permissions for your IIS worker process user to Temporary .net folder please tell us the version of the .net you are using and we will setup the permissions accordingly.”

Since I had no idea what the second work-around was, I manually uploaded the DLL file to the cgi-bin directory.

Same error message “The system cannot find the file specified.” After some research, I added the path info to web.config:

Same error. Next, I added tagPrefix to controls:

Same error. Decided to switch tactics and go back to registering the assembly on the aspx page instead of web.config.

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

Same error, more research. Decided to add Src tag:

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Src="cgi-bin/AjaxControlToolkit.dll" %>

New error: The directive is missing a ‘tagprefix’ attribute. Added it:

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Src="cgi-bin/AjaxControlToolkit.dll" TagPrefix="toolkit" %>

New error: The directive is missing a ‘tagname’ attribute. Added it:

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Src="~cgi-bin/AjaxControlToolkit.dll" TagPrefix="toolkit" TagName="ajxtool" %>

New error: The ‘assembly’ attribute is not supported on this directive when a ‘tagname’ attribute is present. More research, took out the src and tagname tags:

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" TagPrefix="toolkit" %>

New error, The directive is missing a ‘namespace’ attribute. Added Namespace:

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" TagPrefix="toolkit" Namespace="AjaxControlToolkit" %>

New error: The located assembly’s manifest definition does not match the assembly reference. Aha, this was new. I double checked the version of the DLL I downloaded from Microsoft. It was version 1.0.20229.20821, not 1.0.61025.0. Finally, this worked:

<%@ Register Assembly="AjaxControlToolkit, Version=1.0.20229.20821, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" TagPrefix="toolkit" Namespace="AjaxControlToolkit" %>

So, the ASP “Parser Error” was leading me down the wrong path. The file wasn’t missing at all, it was an incorrect version match. After all that research, I knew it had to be some basic, fundamental thing I was doing wrong (it almost always is).

I went back to web.config and made the following changes.

Finally, what a relief. And all that work just to get a modal popup dialog, sheesh!

APK update

APK for Windows version 3.5.16 is available. Highlights include:

  • Added a point prediction tool.
  • Improved the ‘Outlier’ message dialog.
  • Added a goodness-of-fit indicator to the Bayesian results dialog.
  • Improved the ‘Non-steady-state’ message dialog.
  • Improved the serum level graph print-out.
  • Updated the help file.

Point prediction tool
Provides serum level prediction at any point within the dosing regimen. I find this useful for timing off-schedule doses and for evaluating non-steady-state levels.

Improved outlier message
Added detail to the Bayesian outlier message. Here is the old message:

And here is the improved message:

Goodness-of-fit indicator
A goodness-of-fit indicator was added to the Bayesian results dialog. This parameter is used to evaluate the reliability of the serum level analysis. The following parameters are included in the test:

  • Initial SS < 5
  • Final SS < 0.5
  • Final Vd SD < Model Vd SD
  • Final Kel SD < Model Kel SD
  • Predicted level = Measured level

If all 5 parameters are true, the measurement-to-model fit is excellent; 4/5 is a good fit; 3/5 is a fair fit; less than 3 is a poor fit.

Improved ‘Non-steady-state’ dialog
The previous dialog was wordy and vague:

The new dialog is simpler with clear choices: