Is there any way to force OpenMDAO to raise an error if a given input is unconnected? I know for many inputs, defaults can be provided such that the input doesn't need to be connected, however is there a way to tell OpenMDAO to automatically raise an error if certain key inputs are unconnected?
This is not built into OpenMDAO, as of V3.17. However, it is possible to do it. The only caveat is that i had to use some non public APIs to make it work (notice the use of the p.model._conn_global_abs_in2out). So those APIs are subject to changer later.
This code should give you the behavior your want. You could augment things with the use of variable tagging if you wanted a solution that didn't require you to give a list of variable names to the validate function. The list_inputs method can accept tags to filter by instead if you prefer that.
import openmdao.api as om
def validate_connections(prob, force_connected):
# make sure its a set and not a generic iterator (i.e. array)
force_connected_set = set(force_connected)
model_inputs = prob.model.list_inputs(out_stream=None, prom_name=True)
#gets the promoted names from the list of inputs
input_set = set([inp[1]['prom_name'] for inp in model_inputs])
# filter the inputs into connected and unconnected sets
connect_dict = p.model._conn_global_abs_in2out
unconnected_inputs = set()
connected_inputs = set()
for abs_name, in_data in model_inputs:
if abs_name in connect_dict and (not 'auto_ivc' in connect_dict[abs_name]):
connected_inputs.add(in_data['prom_name'])
else:
unconnected_inputs.add(in_data['prom_name'])
# now we need to check if there are any unconnected inputs
# in the model that aren't in the spec
illegal_unconnected = force_connected_set.intersection(unconnected_inputs)
if len(illegal_unconnected) > 0:
raise ValueError(f'unconnected inputs {illegal_unconnected} are are not allowed')
p = om.Problem()
###############################################################################################
# comment and uncomment these three lines to change the error you get from the validate method
###############################################################################################
# p.model.add_subsystem('c0', om.ExecComp('x=3*a'), promotes_outputs=['x'])
# p.model.add_subsystem('c1', om.ExecComp('b=a+17'))
# p.model.connect('c1.b', 'c2.b')
p.model.add_subsystem('c2', om.ExecComp('y=2*x+b'), promotes_inputs=['x'])
p.model.add_subsystem('c3', om.ExecComp('z=x**2+y'))
p.setup()
p.final_setup()
If this is a feature you think should be added to OpenMDAO proper, then feel free to submit a POEM proposing how a formal feature and its API might look.
Related
I'm switching over to using the Auto-IVC component as opposed to the IndepVar component. I'd like to be able to get a list of the promoted output names of the Auto-IVC component, so I can then use them to go and pull the appropriate value out of a configuration file and set the values that way. This will get rid of some boilerplate.
p.model._auto_ivc.list_outputs()
returns an empty list. It seems that p.model__dict__ has this information encoded in it, but I don't know exactly what is going on there so I am wondering if there is an easier way to do it.
To avoid confusion from future readers, I assume you meant that you wanted the promoted input names for the variables connected to the auto_ivc outputs.
We don't have a built-in function to do this, but you could do it with a bit of code like this:
seen = set()
for n in p.model._inputs:
src = p.model.get_source(n)
if src.startswith('_auto_ivc.') and src not in seen:
print(src, p.model._var_allprocs_abs2prom['input'][n])
seen.add(src)
assuming 'p' is the name of your Problem instance.
The code above just prints each auto_ivc output name followed by the promoted input it's connected to.
Here's an example of the output when run on one of our simple test cases:
_auto_ivc.v0 par.x
How do i do a call order verification of a fake with argument validation in sinon.js?
It is the same fake which is called multiple times with different arguments...
something like below
let someFake = sinon.fake();
someFake(1);
someFake(2);
someFake(3);
sinon.assert.callOrder(someFake.calledWith(1), someFake.calledWith(2),
someFake.calledWith(3));
You are essentially using the wrong API for the job, so it's no wonder you are not getting the expected results :) If you look at the docs for callOrder you will see that the signature is using spy1, spy2, .., which indicates that it is meant to be used by more than one spy. A fake is implementation wise also a Spy, so all bits of the Spy API also applies to a Fake. From the docs:
The created fake Function, with or without behavior has the same API as a sinon.spy
A sidenode, that is a bit confusing, is that the docs often use the term "fake" to apply to any fake object or function, not functions created using the sinon.fake API specifically, although, that should not be an issue in this particular case.
With regards to your original question, the Spy API has got you covered since Sinon 1.0 here. You use getCall(n) to return the nth call. There are lots of interactive examples in the docs for this, but essentially you just do this:
// dumbed down version of https://runkit.com/fatso83/stackoverflow-66192966
const fake = sinon.fake();
fake(1);
fake(20);
fake(300);
sinon.assert.calledThrice(fake);
assertEquals(1, fake.getCall(0).args[0])
assertEquals(20, fake.secondCall.args[0])
assertEquals(300, fake.lastCall.args[0])
function assertEquals(arg1,arg2){
if(arg1 !== arg2) {
throw new Error(`Expected ${arg1} to equal ${arg2}`);
}
console.log(`${arg1} equal ${arg2}: OK`)
}
<script src="https://cdn.jsdelivr.net/npm/sinon#latest/pkg/sinon.js"></script>
I have a unit test for a function that adds data (untransformed) to the database. The data to insert is given to the create function.
Do I use the input data in my asserts or is it better to specify the data that I’m asserting?
For eample:
$personRequest = [
'name'=>'John',
'age'=>21,
];
$id = savePerson($personRequest);
$personFromDb = getPersonById($id);
$this->assertEquals($personRequest['name'], $personFromDb['name']);
$this->assertEquals($personRequest['age'], $personFromDb['age']);
Or
$id = savePerson([
'name'=>'John',
'age'=>21,
]);
$personFromDb = getPersonById($id);
$this->assertEquals('John', $personFromDb['name']);
$this->assertEquals(21, $personFromDb['age']);
I think 1st option is better. Your input data may change in future and if you go by 2nd option, you will have to change assertion data everytime.
2nd option is useful, when your output is going to be same irrespective of your input data.
I got an answer from Adam Wathan by e-mail. (i took his test driven laravel course and noticed he uses the 'specify' option)
I think it's just personal preference, I like to be able to visually
skim and see "ok this specific string appears here in the output and
here in the input", vs. trying to avoid duplication by storing things
in variables." Nothing wrong with either approach in my opinion!
So i can't choose a correct answer.
I'm trying to get the basic Forio Epicenter example from the documentation to work, using Julia and the UI Builder. It looks like this:
#in MyModel.jl
module MyModel
# use the Epicenter Julia package
using Epicenter
# expose methods you want to call
# (e.g. using the Run API or Model Operation API,
# or the Model Operation property of a component in UI Builder)
export doubleIt
# expose variables you want to view or update
# (e.g. using the Run API or Model Variable API,
# or the Model Variable property of a component in UI Builder)
export parameters, result
# make exposed variables global
global parameters, result
# define variables that the end user will change as part of a complex type
type ParametersType
input1
input2
function ParametersType()
item = new()
item
end
end
function doubleIt()
global parameters
global result
result = parameters.input1 * 2
record(:result)
end
# other files in your model
include("Utils.jl")
include("OtherModelCalculations.jl")
end
I can't get any variables to connect. When I make a "Text Input" component and change the "Model Variable" property to match the Julia variable (exposed via "export" and "global"), loading the page gives me
400 Bad Request
Full JSON response:
{"status":400,
"errors":{"status":400,
"internal":{
"procedure":"push!(LOAD_PATH, string(ENV[\"HOME\"],
\"\/model\/\")); import Epicenter; using Juliet; using EpicenterSystem; EpicenterSystem.setup_idle_timeout
(1800); require(\"MyModel.jl\"); using MyModel","sessionId":"c5b8189b-4f4d-4377-87a9-ca8d154863c0","trace":""}
},
"message":"Bad Request."
}
I've tried every combination of the "Model Variable" I can think of, including "input1", "parameters", and "parameters.input1"; always comes back with a 400 error.
Forio Support here.
Two problems
a) since "input1" is a field in of variable parameters (type ParameterTypes), it must be referred to in the interface as "parameters.input1"
b) More importantly, the model attempts to include Utils.jl and OtherModelCalculations.jl, neither of which exist. Thus the model never loads.
I am searching for a way to get user input inside a loop while executing in batch mode.
readLines() and scan() work well for me in interactive mode only, in batch mode they start to read in lines of code as user input, unless all the code is surrounded by {}, which is inconvenient. I need a simple solution to get just 1 integer value in a way that I can just type in value and press ENTER, so
the input field (if the solution involves GUI) must automatically get focus and
ENTER must trigger end of input/submission.
I can't find a way to do it that will satisfy both conditions, e.g. ginput() from gWidgets activates the input field, but ENTER doesn't trigger form submission.
Here is how I solved my own problem:
require(gWidgets)
options(guiToolkit="RGtk2")
INPUT <- function(message) {
CHOICE <- NA
w <- gbasicdialog(title=message, handler = function(h,...) CHOICE <<- svalue(input))
input <- gedit("", initial.msg="", cont=w, width=10)
addHandlerChanged(input, handler=function (h, ...) {
CHOICE <<- svalue(input)
dispose(w)
})
visible(w, set=TRUE)
return(CHOICE)
}
repeat{
x=INPUT("Input an integer")
if(!is.na(as.integer(x))) break
}
print(x)
Update:
I can't test this right now, but take a look at ?menu and have it pop up a gui window.
I'm not certain if that will work, but it is different in that it takes a mouse-click response.
original answer:
As per the documentation to ?readline:
This can only be used in an interactive session.
..
In non-interactive use the result is as if the response was RETURN and the value is "".
If you are simply waiting for one piece of information, and you do not know this piece of information before beginning the execution of the script (presumably, there is a decision to be made which is dependent on the results earlier in the script), then one alternative is to simply break your script up into three parts:
everything before the decision point.
an interactive script which prompts for input
everything after the decision point.
And simply chain the three together by having the first end by calling the second in an interactive session. Then have the second end by calling the third.