Skip to content

Commit

Permalink
Handling the observed geometry through a resolution constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
fvilla committed Dec 3, 2024
1 parent 2d9baa6 commit c2b9e1f
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@
import org.integratedmodelling.klab.api.collections.Identifier;
import org.integratedmodelling.klab.api.data.KnowledgeGraph;
import org.integratedmodelling.klab.api.data.Metadata;
import org.integratedmodelling.klab.api.data.Mutable;
import org.integratedmodelling.klab.api.geometry.Geometry;
import org.integratedmodelling.klab.api.knowledge.Observable;
import org.integratedmodelling.klab.api.knowledge.Urn;
import org.integratedmodelling.klab.api.knowledge.observation.Observation;
import org.integratedmodelling.klab.api.knowledge.observation.impl.ObservationImpl;
import org.integratedmodelling.klab.api.knowledge.observation.scale.time.TimeInstant;
import org.integratedmodelling.klab.api.lang.kim.KimConcept;
import org.integratedmodelling.klab.api.lang.kim.KimModel;
import org.integratedmodelling.klab.api.lang.kim.KimObservable;
import org.integratedmodelling.klab.api.lang.kim.KimSymbolDefinition;
import org.integratedmodelling.klab.api.provenance.Provenance;
import org.integratedmodelling.klab.api.scope.ContextScope;
import org.integratedmodelling.klab.api.scope.Scope;
import org.integratedmodelling.klab.api.services.Reasoner;
import org.integratedmodelling.klab.api.services.resolver.ResolutionConstraint;
import org.integratedmodelling.klab.api.services.runtime.Dataflow;

