Dec21

Logistic model for the s-curve and project management cost estimation

Tagged with: ,,,.
Comments

I posted a while ago an article on Modeling market adoption with a logistic curve in Excel. It has been quite popular on this site, and even at some point was linked as a reference on Wikipedia. Shameless plug – if you find it valuable, please ask Wikipedia maintainers to keep the link.

Recently reader Mina asked how to use the Excel model in the context of project management. The particular question is: if the project duration changes from 18 to 48 months, what is the new spending curve? Fast and quick answer would be – it’s not applicable. This post will take the question for a spin, though.

First off, the PMBOK, or Project Management Body of Knowledge, tries to standardize and unify the terminology that PMPs use when managing projects. The term used to refer to the spending curve, along the life of a project is called “s-curve” in the PMBOK, just because in real life, projects tend to have low budgetary requirements in the early stages (you are framing the project, defining what will be done, etc), then a bulk of spending in the middle, and finally, low requirements again at the end. With that general pattern, the cumulative spending plan roughly looks as an s-curve.

In most cases, the easiest way to answer the question would be to look at your Gantt chart, estimate which are the tasks that will be delayed/extended, and regenerate an spending curve.

If you are not actually managing the project, but instead you are doing analysis on “what ifs” regarding projects where you don’t have the actual list of tasks, or you are doing portfolio management and you are forecasting across a portfolio of projects with different life spans and budgetary needs, you may be fine using a logistic model. Your needs may be different.

With that preamble, here we go. I’m going to use the estimate percentages of spending in Mina’s question

The process will be quite simple:

  1. Fit the data to a logistic model
  2. Apply the derived model to the new project duration
  3. Infer the spending by month under #2

An Excel file to do this can be downloaded here. By downloading it, you are agreeing that any damages, consequential or incidental arising from using this file or the information in this post are your sole responsibility, and you explicitly releases me from any liability.

If you look at the table below, the first two columns are the data provided by Mina, the third column just adds up previous spending, then column D uses my simplified Excel logistic model =saturation/(1+sharpness^((hypergrowth+takeover/2-year)/takeover)) to forecast the cumulative spending, and column E is simply the square of the difference between C and D. The formula for E2 is =(C2-D2)^2. We’ll use the sum of minimum square of errors to fit the curve, to keep things easy. Other fitting techniques are OK too.

Excel Table showing the process

Excel Table showing the process

Then we use Excel Solver, to minimize E21, subject to the condition that $D$19=1, by changing saturation, hypergrowth, takeover, and sharpness (C21 to C24). Fill in the most realistic guesses you can find for these parameters before running Solver. Given the technique Solver uses, your guess of sharpness will be barely modified (if at all), so spend some time looking at your data and tweaking manually.

As you can see in the image below, this step fitted the cumulative spending curve to the logistic function.

Fitted s-curve logistic function to data

Fitted s-curve logistic function to data

Column F in the table just infers the spending by month by subtracting the cumulative on each period to the cumulative on the previous one. As you can see below, even if the cumulative curve fitted more or less nicely, the inferred monthly spending may look surprisingly different to the original.

Monthly spending curves side by side

Monthly spending curves side by side

You’ve got to remember, all models are a simplification of reality, to extract the things that are important for the particular use of the model. As mentioned above, if you are doing “what if” analysis or portfolio management, this simplification may be acceptable and the differences are not a surprise to you.

Finally, we define two new constants newhypergrowth=hypergrowth*newlength/oldlength, and newtakeover=takeover*newlength/oldlength which simply allows us to use the s-curve over a longer time span. The new cumulative spending (across months 1-48 in the example) would be =saturation/(1+sharpness^((newhypergrowth+newtakeover/2-newyear)/newtakeover))

Dec18

Dynamic DNS woes

Closed

My home network has the typical configuration NAT behind a hardware firewall with a dynamic IP address provided by the ISP. I use DynDNS to have a fixed name that I can use anywhere on the internet to access the home network.

The Linksys WRT54G I use supports DynDNS updates, however, from some weeks ago, I started having issues with the updates. A cryptic error -1 wasn’t of much help.

To avoid the hassle of debugging what’s going on, I decided to give the responsibility of the updates to a Linux box inside my home network, instead of the router.

The software I used is ddclient, which is in the list of recommended clients for DynDNS. From version 3.7.0 ddclient supports SSL, so the password isn’t traveling in cleartext.

Installing ddclient in Gentoo Linux is a breeze: emerge -a ddclient

The configuration file /etc/ddclient/ddclient.conf is quite straightforward and the sample file installed should get you going right away. Mine now looks as follows.

