-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add configurable policies (#254)
* feat: add configurable policies * add person filter * add option to provide link list for LTZ * bugfix for city tax * switch to factor-based penalty for LTZ * fix for link list path * simplify factor penalty * update logging * fix bug for link list * fix imports * fix
- Loading branch information
Showing
31 changed files
with
1,045 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
ile_de_france/src/main/java/org/eqasim/ile_de_france/IDFConfigurator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,12 @@ | ||
package org.eqasim.ile_de_france; | ||
|
||
import org.eqasim.core.simulation.EqasimConfigurator; | ||
import org.eqasim.ile_de_france.policies.PoliciesConfigGroup; | ||
|
||
public class IDFConfigurator extends EqasimConfigurator { | ||
public IDFConfigurator() { | ||
super(); | ||
|
||
registerConfigGroup(new PoliciesConfigGroup(), true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
ile_de_france/src/main/java/org/eqasim/ile_de_france/policies/DefaultPolicy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package org.eqasim.ile_de_france.policies; | ||
|
||
import org.eqasim.ile_de_france.policies.mode_choice.UtilityPenalty; | ||
import org.eqasim.ile_de_france.policies.routing.RoutingPenalty; | ||
|
||
public class DefaultPolicy implements Policy { | ||
private final RoutingPenalty routingPenalty; | ||
private final UtilityPenalty utilityPenalty; | ||
|
||
public DefaultPolicy(RoutingPenalty routingPenalty, UtilityPenalty utilityPenalty) { | ||
this.routingPenalty = routingPenalty; | ||
this.utilityPenalty = utilityPenalty; | ||
} | ||
|
||
@Override | ||
public RoutingPenalty getRoutingPenalty() { | ||
return routingPenalty; | ||
} | ||
|
||
@Override | ||
public UtilityPenalty getUtilityPenalty() { | ||
return utilityPenalty; | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
ile_de_france/src/main/java/org/eqasim/ile_de_france/policies/PoliciesConfigGroup.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package org.eqasim.ile_de_france.policies; | ||
|
||
import org.eqasim.ile_de_france.policies.city_tax.CityTaxConfigGroup; | ||
import org.eqasim.ile_de_france.policies.city_tax.CityTaxPolicyFactory; | ||
import org.eqasim.ile_de_france.policies.limited_traffic_zone.LimitedTrafficZoneConfigGroup; | ||
import org.eqasim.ile_de_france.policies.limited_traffic_zone.LimitedTrafficZonePolicyFactory; | ||
import org.eqasim.ile_de_france.policies.transit_discount.TransitDiscountConfigGroup; | ||
import org.eqasim.ile_de_france.policies.transit_discount.TransitDiscountPolicyFactory; | ||
import org.matsim.core.config.Config; | ||
import org.matsim.core.config.ConfigGroup; | ||
import org.matsim.core.config.ReflectiveConfigGroup; | ||
|
||
public class PoliciesConfigGroup extends ReflectiveConfigGroup { | ||
static public final String CONFIG_NAME = "eqasim:policies"; | ||
|
||
public PoliciesConfigGroup() { | ||
super(CONFIG_NAME); | ||
} | ||
|
||
@Override | ||
public ConfigGroup createParameterSet(String type) { | ||
switch (type) { | ||
case CityTaxPolicyFactory.POLICY_NAME: | ||
return new CityTaxConfigGroup(); | ||
case LimitedTrafficZonePolicyFactory.POLICY_NAME: | ||
return new LimitedTrafficZoneConfigGroup(); | ||
case TransitDiscountPolicyFactory.POLICY_NAME: | ||
return new TransitDiscountConfigGroup(); | ||
default: | ||
throw new IllegalStateException(); | ||
} | ||
} | ||
|
||
static public PoliciesConfigGroup get(Config config) { | ||
return (PoliciesConfigGroup) config.getModules().get(CONFIG_NAME); | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
ile_de_france/src/main/java/org/eqasim/ile_de_france/policies/Policy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.eqasim.ile_de_france.policies; | ||
|
||
import org.eqasim.ile_de_france.policies.mode_choice.UtilityPenalty; | ||
import org.eqasim.ile_de_france.policies.routing.RoutingPenalty; | ||
|
||
public interface Policy { | ||
RoutingPenalty getRoutingPenalty(); | ||
|
||
UtilityPenalty getUtilityPenalty(); | ||
} |
18 changes: 18 additions & 0 deletions
18
ile_de_france/src/main/java/org/eqasim/ile_de_france/policies/PolicyConfigGroup.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.eqasim.ile_de_france.policies; | ||
|
||
import org.matsim.core.config.ReflectiveConfigGroup; | ||
|
||
public abstract class PolicyConfigGroup extends ReflectiveConfigGroup { | ||
protected PolicyConfigGroup(String name) { | ||
super(name); | ||
} | ||
|
||
@Parameter | ||
public String policyName; | ||
|
||
@Parameter | ||
public boolean active = true; | ||
|
||
@Parameter | ||
public String personFilter; | ||
} |
167 changes: 167 additions & 0 deletions
167
ile_de_france/src/main/java/org/eqasim/ile_de_france/policies/PolicyExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package org.eqasim.ile_de_france.policies; | ||
|
||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import org.eqasim.core.components.config.EqasimConfigGroup; | ||
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension; | ||
import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator; | ||
import org.eqasim.ile_de_france.policies.city_tax.CityTaxPolicyExtension; | ||
import org.eqasim.ile_de_france.policies.city_tax.CityTaxPolicyFactory; | ||
import org.eqasim.ile_de_france.policies.limited_traffic_zone.LimitedTrafficZonePolicyExtension; | ||
import org.eqasim.ile_de_france.policies.limited_traffic_zone.LimitedTrafficZonePolicyFactory; | ||
import org.eqasim.ile_de_france.policies.mode_choice.PolicyUtilityEstimator; | ||
import org.eqasim.ile_de_france.policies.mode_choice.SumUtilityPenalty; | ||
import org.eqasim.ile_de_france.policies.mode_choice.UtilityPenalty; | ||
import org.eqasim.ile_de_france.policies.routing.PolicyTravelDisutilityFactory; | ||
import org.eqasim.ile_de_france.policies.routing.RoutingPenalty; | ||
import org.eqasim.ile_de_france.policies.routing.SumRoutingPenalty; | ||
import org.eqasim.ile_de_france.policies.transit_discount.TransitDiscountPolicyExtension; | ||
import org.eqasim.ile_de_france.policies.transit_discount.TransitDiscountPolicyFactory; | ||
import org.matsim.api.core.v01.TransportMode; | ||
import org.matsim.api.core.v01.population.Population; | ||
import org.matsim.core.config.Config; | ||
import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutilityFactory; | ||
|
||
import com.google.common.base.Verify; | ||
import com.google.inject.Key; | ||
import com.google.inject.Provider; | ||
import com.google.inject.Provides; | ||
import com.google.inject.Singleton; | ||
import com.google.inject.multibindings.MapBinder; | ||
import com.google.inject.name.Named; | ||
import com.google.inject.name.Names; | ||
|
||
public class PolicyExtension extends AbstractEqasimExtension { | ||
private final static String ESTIMATOR_PREFIX = "policy:"; | ||
|
||
private String delegateCarEstimator; | ||
private String delegateTransitEstimator; | ||
|
||
public void adaptConfiguration(Config config) { | ||
EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); | ||
|
||
delegateCarEstimator = eqasimConfig.getEstimators().get(TransportMode.car); | ||
delegateTransitEstimator = eqasimConfig.getEstimators().get(TransportMode.pt); | ||
|
||
delegateCarEstimator = delegateCarEstimator.replace(ESTIMATOR_PREFIX, ""); | ||
delegateTransitEstimator = delegateTransitEstimator.replace(ESTIMATOR_PREFIX, ""); | ||
|
||
eqasimConfig.setEstimator(TransportMode.car, ESTIMATOR_PREFIX + delegateCarEstimator); | ||
eqasimConfig.setEstimator(TransportMode.pt, ESTIMATOR_PREFIX + delegateTransitEstimator); | ||
} | ||
|
||
@Override | ||
protected void installEqasimExtension() { | ||
Verify.verifyNotNull(delegateCarEstimator, "Need to run PolicyExtension.adaptConfiguration first"); | ||
Verify.verifyNotNull(delegateTransitEstimator, "Need to run PolicyExtension.adaptConfiguration first"); | ||
|
||
// set up travel disutility for routing | ||
addTravelDisutilityFactoryBinding(TransportMode.car).to(PolicyTravelDisutilityFactory.class); | ||
addTravelDisutilityFactoryBinding("car_passenger").to(OnlyTimeDependentTravelDisutilityFactory.class); | ||
|
||
install(new CityTaxPolicyExtension()); | ||
install(new LimitedTrafficZonePolicyExtension()); | ||
install(new TransitDiscountPolicyExtension()); | ||
|
||
var policyBinder = MapBinder.newMapBinder(binder(), String.class, PolicyFactory.class); | ||
policyBinder.addBinding(CityTaxPolicyFactory.POLICY_NAME).to(CityTaxPolicyFactory.class); | ||
policyBinder.addBinding(LimitedTrafficZonePolicyFactory.POLICY_NAME).to(LimitedTrafficZonePolicyFactory.class); | ||
policyBinder.addBinding(TransitDiscountPolicyFactory.POLICY_NAME).to(TransitDiscountPolicyFactory.class); | ||
|
||
bindUtilityEstimator(ESTIMATOR_PREFIX + delegateCarEstimator) | ||
.to(Key.get(PolicyUtilityEstimator.class, Names.named(TransportMode.car))); | ||
|
||
bindUtilityEstimator(ESTIMATOR_PREFIX + delegateTransitEstimator) | ||
.to(Key.get(PolicyUtilityEstimator.class, Names.named(TransportMode.pt))); | ||
} | ||
|
||
@Provides | ||
@Singleton | ||
Map<String, Policy> providePolicies(Map<String, PolicyFactory> factories, Population population) { | ||
PoliciesConfigGroup policyConfig = PoliciesConfigGroup.get(getConfig()); | ||
Map<String, Policy> policies = new HashMap<>(); | ||
|
||
Set<String> names = new HashSet<>(); | ||
|
||
if (policyConfig != null) { | ||
for (var collection : policyConfig.getParameterSets().values()) { | ||
for (var raw : collection) { | ||
PolicyConfigGroup policy = (PolicyConfigGroup) raw; | ||
|
||
if (policy.active) { | ||
Verify.verify(policy.policyName != null && policy.policyName.length() > 0, | ||
"Policy names must be set"); | ||
|
||
if (!names.add(policy.policyName)) { | ||
throw new IllegalStateException("Duplicate policy name: " + policy.policyName); | ||
} | ||
|
||
PolicyPersonFilter filter = PolicyPersonFilter.create(population, policy); | ||
|
||
policies.put(policy.policyName, | ||
factories.get(policy.getName()).createPolicy(policy.policyName, filter)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return policies; | ||
} | ||
|
||
@Provides | ||
@Singleton | ||
PolicyTravelDisutilityFactory providePolicyTravelDisutilityFactory(RoutingPenalty linkPenalty) { | ||
return new PolicyTravelDisutilityFactory(linkPenalty); | ||
} | ||
|
||
@Provides | ||
@Named(TransportMode.car) | ||
PolicyUtilityEstimator providePolicyUtilityEstimatorForCar(Map<String, Provider<UtilityEstimator>> providers, | ||
UtilityPenalty penalty) { | ||
UtilityEstimator delegate = providers.get(delegateCarEstimator).get(); | ||
return new PolicyUtilityEstimator(delegate, penalty, TransportMode.car); | ||
} | ||
|
||
@Provides | ||
@Named(TransportMode.pt) | ||
PolicyUtilityEstimator providePolicyUtilityEstimatorForTransit(Map<String, Provider<UtilityEstimator>> providers, | ||
UtilityPenalty penalty) { | ||
UtilityEstimator delegate = providers.get(delegateTransitEstimator).get(); | ||
return new PolicyUtilityEstimator(delegate, penalty, TransportMode.pt); | ||
} | ||
|
||
@Provides | ||
UtilityPenalty provideUtilityPenalty(Map<String, Policy> policies) { | ||
List<UtilityPenalty> penalties = new LinkedList<>(); | ||
|
||
for (Policy policy : policies.values()) { | ||
UtilityPenalty penalty = policy.getUtilityPenalty(); | ||
|
||
if (penalty != null) { | ||
penalties.add(penalty); | ||
} | ||
} | ||
|
||
return new SumUtilityPenalty(penalties); | ||
} | ||
|
||
@Provides | ||
RoutingPenalty provideRoutingPenalty(Map<String, Policy> policies) { | ||
List<RoutingPenalty> penalties = new LinkedList<>(); | ||
|
||
for (Policy policy : policies.values()) { | ||
RoutingPenalty penalty = policy.getRoutingPenalty(); | ||
|
||
if (penalty != null) { | ||
penalties.add(penalty); | ||
} | ||
} | ||
|
||
return new SumRoutingPenalty(penalties); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
ile_de_france/src/main/java/org/eqasim/ile_de_france/policies/PolicyFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.eqasim.ile_de_france.policies; | ||
|
||
public interface PolicyFactory { | ||
Policy createPolicy(String name, PolicyPersonFilter personFilter); | ||
} |
36 changes: 36 additions & 0 deletions
36
ile_de_france/src/main/java/org/eqasim/ile_de_france/policies/PolicyPersonFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.eqasim.ile_de_france.policies; | ||
|
||
import org.matsim.api.core.v01.Id; | ||
import org.matsim.api.core.v01.IdSet; | ||
import org.matsim.api.core.v01.population.Person; | ||
import org.matsim.api.core.v01.population.Population; | ||
|
||
public class PolicyPersonFilter { | ||
private final IdSet<Person> selection; | ||
|
||
PolicyPersonFilter(IdSet<Person> selection) { | ||
this.selection = selection; | ||
} | ||
|
||
public boolean applies(Id<Person> personId) { | ||
return selection == null ? true : selection.contains(personId); | ||
} | ||
|
||
static public PolicyPersonFilter create(Population population, PolicyConfigGroup policy) { | ||
if (policy.personFilter != null && policy.personFilter.length() > 0) { | ||
IdSet<Person> selection = new IdSet<>(Person.class); | ||
|
||
for (Person person : population.getPersons().values()) { | ||
Boolean indicator = (Boolean) person.getAttributes().getAttribute(policy.personFilter); | ||
|
||
if (indicator != null && indicator) { | ||
selection.add(person.getId()); | ||
} | ||
} | ||
|
||
return new PolicyPersonFilter(selection); | ||
} else { | ||
return new PolicyPersonFilter(null); | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...e_france/src/main/java/org/eqasim/ile_de_france/policies/city_tax/CityTaxConfigGroup.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.eqasim.ile_de_france.policies.city_tax; | ||
|
||
import org.eqasim.ile_de_france.policies.PolicyConfigGroup; | ||
import org.matsim.core.config.ReflectiveConfigGroup.Parameter; | ||
|
||
public class CityTaxConfigGroup extends PolicyConfigGroup { | ||
public CityTaxConfigGroup() { | ||
super(CityTaxPolicyFactory.POLICY_NAME); | ||
} | ||
|
||
@Parameter | ||
public double tax_EUR = 0.0; | ||
|
||
@Parameter | ||
public String perimetersPath; | ||
} |
20 changes: 20 additions & 0 deletions
20
...ance/src/main/java/org/eqasim/ile_de_france/policies/city_tax/CityTaxPolicyExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.eqasim.ile_de_france.policies.city_tax; | ||
|
||
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension; | ||
import org.eqasim.ile_de_france.mode_choice.parameters.IDFModeParameters; | ||
import org.matsim.api.core.v01.network.Network; | ||
|
||
import com.google.inject.Provides; | ||
import com.google.inject.Singleton; | ||
|
||
public class CityTaxPolicyExtension extends AbstractEqasimExtension { | ||
@Override | ||
protected void installEqasimExtension() { | ||
} | ||
|
||
@Provides | ||
@Singleton | ||
CityTaxPolicyFactory provideCityTaxPolicyFactory(Network network, IDFModeParameters modeParameters) { | ||
return new CityTaxPolicyFactory(getConfig(), network, modeParameters); | ||
} | ||
} |
Oops, something went wrong.