Skip to content

Commit

Permalink
criticality calculation of operationplans
Browse files Browse the repository at this point in the history
  • Loading branch information
jdetaeye committed Aug 3, 2014
1 parent f707e29 commit 0a398cc
Show file tree
Hide file tree
Showing 26 changed files with 444 additions and 23 deletions.
2 changes: 2 additions & 0 deletions bin/frepple_core.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@
<xsd:element name="quantity" type="positiveDouble" />
<xsd:element name="locked" type="xsd:boolean" />
<xsd:element name="owner" type="operationplan" />
<xsd:element name="criticality" type="xsd:double" />
<xsd:element name="consume_material" type="xsd:boolean" />
<xsd:element name="consume_capacity" type="xsd:boolean" />
<xsd:element name="produce_material" type="xsd:boolean" />
Expand All @@ -848,6 +849,7 @@
<xsd:attribute name="start" type="xsd:dateTime" />
<xsd:attribute name="end" type="xsd:dateTime" />
<xsd:attribute name="quantity" type="positiveDouble" />
<xsd:attribute name="criticality" type="xsd:double" />
<xsd:attribute name="locked" type="xsd:boolean" />
<xsd:attribute name="consume_material" type="xsd:boolean" />
<xsd:attribute name="consume_capacity" type="xsd:boolean" />
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ AC_CONFIG_FILES([ include/Makefile include/frepple/Makefile ])
AC_CONFIG_FILES([ src/Makefile src/model/Makefile src/solver/Makefile src/utils/Makefile ])
AC_CONFIG_FILES([ modules/Makefile modules/lp_solver/Makefile ])
AC_CONFIG_FILES([ contrib/Makefile contrib/vc/Makefile contrib/django/Makefile contrib/installer/Makefile contrib/rpm/Makefile contrib/debian/Makefile contrib/odoo/Makefile ])
AC_CONFIG_FILES([ test/Makefile test/cluster/Makefile test/custom_fields/Makefile test/calendar/Makefile test/constraints_combined_1/Makefile test/constraints_combined_2/Makefile test/constraints_leadtime_1/Makefile test/constraints_leadtime_2/Makefile test/constraints_material_1/Makefile test/constraints_material_2/Makefile test/constraints_material_3/Makefile test/constraints_material_4/Makefile test/datetime/Makefile test/flow_alternate_1/Makefile test/flow_alternate_2/Makefile test/flow_fixed/Makefile test/lpsolver_1/Makefile test/scalability_1/Makefile test/scalability_2/Makefile test/scalability_3/Makefile test/jobshop/Makefile test/xml/Makefile test/xml_remote/Makefile test/constraints_resource_1/Makefile test/constraints_resource_2/Makefile test/constraints_resource_3/Makefile test/constraints_resource_4/Makefile test/constraints_resource_5/Makefile test/constraints_resource_6/Makefile test/problems/Makefile test/deletion/Makefile test/demand_policy/Makefile test/operation_alternate/Makefile test/operation_available/Makefile test/operation_effective/Makefile test/operation_pre_post/Makefile test/operation_routing/Makefile test/multithreading/Makefile test/name/Makefile test/python_1/Makefile test/python_2/Makefile test/python_3/Makefile test/sample_module/Makefile test/callback/Makefile test/pegging/Makefile test/safety_stock/Makefile test/buffer_procure_1/Makefile test/flow_effective/Makefile test/load_alternate/Makefile test/load_effective/Makefile test/setup_1/Makefile test/setup_2/Makefile test/setup_3/Makefile test/skills/Makefile test/wip/Makefile ])
AC_CONFIG_FILES([ test/Makefile test/cluster/Makefile test/custom_fields/Makefile test/calendar/Makefile test/constraints_combined_1/Makefile test/constraints_combined_2/Makefile test/constraints_leadtime_1/Makefile test/constraints_leadtime_2/Makefile test/constraints_material_1/Makefile test/constraints_material_2/Makefile test/constraints_material_3/Makefile test/constraints_material_4/Makefile test/datetime/Makefile test/flow_alternate_1/Makefile test/flow_alternate_2/Makefile test/flow_fixed/Makefile test/lpsolver_1/Makefile test/scalability_1/Makefile test/scalability_2/Makefile test/scalability_3/Makefile test/jobshop/Makefile test/xml/Makefile test/xml_remote/Makefile test/constraints_resource_1/Makefile test/constraints_resource_2/Makefile test/constraints_resource_3/Makefile test/constraints_resource_4/Makefile test/constraints_resource_5/Makefile test/constraints_resource_6/Makefile test/criticality/Makefile test/problems/Makefile test/deletion/Makefile test/demand_policy/Makefile test/operation_alternate/Makefile test/operation_available/Makefile test/operation_effective/Makefile test/operation_pre_post/Makefile test/operation_routing/Makefile test/multithreading/Makefile test/name/Makefile test/python_1/Makefile test/python_2/Makefile test/python_3/Makefile test/sample_module/Makefile test/callback/Makefile test/pegging/Makefile test/safety_stock/Makefile test/buffer_procure_1/Makefile test/flow_effective/Makefile test/load_alternate/Makefile test/load_effective/Makefile test/setup_1/Makefile test/setup_2/Makefile test/setup_3/Makefile test/skills/Makefile test/wip/Makefile ])
AC_OUTPUT

