After a lot of thought and a bunch of playing around with the code and running many tests, I have come to the conclusion that trying to have a prioritized order to the car attributes will not work. The thought was that if we could figure out the right order from most important to least, then we could start to relax the matching specifications by first ignoring the least important one, and so on with increasing importance. This would help to “force” a close-enough match during restaging.
The main problem with this idea is that there is really no intrinsic relative importance to these attributes, so that we could ignore the attributes in a fixed sequence of increasing order of importance. Such an ordering does not exist in general, because what may be safely ignored in one situation might not be the same in another. For example, is the car owner more or less important than its length? I think the correct answer is “it depends…” We may have roles that must have CPR cars because there is a contract to supply them but they don’t care about the length, and we might have other roles that must have a certain car length to fit at a loading dock but don’t care who the owner is. Neither is always more important than the other. A better way is needed to indicate what is optional and what is not.
Here is what I came up with, at least for now. Taking car length as an example, currently the program will accept an input of blank or “*” to indicate “don’t care”, or a list of one or more specific items to indicate that any one of them is acceptable. For example, a role with a length specification of “*” means that any length car is acceptable. A specification of “40|50|60” means that a car length of 40, or 50, or 60 feet is acceptable, but nothing else.
I am proposing to extend this approach and allow for something like 40|50|*, which would mean either 40 or 50 feet to start, but if there is no match, then the length can be relaxed to “Any” for a subsequent attempt. Exactly how this would apply when there are multiple attributes that include an “*” is not clear to me yet, but I’m sure something will emerge. The issue is that if a role has say three attributes that all can be relaxed, which one (or more) do you try first? How many attempts do you try? What order do you relax them in? As a start to implement the basic idea of being able to make some attributes optional, I will just have it be all or nothing. The first pass should match exactly or not, and a second pass will simply ignore all of the “relaxable” attributes. Maybe we should call them “Optional” attributes if they include an “*”?
The end result is that each role can include some specifications that are not optional and must be matched (they don’t include an “*”), and some that are optional and may be ignored in order to keep the cars rolling. I think this should provide enough flexibility, at least for now.
Some thoughts on the relative importance of various car attributes that can be considered when restaging cars.
The issue came to light during a phone call I had with Colin about his waybills and how he selects them for a particular car. I need to try to duplicate the mental process he goes through when resetting the layout, if at all possible.
At the moment, I have identified the following attributes, or characteristics, of a car that can be used when selecting a new role for them in staging. The list now includes the reporting mark initials and number, plus the owner railroad. I have listed them in a suggested order of most important to least to be considered when matching a car to a role, along with one sample for each attribute. Note that not all attributes will apply to one specific car. Most obvious is a gallon capacity for a boxcar. But, they are all available to be used as appropriate.
– Initials: CP
– Number: 123456
– Type: Box
– Length: 40
– Subtype: Insulated
– Class: Rough
– Door: 8 feet
– Capacity: 6000 gals
– Owner: CPR
The order of these has some significance when we consider what to do when there is no match. The current program can handle a sequence of either-or values so that more than one attribute value can be considered to be a match. For example, the car length spec can be shown as “40|50” which means either a 40 foot or 50 foot car is acceptable and will match the role.
But what happens if no roles match a car that needs to be restaged when using this approach? Colin said that he often has to “substitute” cars for waybills so that the car can remain on the layout and have a role to play. We obviously want this substitution to be as close a match as possible, and as an example we will readily substitute a 6 foot door for an 8 foot request, as that will be barely noticeable. However, substituting a tank car for a box car would not be a good idea! So obviously some of the different attributes have more importance than others, with the primary car type being very important.
My idea for the program is that if a match can’t be made, then a second pass will be made, this time ignoring one of the less important attributes. If still no match, then we repeat this process until we have gone as far with substitutions as we are willing, and give up and store the car. It is this ordering of importance that I am trying to figure out.
My process to restage cars in staging by finding a role for each car relies on being able to find at least one suitable role for each car that is restaged. If no role can be found the car must be removed temporarily to storage. This approach works well when there are enough suitable roles to select from for a given car, but it breaks down when there are not. Too many cars may end up in storage when the entire point of the exercise is to run trains! A solution to keep cars moving is to substitute roles that are not a perfect match and carry on. Colin mentioned that he and Gary often have to do just that when using their traditional paper car card and waybill system. I’d like to capture some of the ideas behind how this “forced” matching is worked out and implement it in the program.
There are obvious things we can do in some cases. For example, if the particular car to be restaged has 6 foot doors but there are no roles asking for a 6 foot door, only 8 foot, then we may decide to send the car anyway, even though its door is narrower than what is requested and a real shipper would be angry because their forklift truck would not fit. The basic notion here is that there may be some parts of the car spec that can get ignored to help find a match, without causing too much grief. In this case we ignored the door attribute because it really doesn’t matter, nothing will actually be loaded. Maybe there is an order of things that we can ignore in the hope of finding a match so that the car does not have to get pulled off into storage. For example, if we don’t find a match to start, first ignore the door spec, if still no match, then ignore the car class, and so on until we run out of things we are willing to compromise on and the car has to be moved to storage. I suspect that people go through something like this in their minds when selecting paper waybills. I’d like to try to capture that in some simple rules that I can implement in the program.
In addition to the Car Specification, a role also defines where the car goes on the layout to fulfil the role. Each of these segments of the car’s journey are currently called a “Leg”. This probably needs a better name, but it will do for now.
A leg includes the type of car bill, the destination track, the load, and the wait time after arrival for loading or unloading, or whatever.
Typical types of car bills are “Empty Supply”, “Freight Waybill”, “Empty Return”, and so on. I call them all “waybills” even though that is not technically correct, but so be it.
Loads will normally be specified only for the freight leg of the journey, but it can be anything that makes sense to the user. A car could be used to deliver one load and back-haul a different load.
The notion of legs can easily be expanded beyond the typical waybills to include extra car moves such as for icing, cleaning, custom’s inspection, and so forth. There can be any number of legs in a role. Each leg also includes the wait time that a car must remain at its destination before being eligible to move again. Usually this is about a day, but could be as short as a couple of hours for icing, etc. Again, it depeneds on the desired effect.
To help visualize all of this, here are a couple of example roles, expressed in English:
Role One: – Suitable for a Boxcar, 40 feet, Rough, with 8 foot doors, at CPR Staging.
Legs: – Empty Supply to Shipper A; – Freight Shipment of Lumber to Receiver B; – Empty Return to CPR Staging.
Role Two: – Suitable for a Boxcar, any length, Clean, any width doors, at GN Staging.
Legs: – Freight Shipment of Appliances to Receiver C; – Empty Return to GN Staging.
Roles also include a weighting, or a kind of priority, that is used to influence the random selection from all suitable roles. Roles with a higher weighting will have more chance to be selected, while roles that should only be picked on occasion should have much lower weights.
There are all sorts of ways that this concept could be improved going forward. I can see having some of the legs optional with some sort of probability, so as to reduce repetition and boredom. And, it might be possible to have roles that are continuous for cars in captive service that just go back and forth between two locations. Lots to think about.
Ok, so now we have a bunch of attributes that describe a car, but how do we use this when restaging cars? The things that will make use of all of this car attribute stuff are called “Roles”. A Role includes basic shipment information, but it also contains the entire life-cycle of a car from staging, on to the layout, and eventually back to staging. Some of the phases of that life-cycle have the car being moved empty for supply or return, so there really is no shipment for those parts, hence the new name “Role”. Roles now replace the earlier primitive shipment model that made too many assumptions and caused a lot of grief.
Every car in staging that is about to return on to the layout requires a role that governs all of the places that car will go. For every car being restaged, the program searches through all of the available roles to find those that are suitable for that car by considering all of its attributes. Roles contain a Car Specification that gets tested for a match to the car. A very simple example role might say that it wants a “Boxcar:40” and is silent on the other attributes, so they are considered as “don’t cares”, and only the Car Type and Nominal Length of the Car will be considered. In this case, every 40 foot box car waiting to be restaged would be a match.
This is all part of the process that we go through manually with paper Car Cards and Waybill systems during restaging, I just want to capture the essence of the process and automate it.
Roles usually specify a car type and a couple of the other attributes, but the process of matching cars to roles can get quite complex when we start to think about more subtle issues. What if a shipper can accept cars of different lengths, but not just any length, say 40 or 50 foot, but no longer. And how do we handle roles that apply only to specific individual cars and no others, even if they share the same attributes? A good example of this would be captive service between two industries. And of course there is the owner railroad, and reporting marks to consider. The combinations can grow to be quite complex in order to handle all of these special cases, but we still want the system to be easy to define for the much more common simple cases without becoming so complex that it is hard to understand.
This sounds like it is getting very complicated to set up the data, but please remember that all of these fields can be left blank or filled with just an asterisk (*) and they will not be considered during selection. Only a few of the fields will be routinely used, such as Car Type and Nominal Length.
My solution is to use a series of “Specifications” that each contain a description of which car attributes are “acceptable” to the Role for that piece. The combination of all of these specs is referred to as the Car Specification. So, what is a specification? In simple terms it holds zero or more car attributes that can be matched to a specific car to see if they match or not. The whole notion of a “match” is non-trivial, as we must handle multiple acceptable values, an empty list that signifies that we don’t care about that particular car attribute, and cars that have empty attributes.
Some examples should help makes this clearer. Let’s consider a car’s nominal length. If an industry can accept only 40 foot cars due to loading dock spacing, that would be specified simply as “40”. If it can handle 40 or 50, but no other lengths, it would be specified as “40|50”, where the “|” (vertical bar) signifies “either”. A specification list can hold as many alternatives as needed. In this case, when the role is being considered for a particular car being restaged, it will be included as a possible candidate for any car with a length of 40 or 50 feet. Other actions will affect which role finally gets selected, but it first must be a candidate role that matches the car. The overall selection process does this matching for all non-empty specifications.
On the other hand, if we don’t care about the length of the car, for say a team track, then the specification becomes a simple asterisk, “*”, meaning “don’t care”, and the role will match any car length.
A more complete example would be for a Boxcar, 40 or 50 foot, Clean or High class, with 6 or 8 foot Doors, but we don’t care about a Subtype, or a Capacity. The car specification for this would be:
The complete specification will also include more don’t cares for the initials, number, and owner railroad, but these have not been added in yet.
Given that we now have six attributes to describe a car, we need to decide on a typical order for them when they get displayed. Some are more often used, like the main Car Type, and some are less frequently used, such as Door style. I am thinking that we want them to be arranged from left to right in decreasing order of usage. So here is what I came up with:
Type : NomLength : SubType : Class : Door : Capacity
Those that are blank will simply not be included. Examples again from Colin’s railroad:
And one with everything, just not sure what it all means together:
I started to think more about the car attributes and which ones are really necessary. I had said that we need the Car Type and Nominal Length, but not always the other four attributes. Now I am thinking that they are all optional. Stay with me for a moment, and it may make sense. Ok, we do need the Car ID, namely the Road and Number, otherwise we can’t find the car to move, and we need the actual length to make sure there is space for it. But as for the other things, they are only used to select suitable shipments for the cars when they get restaged. It would work even if we had all six attributes blank, meaning “don’t care”, or “unknown” for everything. That means that such a car could be given any role, as there is nothing to match on. While this sounds silly, it is a good way to get a new system started by generating lots of car movements without any real regard to what is going where and in what type of car. Not very prototypical for sure, but it can help prove out a train scheduling scheme and get lots of cars moving about. There is nothing really that dictates that only one specific type or class of car can be delivered to a certain industry. It is all 4’- 8 1/2” between the rails and any car can physically go anywhere. After some experience is gained with how the cars move with the trains according to their timetables, more specific roles and car types can be introduced to narrow things down and get more prototypical movements.
So far my Car Forwarding program has only considered a car’s basic type and length when doing restaging. This is not specific enough to be realistic, but it was sufficient to get the rest of the code working correctly. Now it is time to expand the attributes for a car so that we can have more realistic assignments for cars leaving staging.
After a lot of analysis and discussion, it looks like there needs to be six main attributes to describe a car. It is always possible to add more later on if the need arises, but this should work well for now.
The additional attributes are car Subtype, Class, Door, and Capacity. All of these are user definable as to what they actually mean, but the terms are there to help guide the process and be familiar. None of these are actually numeric, but symbolic, at least for now. For example, a capacity of 6000 cannot be compared to 7000 numerically, but is just a label that can be displayed and printed, and should make some sense to the user. Later on, if there is a need to specify some kind of numeric comparison, such as “any car length less than 50” it can be added.
Not all attributes will apply to a given car, for example, tank cars usually don’t have doors. In this case, the field is simply left blank and it will be ignored.
The purpose of all this is to allow the car restaging process to be able to match suitable roles to the cars in staging. We don’t want to assign a tank car shipment to a boxcar, or vice versa. There is a lot of flexibility in this process, as a role can be told to ignore, or “don’t care” about any or all car attributes so that there is very fine grained control over the assignment process.
The following are some examples of the six car attributes, taken from Colin’s railroad.
Type Autorack Boxcar Covered Hopper Flat Gondola Reefer Tank
Length 40 50 75 85
Subtype 2 Deck 3 Deck Auto Bulkhead Chemical Drop Bottom Ice Insulated Mechanical Oil Vinegar
Class Clean High Newsprint Rough
Door 5 6 8 10 Combo Double Plug PS
Capacity 6000 8000
In the car description data any or all of these attributes can be left blank which implies that the attribute is not important and will be ignored when matching the car to roles. Obviously tank cars do not have doors, etc. Even the main car type can be ignored, if for instance we had some generic shipments to a team track that can accept any type of car.
More attributes can be added if necessary, but this should handle most situations based on local discussions.
Other attributes that help identify the car are its owner railroad and reporting mark initials and number. These may get added to the role data model so that roles can be made very specific down to an individual car or set of cars.
Some format is needed to be able to display the complete car attribute information, so the following is proposed. The complete string will consist of a concatenation of the non blank attributes, separated with colons, “:”. Something similar applies to the roles, but more details of that will come later. The order of the attributes was selected to move from most common on the left, to least on the right. All cars should have a type and length, so those are the first two , followed by subtype, class, door, and capacity. So some examples:
A 40’ boxcar with 8’ doors, for clean loading would be specified by: “Boxcar:40:Clean:D8”.
A Shipment that needs a clean 40’ boxcar, but with any door width would request it as “Boxcar:40:Clean:*”, where the door width is specified with an asterisk, indicating “any” width will do. The same can be done for the length, etc.
Another Shipment that only cares that it has any kind of a boxcar would use “Boxcar:*:*:*”, with asterisks for everything except the basic car type.
One issue that is not completely resolved at this time is how to handle missing attributes. If we want to be able to parse a car attribute string, it gets difficult if it has an unpredictable number of parts, for example “Boxcar” versus “Boxcar:40:Clean:D8”. The simplistic approach would be to require all such strings to have a fixed number of parts, but this will look very messy with lots of “*”. A better solution is to require that all of the attribute names be unique and then the program can search for each name and from it determine if it represents the car’s length, class, door, or whatever. This seems to be a good compromise in that each attribute tends to be quite different in the types of names used, so not allowing the same name for different attributes should not be a problem. For example, lengths tend to be two digits (40), while capacities are 4 digits or more (6000). Time will tell if this assumption holds true or not as more car attributes are encountered from the real world.
The format of a role string, on the other hand, must include all of the parts in the correct sequence, because it must know which are to be ignored. It might be possible to make this more flexible with some more thought and testing, but for now this will have to be the case.
How do we measure the “goodness” of an operations scheme for running a model railroad? Well, having a computer program that can easily run studies of a layout over many days with different operating schemes, train timetables, and other parameters, we can generate a lot of statistics from each run and then compare them, but what measures indicate a better or a worse outcome? We have all attended good and not so good operating sessions and can probably list a number of aspects that made one better than the other. The trick is to find a way to quantify at least some of these so that the program can better serve as a planning tool, and we don’t need to impose on our friends while we “try it and see”. There are certainly some aspects that people enjoy about a session that can’t be quantified, but maybe we can find some way to approximate a measure of “goodness”.
Stepping back from calculations for a moment, the main purpose of an operating session is to move cars around a layout to where they should be, through the running of various trains according to some operating scheme. The non train aspects, such as dispatching, train orders, etc., are outside of the scope of this discussion, because they are not simulated by the program. With that in mind, I think that the following characteristics can be accepted as indicative of a good, well run operating session.
Industries should have the number of cars they require.
A good number of the cars should move at least once.
All cars that are ready to move should move.
Only specialized cars should be off the layout in storage.
Yards, interchange, and other nonrevenue locations should have a minimum number of cars.
In other words, the layout should be well utilized, cars should flow smoothly to where they should be going, and there should be lots of activity to keep the crews busy during the session. Layouts with too few cars in play to provide sufficient interest or those with so many cars plugging up everywhere that hardly anything moves tend to be less satisfying for crews. Some variation is of course desirable, but extremes are not.
So the trick is to find some measures in the program that can capture these ideas so that we can judge if one operating scheme is better than another. The idea is to have a number of metrics about a session that can be combined to produce an overall score. If each one has a maximum value of 1.0, meaning that the aspect is as good as it can be, then we may be able to just multiply them together and still consider 1.0 as the “perfect” result.
Some of these characteristics tend to be at odds with one another. For example, if we add more cars to a layout so that we have a higher fraction of the industries fully served, it may result in cars not flowing through yards as quickly due to congestion and limits. This reduces the overall mobility and can lead to gridlock.
Here are three ideas for what we might measure.
Pickup Ratio – The fraction of times when a car is ready to move, and a train should move it, but it does not pick it up due to some limit on length, space ahead, etc. If a car always gets moved when it is ready, then the score would be 1.0.
Q Factor – The fraction of cars that are at revenue tracks (industries) and staging, versus those on nonrevenue tracks and in storage. We want the cars to be moving about the layout and not sitting waiting somewhere. Again, 1.0 is best.
Revenue Usage – The fraction of the track space at industries that is occupied by cars. Obviously this cannot always be 100 percent, as some industries only receive cars infrequently, but we also don’t want them never more than say half full either.
These three metrics are easy to calculate by the program, and are reported in a summary file for each study run.
To play around with how all of this might look, I plotted these measures for the S&BC for different numbers of cars, and different interchange probabilities. Other posts talk about some of the issues we have had with too many cars clogging up the Grand Forks yard because they are waiting to be interchanged between the two railways. The solution seemed to be to minimize the shipments that require interchange by coercing them to be selected less often, so this was a good test case to see if these metrics would support this idea in a quantifiable way.
The following graph shows the effect of having 40, 60, 70, 80, and 98 cars on the layout, with the train schedules and everything else the same. The probabilities of interchange shipments were not reduced. We would expect that as more cars are added, the revenue track usage would go up, as it is very low with only 40 cars in play, but we might end up with so much congestion that cars start to be unable to move when they should. This in fact is what happens. The green line shows the track usage going up from just over 0.2 to 0.7, meaning that we have a lot more cars delivered to the industries from the additional cars. But, the yellow line shows that there is a price to pay for this, namely congestion, as the rate of car pickup drops from over 0.9 (very good) to about 0.35 (very bad), which means that a lot of the cars that are ready to move are being skipped by trains when they should otherwise be picking them up.
The blue line is what I am calling the Qfactor, using the formula shown at the start of this post. It is the fraction of cars at revenue and staging tracks, divided by the total number of cars. It simply indicates what portion of the cars are on the “good” parts of the layout, actively participating in the running of trains, versus those that are mostly sitting idle for one reason or another. In this case it is mostly constant and independent of the number of cars in play because there is extra capacity at the industries that can accept more cars, however a roughly proportionate number of additional cars will also end up on the non-revenue tracks such as yards while they wait longer to be delivered. This is a good example of why we need more than one metric, as there are opposing tends at work.
And finally, the red line is just a combination of the other three, in an attempt to provide a single value. I’m not sure yet how much value this may have. Time will tell.
A second set of studies were then run with the same numbers of cars, but with a much lower probability for the interchange shipments. This should result in far fewer cars waiting in the Grand Forks yard to be interchanged from one train to another and more at the industries. All of the shipments that required interchange between trains were set to a very low probability, so that they would be selected only as a “last resort” if there were no other direct shipments available.
This time we have a very different set of curves. The car pickup ratio (yellow) is very high throughout, meaning that cars never have to wait long after they are ready to move, which is what was expected. The industry tracks are never very full at only 30% (green) so that is a concern, and the Q factor (blue) starts out very good, but drops rapidly as more cars are added. A closer examination shows that this is because almost all of the extra cars end up getting stuck in storage and never making it on to the layout. This in turn is because none of the shipments will be exchanged in the yard, so there is nowhere for them to accumulate and be in play on the layout. In short, the trains leaving staging quickly get saturated by cars freely going between staging and the industries, with none sitting around waiting. The flat portions of the yellow and green lines shows that the extra cars have no effect on them. This would probably be considered a good operating session with freely flowing cars, so long as it was acceptable to have low utilization of the industries. One conclusion from this is that the “natural” number of cars for this operating scheme is somewhere between 40 and 60 cars. The way to have more cars on the layout is probably to run more trains so that there is more capacity out of staging to feed the layout. This might be worth trying sometime.
So, in summary, I think these three metrics give a good insight into the nature of an operating session simulation. I expect to improve my understanding of their implications as more experience is gained, but for now, they should serve well for planning, comparing, and contrasting different operational schemes. Oh, and what does Goldilocks have to do with this? Nothing at all.
The original design of my CFS program used the notion of a Shipment, which implemented the basic idea of a shipper sending a load of something to a receiver, much as one would expect. The starting and ending tracks could be either in the modelled network (on the layout) or at virtual tracks in the unmodelled space (behind staging). With this approach, only the loaded segment of a car’s journey is defined, regardless of if it is heading in to or out from the modelled layout. Every car’s assignment will always include at least one segment that is done empty, either to supply a car to a shipper to be loaded, or to return an unloaded empty car back home, or both. With a Shipment having only information for the loaded segment, the other segment(s) must be generated using some sort of rules. The various rules tried so far have been less than entirely satisfactory, and this has led to some unfortunate side effects in terms of not being able to shape car movements in desirable ways.
A good example of this problem is when a car is supplied to a modelled shipper. The car will usually come from one of the staging tracks, but which one? The shipment is silent about where a car comes from, or where it returns to. When restaging a car, we may not want the selection process to consider all staging tracks equally, as that may have ramifications on car interchanges, flow through yards, etc. It would be better if there was a way to influence this selection process by having relative weights on the different choices, so that some are more likely to be selected than others. This allows for some oddball routing for variety, but the bulk of the selections will be what we expect.
Similarly, the choice of which way to route an empty car towards home suffers from the same problem. While there are lots of prototype rules around this, the reality on a model railroad is that we may have to coerce the routing so that parts of the layout do not get overloaded. Again, having a weighting scheme would help.
A simple solution to both problems is to define a complete life cycle for each shipment, including car supply, freight movement, and empty return. Each role is given a weighting that can be used when selecting from otherwise equal and suitable choices. For example, car routings that do not involve interchange to other railroads may be given a higher weighting and therefore are more likely to be selected than ones that have interchange, thereby minimizing cars having to be switched through a yard.
This new notion is called a “Role” for now, so that it can coexist with the current Shipments, but the name may get changed back to just “Shipment” after the original Shipment concept has been completely removed from the code.
One major benefit of this approach is that car life cycles that are more complicated than just a simple supply, freight, and return sequence become very easy to implement, as the role will simply have more segments to be followed. Think of a reefer needing to be iced before it is loaded. It really needs to make two stops for “loading”, one for ice, and the other for the actual load. This new simpler scheme eliminates the need for convoluted rules to handle the extra car movements. It now becomes very easy to include some unusual shipments that should get selected only very infrequently, simply by giving them low weights.
In order to implement this change in the program, significant code must be changed, which will be a big job as the results must be checked carefully to make sure that it really is working as expected. Results from early testing look very promising.