daemon=300                              # check every 300 seconds
syslog=yes                              # log update msgs to syslog
#
## To obtain an IP address from Web status page (using the proxy if defined)
use=web, web=checkip.dyndns.com/, web-skip='IP Address'
login=yourlogin
password=yourpassword
protocol=dyndns2                                # default protocol
server=members.dyndns.com
ssl=yes
#mx=mx.for.your.host                            # default MX
#backupmx=yes|no                                # host is primary MX?
wildcard=yes

your.domain.net

To get there, I had to jump through some hoops, which I hope this post avoids you:
1. Do not use members.dyndns.org as the documentation states. Use .com
2. The ssl option doesn’t seem to work and it is not because IO::Socket::SSL is not installed, as hinted in the DynDNS FAQ and the ddclient FAQ — well, you DO need it installed but I’ve read the fine manual already and that was not my problem for sure.

I discover #1 after starting the ddclient daemon as usually in Gentoo /etc/init.d/ddclient start and discovering syslog errors like

Dec 17 03:24:50 host ddclient[9348]: WARNING:  cannot connect to members.dyndns.org:8245 socket: IO::Socket::SSL: connect: Connection timed out IO::Socket::INET configuration failederror:00000000:lib(0):func(0):reason(0)
Dec 17 03:24:50 host ddclient[9348]: FAILED:   your.domain.net: Could not connect to members.dyndns.org:8245.

It may be that I’m in China and dyndns.org is blocked from there. I don’t know. But you can check easily on any web browser. Browse to http://checkip.dyndns.com/ and you should get Current IP Address: 111.222.333.444. For me dyndns.com works.

Next it was the ssl issue. Fortunately, debugging ddclient is very easy. Stop the daemon /etc/init.d/ddclient stop and then run ddclient -daemon=0 -noquiet -debug from a terminal. Doing that I was able to see that even with the ssl=yes in the config file, the request was done using http. On the output of the command I was able to see

DEBUG:     nic_dyndns2_update -------------------
DEBUG:    proxy  =
DEBUG:    url    = http://members.dyndns.org/nic/update?system=dyndns&hostname=your.domain.net&myip=111.222.333.444&wildcard=ON
DEBUG:    server = members.dyndns.org

I changed the file /etc/init.d/ddclient — I know it is not recommended because any update to ddclient will overwrite the change. However, these days I’m always rushed and therefore less of a purist :)

The change was adding the -ssl switch in front of --exec /usr/sbin/ddclient to the start function, as seen below

start() {
        checkconfig || return 1
        ebegin "Starting ${SVCNAME}"
        start-stop-daemon \
                --start \
                --chuid ddclient \
                --exec /usr/sbin/ddclient -ssl \
                --name ddclient \
                --pidfile "${PIDFILE}" \
                -- -pid="${PIDFILE}"
        eend $?
}

I’d love to hear comments on a better way to get this problem fixed.

Dec3

ActiveScaffold summary fields

Closed

In one project I am the “gatekeeper” of a list of potential initiatives that the organization would like to explore as sources of revenue.

These initiatives are cross-regional in Asia, and best practice would call for the subject matter experts on each country to “own” and maintain the initiatives.

Thanks to the beauty of Rails, ActiveScaffold and other wealth of plug-ins available for Rails, in just 2 or 3 days of coding I was able to get the basics of a web application to handle this. Much better than a shared Excel Spreadsheet.

By the way, I tried using Sharepoint to maintain this list, and was underwhelmed. Anyway, recently added a summary field on the initiatives I track, to be able to show total (forecast) revenue and sales.  Followed the documentation on the ActiveScaffold website, and started receiving an error:

Showing vendor/plugins/active_scaffold/frontends/default/views/
_list_calculations.rhtml where line #12 raised:

undefined local variable or method `_erbout' for #<actionview ::Base:0xb518c850>

Extracted source (around line #12):

9:         override_formatter = "render_#{column.name}_#{column.calculate}"
10:         calculation = self.method(override_formatter).call(calculation) if respond_to? override_formatter
11:
12:         _erbout.concat calculation.to_s
13:         -%>
14:       < % else -%>
15:         &nbsp;

Trace of template inclusion: vendor/plugins/active_scaffold/frontends/
default/views/_list.rhtml, vendor/plugins/active_scaffold/frontends/
default/views/list.rhtml

The variable _erbout is the default output variable from an Erb object (see http://noobkit.com/show/ruby/ruby/standard-library/erb/new.html)

My proposed fix (I’ll send a patch to the ActiveScaffold devs) is to remove the _erbout reference and replace by

  < % if column.calculation? -%>
    < %
      calculation = column_calculation(column)

      override_formatter = "render_#{column.name}_#{column.calculate}"
      calculation = self.method(override_formatter).call(calculation) if respond_to? override_formatter
    -%>
    < %= calculation.to_s -%>
  < % else -%>
    &nbsp;
  < % end -%></actionview>