# Byebye message
Expand Down
7 changes: 4 additions & 3 deletions contrib/django/freppledb/execute/export_database_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ def exportOperationplans(cursor):
for i in frepple.operations():
cursor.executemany(
"insert into out_operationplan \
(id,operation,quantity,startdate,enddate,locked,unavailable,owner) \
values (%s,%s,%s,%s,%s,%s,%s,%s)",
(id,operation,quantity,startdate,enddate,criticality,locked,unavailable,owner) \
values (%s,%s,%s,%s,%s,%s,%s,%s,%s)",
[(
j.id, i.name.replace("'","''"),
round(j.quantity,settings.DECIMAL_PLACES), str(j.start), str(j.end),
j.locked, j.unavailable, j.owner and j.owner.id or None
round(j.criticality,settings.DECIMAL_PLACES), j.locked, j.unavailable,
j.owner and j.owner.id or None
) for j in i.operationplans ])
cnt += 1
if cnt % 300 == 0: transaction.commit(using=database)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,14 @@ def exportConstraints(process):
def exportOperationplans(process):
print("Exporting operationplans...")
starttime = time()
process.stdin.write('COPY out_operationplan (id,operation,quantity,startdate,enddate,locked,unavailable,owner) FROM STDIN;\n')
process.stdin.write('COPY out_operationplan (id,operation,quantity,startdate,enddate,criticality,locked,unavailable,owner) FROM STDIN;\n')
for i in frepple.operations():
for j in i.operationplans:
process.stdin.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" % (
process.stdin.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" % (
j.id, i.name[0:settings.NAMESIZE].encode(encoding),
round(j.quantity,settings.DECIMAL_PLACES), str(j.start), str(j.end),
j.locked, j.unavailable, j.owner and j.owner.id or "\\N"
round(j.criticality,settings.DECIMAL_PLACES), j.locked, j.unavailable,
j.owner and j.owner.id or "\\N"
))
process.stdin.write('\\.\n')
print('Exported operationplans in %.2f seconds' % (time() - starttime))
Expand Down
1 change: 1 addition & 0 deletions contrib/django/freppledb/output/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class OperationPlan(models.Model):
unavailable = models.DecimalField(_('unavailable'), max_digits=settings.MAX_DIGITS, decimal_places=settings.DECIMAL_PLACES, default='0.00')
startdate = models.DateTimeField(_('startdate'), db_index=True)
enddate = models.DateTimeField(_('enddate'), db_index=True)
criticality = models.DecimalField(_('criticality'), max_digits=settings.MAX_DIGITS, decimal_places=settings.DECIMAL_PLACES, null=True)
locked = models.BooleanField(_('locked'), default=True)
owner = models.IntegerField(_('owner'), null=True, blank=True, db_index=True)