import java.util.Map;
Expand Down Expand Up @@ -93,22 +97,31 @@ enum Relationship {
* Accepts all the needed elements for the observation, including geometry, observable and the like. If
* two geometries are passed, the second is the observer's (FIXME that should be deprecated). In dependent
* observations, the geometry may be omitted and the geometry of the owning substantial will be used.
* <p>
* If the observation is an observer definition, the geometry ends up as a constraint in the scope. If an
* observer's own geometry is unspecified, a default scalar one is attributed.
*
* @param scope a scope. If a context scope and we use an observer definition, the scope's resolution constraints
* will include an observer geometry after the call.
* @param resolvables
* @return
*/
static ObservationImpl createObservation(Scope scope, Object... resolvables) {
static ObservationImpl createObservation(@Mutable Scope scope, Object... resolvables) {

final Set<String> knownKeys = Set.of("observation", "semantics", "space", "time");

String name = null;
Geometry geometry = null;
Geometry observerGeometry = null;
Observable observable = null;
String resourceUrn = null;
String modelUrn = null;
String defaultValue = null;
Metadata metadata = Metadata.create();
boolean isObserver = false;


Geometry ogeom = null;
if (resolvables != null) {
for (Object o : resolvables) {
if (o instanceof Observable obs) {
Expand All @@ -129,6 +142,8 @@ static ObservationImpl createObservation(Scope scope, Object... resolvables) {
if (("observation".equals(symbol.getDefineClass()) || "observer".equals(symbol.getDefineClass())) && symbol.getValue() instanceof Map<?
, ?> definition) {

isObserver = "observer".equals(symbol.getDefineClass());

name = symbol.getName();
if (definition.containsKey("semantics")) {
observable = scope.getService(Reasoner.class).resolveObservable(definition.get(
Expand All @@ -139,36 +154,18 @@ static ObservationImpl createObservation(Scope scope, Object... resolvables) {
}
}
if (definition.containsKey("space") || definition.containsKey("time")) {
var geometryBuilder = Geometry.builder();
if (definition.containsKey("space")) {
var spaceBuilder = geometryBuilder.space();
if (definition.get("space") instanceof Map<?, ?> spaceDefinition) {
if (spaceDefinition.containsKey("shape")) {
spaceBuilder.shape(spaceDefinition.get("shape").toString());
}
if (spaceDefinition.containsKey("grid")) {
spaceBuilder.resolution(spaceDefinition.get("grid").toString());
}
// TODO add bounding box etc
}
geometryBuilder = spaceBuilder.build();
}
if (definition.containsKey("time")) {
var timeBuilder = geometryBuilder.time();
if (definition.get("time") instanceof Map<?, ?> timeDefinition) {
if (timeDefinition.containsKey("year")) {
var year = timeDefinition.get("year");
if (year instanceof Number number) {
timeBuilder.year(number.intValue());
} else if (year instanceof Identifier identifier && "default".equals(
identifier.getValue())) {
timeBuilder.year(TimeInstant.create().getYear());
}
}
}
geometryBuilder = timeBuilder.build();
}
geometry = geometryBuilder.build();
geometry = defineGeometry(definition);
}

if (definition.containsKey("geometry") && definition.get("geometry") instanceof Map<?, ?>) {
ogeom = defineGeometry((Map<?, ?>) definition.get("geometry"));
}

if (isObserver) {
observerGeometry = geometry;
geometry = ogeom == null ? Geometry.builder().build() : ogeom;
} else if (geometry == null && ogeom != null) {
geometry = ogeom;
}

for (var key : definition.keySet()) {
Expand All @@ -178,19 +175,25 @@ static ObservationImpl createObservation(Scope scope, Object... resolvables) {
}
}
} else if (o instanceof KimModel model) {
// send the model URN and extract the observable
// send the model URN and extract the observable. The modelUrn should become a
// constraint within the requesting scope upstream.
observable =
scope.getService(Reasoner.class).declareObservable(model.getObservables().get(0));
scope.getService(Reasoner.class).declareObservable(model.getObservables().getFirst());
modelUrn = model.getUrn();
} else if (o instanceof Map<?, ?> map) {
// metadata
metadata.putAll((Map<? extends String, ?>) map);
} else if (o instanceof KimConcept concept) {
observable = scope.getService(Reasoner.class).resolveObservable(concept.getUrn());
} else if (o instanceof KimObservable obs) {
observable = scope.getService(Reasoner.class).resolveObservable(obs.getUrn());
}
}
}

/*
least requisite is having an observable
least requisite is having an observable. A quality observation doesn't need to specify
a geometry.
*/
if (observable != null) {
ObservationImpl ret = new ObservationImpl();
Expand All @@ -199,11 +202,50 @@ static ObservationImpl createObservation(Scope scope, Object... resolvables) {
ret.setObservable(observable);
ret.setValue(defaultValue);
ret.setName(name);

if (observerGeometry != null && scope instanceof ContextScope contextScope) {
contextScope.getResolutionConstraints().add(ResolutionConstraint.of(ResolutionConstraint.Type.ObserverGeometry, observerGeometry));
}


return ret;
}

return null;
}

static Geometry defineGeometry(Map<?, ?> definition) {
var geometryBuilder = Geometry.builder();
if (definition.containsKey("space")) {
var spaceBuilder = geometryBuilder.space();
if (definition.get("space") instanceof Map<?, ?> spaceDefinition) {
if (spaceDefinition.containsKey("shape")) {
spaceBuilder.shape(spaceDefinition.get("shape").toString());
}
if (spaceDefinition.containsKey("grid")) {
spaceBuilder.resolution(spaceDefinition.get("grid").toString());
}
// TODO add bounding box etc
}
geometryBuilder = spaceBuilder.build();
}
if (definition.containsKey("time")) {
var timeBuilder = geometryBuilder.time();
if (definition.get("time") instanceof Map<?, ?> timeDefinition) {
if (timeDefinition.containsKey("year")) {
var year = timeDefinition.get("year");
if (year instanceof Number number) {
timeBuilder.year(number.intValue());
} else if (year instanceof Identifier identifier && "default".equals(
identifier.getValue())) {
timeBuilder.year(TimeInstant.create().getYear());
}
}
}
geometryBuilder = timeBuilder.build();
}
return geometryBuilder.build();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,11 @@ public GeometryImpl build() {

GeometryImpl ret = new GeometryImpl();

if (space == null && time == null) {
ret.setScalar(true);
ret.setEmpty(false);
}

if (space != null || time != null) {
ret.setScalar(false);
ret.setEmpty(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.integratedmodelling.klab.api.utils.Utils;

import java.util.*;
import java.util.function.BiFunction;

public class GeometryImpl implements Geometry {

Expand Down Expand Up @@ -873,7 +872,10 @@ public GeometryImpl withBoundingBox(double minX, double maxX, double minY, doubl
if (space == null) {
throw new KlabIllegalStateException("cannot set spatial parameters on a geometry without space");
}
space.getParameters().put(PARAMETER_SPACE_BOUNDINGBOX, GeometryImpl.encodeVal(new double[]{minX, maxX, minY, maxY}));
space.getParameters().put(PARAMETER_SPACE_BOUNDINGBOX, GeometryImpl.encodeVal(new double[]{minX,
maxX,
minY,
maxY}));
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,6 @@ static String getScopeId(ContextScope scope) {
return ret.toString();
}


static Geometry getResolutionGeometry(ContextScope scope) {

var resolutionGeometry = scope.getConstraint(ResolutionConstraint.Type.Geometry, Geometry.class);
Expand All @@ -437,7 +436,7 @@ default Geometry getObservationGeometry(Observation observation) {
}
// override if collective and substantial
if (observation.getObservable().isCollective() && getObserver() != null && getObserver().getGeometry() != null) {
geometry = getObserver().getGeometry();
geometry = getObservedGeometry();
}
}

Expand All @@ -448,6 +447,21 @@ default Geometry getObservationGeometry(Observation observation) {
return geometry;
}

/**
* The geometry currently observed by the current observer, which may be null if the observer is
* null, and may NOT otherwise. This is set when an observer is defined, but is independent
* of the observer's own geometry, and may change through calls independent of the observer as long
* as an observer is there.
*
* Observer geometry is set by adding a {@link ResolutionConstraint} to the scope.
*
* @return
*/
default Geometry getObservedGeometry() {
var ret = getConstraint(ResolutionConstraint.Type.ObserverGeometry, Geometry.class);
return ret == null ? Geometry.EMPTY : ret;
}

/**
* Parse a scope token into the corresponding data structure
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.integratedmodelling.klab.api.services.resources.ResourceSet;
import org.integratedmodelling.klab.api.services.runtime.Dataflow;
import org.integratedmodelling.klab.api.services.runtime.objects.SessionInfo;
import org.integratedmodelling.klab.api.utils.Utils;

import java.util.List;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ enum Type {

Scenarios(String.class, false /* debatable */),
Geometry(Geometry.class, false),
ObserverGeometry(Geometry.class, false),
ResolutionNamespace(String.class, false),
ResolutionProject(String.class, false),
UsingModel(String.class, false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.integratedmodelling.klab.api.data.RuntimeAsset;
import org.integratedmodelling.klab.api.data.Storage;
import org.integratedmodelling.klab.api.exceptions.KlabInternalErrorException;
import org.integratedmodelling.klab.api.geometry.Geometry;
import org.integratedmodelling.klab.api.knowledge.Observable;
import org.integratedmodelling.klab.api.knowledge.SemanticType;
import org.integratedmodelling.klab.api.knowledge.observation.Observation;
Expand Down Expand Up @@ -301,5 +302,4 @@ public <T> List<T> getConstraints(ResolutionConstraint.Type type, Class<T> resul
public Storage getStorage(Observation observation) {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,10 @@ public void observe(Object asset, boolean adding) {
}
} else if (statement instanceof KimConceptStatement conceptStatement) {
// TODO check observable vs. context (qualities w/ their context etc.)
resolvables.add(conceptStatement.getNamespace() + ":" + conceptStatement.getUrn());
resolvables.add(conceptStatement);
} else if (statement instanceof KimObservable conceptStatement) {
// TODO check observable vs. context (qualities w/ their context etc.)
resolvables.add(conceptStatement.getUrn());
resolvables.add(conceptStatement);
}
} else if (asset instanceof String || asset instanceof Urn) {
resolvables.add(asset.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.integratedmodelling.klab.api.exceptions.KlabIllegalStateException;
import org.integratedmodelling.klab.api.exceptions.KlabInternalErrorException;
import org.integratedmodelling.klab.api.exceptions.KlabResourceAccessException;
import org.integratedmodelling.klab.api.knowledge.SemanticType;
import org.integratedmodelling.klab.api.knowledge.observation.Observation;
import org.integratedmodelling.klab.api.knowledge.observation.impl.ObservationImpl;
import org.integratedmodelling.klab.api.lang.Contextualizable;
Expand Down Expand Up @@ -287,6 +288,22 @@ public long submit(Observation observation, ContextScope scope) {
"knowledge graph for now");
}

if (observation.getObservable().is(SemanticType.QUALITY) && scope.getContextObservation() == null) {
throw new KlabIllegalStateException("Cannot observe a quality without a context observation");
}

/**
* Only situation when we accept an observation w/o geometry
*/
if (observation.getGeometry() == null &&
observation instanceof ObservationImpl observation1) {
if (observation.getObservable().is(SemanticType.QUALITY) && scope.getContextObservation() != null) {
observation1.setGeometry(scope.getContextObservation().getGeometry());
} else if (observation.getObservable().is(SemanticType.COUNTABLE) && observation.getObservable().isCollective() && scope.getObserver() != null) {
observation1.setGeometry(scope.getObservedGeometry());
}
}

if (scope instanceof ServiceContextScope serviceContextScope) {

var digitalTwin = getDigitalTwin(scope);
Expand Down

0 comments on commit c2b9e1f

Please sign in to comment.