Expand Down
1 change: 1 addition & 0 deletions contrib/django/freppledb/output/views/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def extra_context(reportclass, request, *args, **kwargs):
GridFieldNumber('quantity', title=_('quantity'), editable=False),
GridFieldDateTime('flowdate', title=_('date'), editable=False),
GridFieldNumber('onhand', title=_('onhand'), editable=False),
GridFieldNumber('operationplan__criticality', title=_('criticality'), editable=False),
GridFieldBool('operationplan__locked', title=_('locked'), editable=False),
GridFieldInteger('operationplan', title=_('operationplan'), editable=False),
)
Expand Down
1 change: 1 addition & 0 deletions contrib/django/freppledb/output/views/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def extra_context(reportclass, request, *args, **kwargs):
GridFieldNumber('quantity', title=_('quantity'), editable=False),
GridFieldDateTime('startdate', title=_('start date'), editable=False),
GridFieldDateTime('enddate', title=_('end date'), editable=False),
GridFieldNumber('criticality', title=_('criticality'), editable=False),
GridFieldBool('locked', title=_('locked'), editable=False),
GridFieldNumber('unavailable', title=_('unavailable'), editable=False),
GridFieldInteger('owner', title=_('owner'), editable=False),
Expand Down
3 changes: 2 additions & 1 deletion contrib/django/freppledb/output/views/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,10 @@ def extra_context(reportclass, request, *args, **kwargs):
GridFieldDateTime('enddate', title=_('end date'), editable=False),
GridFieldNumber('operationplan__quantity', title=_('operationplan quantity'), editable=False),
GridFieldNumber('quantity', title=_('load quantity'), editable=False),
GridFieldText('setup', title=_('setup'), editable=False),
GridFieldNumber('operationplan__criticality', title=_('criticality'), editable=False),
GridFieldBool('operationplan__locked', title=_('locked'), editable=False),
GridFieldNumber('operationplan__unavailable', title=_('unavailable'), editable=False),
GridFieldInteger('operationplan', title=_('operationplan'), editable=False),
GridFieldText('setup', title=_('setup'), editable=False),
)

17 changes: 9 additions & 8 deletions contrib/django/freppledb/output/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,15 @@ def render(cls, request=None):
except: db = DEFAULT_DB_ALIAS
result = [
'<table style="width:100%">',
'<tr><th class="alignleft">%s</th><th>%s</th><th>%s</th><th>%s</th></tr>' % (
'<tr><th class="alignleft">%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>' % (
capfirst(force_unicode(_("operation"))), capfirst(force_unicode(_("startdate"))),
capfirst(force_unicode(_("enddate"))), capfirst(force_unicode(_("quantity")))
capfirst(force_unicode(_("enddate"))), capfirst(force_unicode(_("quantity"))),
capfirst(force_unicode(_("criticality")))
)
]
for opplan in OperationPlan.objects.using(db).filter(operation__startswith='Purchase ', locked=False).order_by('startdate')[:limit]:
result.append('<tr><td>%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td></tr>' % (
opplan.operation, opplan.startdate.date(), opplan.enddate.date(), int(opplan.quantity)
result.append('<tr><td>%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td></tr>' % (
opplan.operation, opplan.startdate.date(), opplan.enddate.date(), int(opplan.quantity), int(opplan.criticality)
))
result.append('</table>')
return HttpResponse('\n'.join(result))
Expand Down Expand Up @@ -187,15 +188,15 @@ def render(cls, request=None):
except: db = DEFAULT_DB_ALIAS
result = [
'<table style="width:100%">',
'<tr><th class="alignleft">%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>' % (
'<tr><th class="alignleft">%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>' % (
capfirst(force_unicode(_("resource"))), capfirst(force_unicode(_("operation"))),
capfirst(force_unicode(_("startdate"))), capfirst(force_unicode(_("enddate"))),
capfirst(force_unicode(_("quantity")))
capfirst(force_unicode(_("quantity"))), capfirst(force_unicode(_("criticality")))
)
]
for ldplan in LoadPlan.objects.using(db).select_related().order_by('startdate')[:limit]:
result.append('<tr><td class="underline"><a href="%s/loadplan/?theresource=%s&sidx=startdate&sord=asc">%s</a></td><td>%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td></tr>' % (
request.prefix, urlquote(ldplan.theresource), ldplan.theresource, ldplan.operationplan.operation, ldplan.startdate, ldplan.enddate, int(ldplan.operationplan.quantity)
result.append('<tr><td class="underline"><a href="%s/loadplan/?theresource=%s&sidx=startdate&sord=asc">%s</a></td><td>%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td><td class="aligncenter">%s</td></tr>' % (
request.prefix, urlquote(ldplan.theresource), ldplan.theresource, ldplan.operationplan.operation, ldplan.startdate, ldplan.enddate, int(ldplan.operationplan.quantity), int(ldplan.operationplan.criticality)
))
result.append('</table>')
return HttpResponse('\n'.join(result))
Expand Down
15 changes: 15 additions & 0 deletions include/frepple/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -1663,6 +1663,21 @@ class OperationPlan
/** Returns how many loadplans are created on an operationplan. */
int sizeLoadPlans() const;

/** Returns the criticality index of the operationplan, which reflects
* its urgency.<br>
* If the operationplan is on the critical path of one or more orders
* the criticality is high. If the operationplan is only used to satisfy
* safety stock requirements it will have a low criticality.<br>
* Computing the criticality is complex, CPU-expensive and the result
* will change when the plan changes. Caching the value may be in
* order.<br>
* Criticality is currently implemented as the slack in the downstream
* path. If the criticality is 2, it means the operationplan can be
* delayed by 2 days without impacting the delivery of any demand.
* TODO should criticality also include priority of demand and critical quantity?
*/
DECLARE_EXPORT double getCriticality() const;

/** @brief This class models an STL-like iterator that allows us to iterate over
* the operationplans in a simple and safe way.
*
Expand Down
4 changes: 1 addition & 3 deletions include/frepple/tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,14 @@ class Tags
static DECLARE_EXPORT const Keyword tag_carrying_cost;
static DECLARE_EXPORT const Keyword tag_category;
static DECLARE_EXPORT const Keyword tag_cluster;
static DECLARE_EXPORT const Keyword tag_cmdline;
static DECLARE_EXPORT const Keyword tag_command;
static DECLARE_EXPORT const Keyword tag_commands;
static DECLARE_EXPORT const Keyword tag_constraints;
static DECLARE_EXPORT const Keyword tag_consume_material;
static DECLARE_EXPORT const Keyword tag_consume_capacity;
static DECLARE_EXPORT const Keyword tag_consuming;
static DECLARE_EXPORT const Keyword tag_consuming_date;
static DECLARE_EXPORT const Keyword tag_content;
static DECLARE_EXPORT const Keyword tag_cost;
static DECLARE_EXPORT const Keyword tag_criticality;
static DECLARE_EXPORT const Keyword tag_current;
static DECLARE_EXPORT const Keyword tag_customer;
static DECLARE_EXPORT const Keyword tag_customers;
Expand Down
36 changes: 36 additions & 0 deletions src/model/operationplan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ DECLARE_EXPORT void OperationPlan::writeElement(XMLOutput *o, const Keyword& tag
o->writeElement(Tags::tag_start, dates.getStart());
o->writeElement(Tags::tag_end, dates.getEnd());
o->writeElement(Tags::tag_quantity, quantity);
o->writeElement(Tags::tag_criticality, getCriticality());
if (getLocked()) o->writeElement (Tags::tag_locked, true);
if (!getConsumeMaterial()) o->writeElement(Tags::tag_consume_material, false);
if (!getProduceMaterial()) o->writeElement(Tags::tag_produce_material, false);
Expand Down Expand Up @@ -1015,6 +1016,8 @@ DECLARE_EXPORT PyObject* OperationPlan::getattro(const Attribute& attr)
return PythonObject(getHidden());
if (attr.isA(Tags::tag_unavailable))
return PythonObject(getUnavailable());
if (attr.isA(Tags::tag_criticality))
return PythonObject(getCriticality());
if (attr.isA(Tags::tag_consume_material))
return PythonObject(getConsumeMaterial());
if (attr.isA(Tags::tag_consume_capacity))
Expand Down Expand Up @@ -1113,4 +1116,37 @@ DECLARE_EXPORT int OperationPlan::setattro(const Attribute& attr, const PythonOb
return 0;
}


DECLARE_EXPORT double OperationPlan::getCriticality() const
{
if (getTopOwner()->getDemand())
{
// Demand delivery operationplan
long early = getTopOwner()->getDemand()->getDue() - getDates().getEnd();
return ((early<=0L) ? 0.0 : early) / 86400.0; // Convert to days
}

// Upstream operationplan
TimePeriod minslack = 86313600L; // 999 days in seconds
vector<TimePeriod> slack(HasLevel::getNumberOfLevels() + 2);
for (PeggingIterator p(this); p; p++)
{
Date d = p.getProducingDate();
if (!d) d = Plan::instance().getCurrent();
slack[-p.getLevel()] = p.getConsumingDate() - d;
OperationPlan* m = p.getConsumingOperationplan();
if (m && m->getTopOwner()->getDemand())
{
// Reached a demand. Get the total slack now.
TimePeriod myslack = m->getTopOwner()->getDemand()->getDue() - m->getDates().getEnd();
if (myslack < 0L) myslack = 0L;
for (int i=-p.getLevel(); i>=0; i--)
myslack += slack[i];
if (myslack < minslack)
minslack = myslack;
}
}
return minslack / 86400.0; // Convert to days
}

} // end namespace
4 changes: 1 addition & 3 deletions src/tags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,14 @@ DECLARE_EXPORT const Keyword Tags::tag_calendars("calendars");
DECLARE_EXPORT const Keyword Tags::tag_carrying_cost("carrying_cost");
DECLARE_EXPORT const Keyword Tags::tag_category("category");
DECLARE_EXPORT const Keyword Tags::tag_cluster("cluster");
DECLARE_EXPORT const Keyword Tags::tag_cmdline("cmdline");
DECLARE_EXPORT const Keyword Tags::tag_command("command");
DECLARE_EXPORT const Keyword Tags::tag_commands("commands");
DECLARE_EXPORT const Keyword Tags::tag_constraints("constraints");
DECLARE_EXPORT const Keyword Tags::tag_consume_material("consume_material");
DECLARE_EXPORT const Keyword Tags::tag_consume_capacity("consume_capacity");
DECLARE_EXPORT const Keyword Tags::tag_consuming("consuming");
DECLARE_EXPORT const Keyword Tags::tag_consuming_date("consuming_date");
DECLARE_EXPORT const Keyword Tags::tag_content("content");
DECLARE_EXPORT const Keyword Tags::tag_cost("cost");
DECLARE_EXPORT const Keyword Tags::tag_criticality("criticality");
DECLARE_EXPORT const Keyword Tags::tag_current("current");
DECLARE_EXPORT const Keyword Tags::tag_customer("customer");
DECLARE_EXPORT const Keyword Tags::tag_customers("customers");
Expand Down
2 changes: 1 addition & 1 deletion test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Process this file with automake to produce Makefile.in
#

SUBDIRS = cluster custom_fields scalability_1 scalability_2 scalability_3 calendar datetime flow_alternate_1 flow_alternate_2 flow_fixed constraints_combined_1 constraints_combined_2 constraints_leadtime_1 constraints_leadtime_2 lpsolver_1 constraints_material_1 constraints_material_2 constraints_material_3 constraints_material_4 jobshop xml constraints_resource_1 constraints_resource_2 constraints_resource_3 constraints_resource_4 constraints_resource_5 constraints_resource_6 problems deletion operation_alternate operation_available operation_effective operation_pre_post operation_routing name multithreading sample_module callback pegging xml_remote python_1 python_2 python_3 demand_policy safety_stock buffer_procure_1 flow_effective load_alternate load_effective setup_1 setup_2 setup_3 skills wip
SUBDIRS = cluster custom_fields scalability_1 scalability_2 scalability_3 calendar datetime flow_alternate_1 flow_alternate_2 flow_fixed constraints_combined_1 constraints_combined_2 constraints_leadtime_1 constraints_leadtime_2 lpsolver_1 constraints_material_1 constraints_material_2 constraints_material_3 constraints_material_4 jobshop xml constraints_resource_1 constraints_resource_2 constraints_resource_3 constraints_resource_4 constraints_resource_5 constraints_resource_6 criticality problems deletion operation_alternate operation_available operation_effective operation_pre_post operation_routing name multithreading sample_module callback pegging xml_remote python_1 python_2 python_3 demand_policy safety_stock buffer_procure_1 flow_effective load_alternate load_effective setup_1 setup_2 setup_3 skills wip

EXTRA_DIST = runtest.py

Expand Down
7 changes: 7 additions & 0 deletions test/criticality/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Process this file with automake to produce Makefile.in
#

EXTRA_DIST = *.expect criticality.xml

CLEANFILES = output.*
Loading

0 comments on commit 0a398cc

Please sign in to comment.